Developing an Alfresco Module

From alfrescowiki

Jump to: navigation, search

Back to Developer Guide

Introduction

An Alfresco module can be developed in any way that suits the developer environment. As long as the resulting AMP file is packaged correctly and the required property and context file are present, the module will install successfully.

On this page we provide you with a recommended way to work, based on the way we develop modules at Alfresco. We assume the use of the Eclipse IDE, but there are dependencies on using it. There are also Maven archetypes available that will greatly expedite the creation of your project. For more information, see Managing Alfresco Lifecyle with Maven.

Structuring your Project

We recommend that module projects be structured as follows:

\
|-- source
   |   
   |-- java
      |
      |-- <module package structure starts here>
   |
   |-- web
      |
      |-- css
      |
      |-- images
      |
      |-- jsp 
      |
      |-- scripts
|
|-- config
   |
   |-- <resource package structure starts here>
|
|-- lib
|
|-- build
   |
   |-- dist
|
|-- project-build.xml

Look at the example project in the SDK for a practical example of this.

  • source/java

This contain the Java source for your Alfresco module. The package structure you choose is up to you, but you will see many of the Alfresco written modules will have the package structure org.alfresco.module.<moduleid>, where moduleid is the id of the module.

This code will need to be built into a jar and placed in the final AMP file.

Have a look at the section about the module properties file for more information about the module id.

  • source/web

This contains any web UI resources split into the various folder outlined above, including JSP pages, images, CSS, scripts, etc.

  • config

This contains configuration files and resources used by the module. There are a couple of files that are required by the module service, these should be placed in the package alfresco.module.<moduleid>. Often the remaining resources are then placed in this package or in sub-packages, but this is not required.

  • build

The class files are built into this directory, with any resulting JARs and the AMP file itself being built to the dist folder.

  • lib

A place to put any 3rd party libraries that this module depends on.

Module Properties

The module properties file is required by the module service to identify the module, and its details, when it is installed.

When the AMP file is built, the module.properties file must be placed at the root of the AMP file, but during development it is recommended that it should reside in the package alfresco.module.<moduleid> as this is the location it will end up in once it is installed into the WAR.

In this location it allows the developer to run unit tests within Eclipse and the embedded repository that is started will behave as if the module is installed. This is because the relevant module.properties file is on the class path in the correct location.

The module.properties file itself contains the module id, version, title and description of the module. The following uses the records management module to give an example of a typical module.properties contents.

# MyModule module properties
module.id=net.sf.myproject.module.MyModule
module.aliases=myModule-123, my-module
module.version=2.0
module.title=My Module
module.description=This is my first Alfresco module

# The following optional properties can be used to prevent the module from being added
# to inappropriate versions of the WAR file.
module.repo.version.min=4.0
module.repo.version.max=4.999

# The following optional property can be used to specific which edition(s) of Alfresco this module is allowed to be installed into.
module.editions=Community,Enterprise

# The following describe dependencies on other modules
# Depends on net.sf.myproject.module.SupportModuleA version 1.0 or later
module.depends.net.sf.myproject.module.SupportModuleA=1.0-*
# Depends on net.sf.myproject.module.SupportModuleB version 1.0 to 2.0
module.depends.net.sf.myproject.module.SupportModuleB=1.0-2.0
# Depends on net.sf.myproject.module.SupportModuleC - any version
module.depends.net.sf.myproject.module.SupportModuleC=*
  • module.id (required)

The module id specified in this file will act as a unique identifier for this module. It is important that the module id is globally unique so that it never clashes with other modules when it is installed. For example: org.alfresco.module.RecordsManagement.

NOTE: Module IDs may contain a-z, A-Z, 0-9, dot, space, minus and underscore. As of 2.1, module renaming is supported using the alias mechanism.

  • module.aliases (optional, since v2.1)

When a module gets renamed, it is necessary to add the original name to the list of aliases. This ensures that the module tool and repository startup can correctly match the new name to one of the old names.

  • module.version (required)

The module version number specifies the current version of the module. This is taken into consideration when installing the module into a WAR. It will determine whether it is a new install or an update to an existing installation of a previous version.

The version number must be made up of numeric values separated by dots. For example '2.1.56' is a valid version number, '2.3.4a' is not.

  • module.title (required)

The title of the module.

  • module.description (required)

The description of the module.

  • module.repo.version.min and module.repo.version.max (optional)

Specifies the minimum and maximum versions of Alfresco that this module can be installed into (defaults to all).

  • module.editions (optional, since v3.4)

Specifies which edition(s) of Alfresco this module can be installed into (defaults to all). Valid values are "Community" and "Enterprise". For multiple values, use a comma-delimited list.

  • module.depends.* (optional, since v2.1)

When a module is installed, it may be a requirement that another module is installed. It may be a requirement that a specific version, set of versions or range of versions is present. The dependecy has been met as long as the installed version of the dependency matches any of the ranges or versions give. Here are some examples:

 # Any version of X must be installed
 module.depends.X=*
 # Need to have versions 1.0, 1.5 or 2.0 of Y installed
 module.depends.Y=1.0, 1.5, 2.0
 # Need to have any version of Z in the range between 1.0 and 2.0 inclusive
 module.depends.Z=1.0-2.0
 # Need to have version any version of Z less than 1.0 installed
 module.depends.Z=*-0.9.9
 # Need to have any version of Z greater than or equal to 1.0 installed
 module.depends.Z=1.0-* 

Module Context

A module is initialised when the Alfresco repository loads the root Spring configuration for that module.

A module's root Spring configuration must be placed in the package alfresco.module.<moduleId> and should be called module-context.xml.

When the module service is initialised, all the module-context.xml configurations found are loaded, thus initialising the installed modules ready for use.

The module-context.xml is a standard Spring configuration file and typically new beans will be defined, custom content models and client configuration specified and data loaded or patched.

In a big module the configuration may be split up into smaller Spring configurations which are included by module-context.xml.

Note: The property executeOnceOnly (default: true) tells the system how many times to execute your module. If you want your module to run every time Alfresco is started, set executeOnceOnly to false.

log4j.properties

As of Alfresco version 2.2, each module can have its own log4j.properties file, which is placed in the same directory as module.properties. The collection of log4j.properties files within all modules installed into the alfresco.war act collectively to augment/override the Alfresco webapp's global WEB-INF/classes/log4j.properties file.

Therefore, you can control the logging levels of classes within your module without having to modify the main log4j.properties file; this also allows the logging configuration of a module to be handled cleanly by the Module Management Tool.

Given that {module.id} denotes the value of module.id set in module.properties, your log4j.properties file should be put in the following position within the source code directory structure of your module:

 config/alfresco/module/{module.id}/log4j.properties

At deployment time, this file will be copied to:

  WEB-INF/classes/alfresco/module/{module.id}/log4j.properties


For example, if module.id=org.alfresco.module.avmCompare, then in the module's source tree you'll have:

  config/alfresco/module/org.alfresco.module.avmCompare/log4j.properties

On the servlet container, your module's log4j.properties file will be deployed to:

  WEB-INF/classes/alfresco/module/org.alfresco.module.avmCompare/log4j.properties


Your module's log4j.properties file might look something like:

   #-----------------------------------------------------------------------
   # webscript module log4j.properties
   #
   #   NOTE
   #   ----
   #      Log4j uses the following logging levels:
   #      debug,info,warn,error,fatal
   #
   #      To set the logging level of {fullClassName} to {loglevel},
   #      add a line to this file of the following form:
   #   
   #               log4j.logger.{fullClassName}={loglevel}
   #
   #      For example, to make 'com.example.MyExample' produce 'debug'
   #      logs, add a line like this:
   #   
   #               log4j.logger.com.example.MyExample=debug
   #
   #
   #   WARNING
   #   -------
   #       Log properties in this log4j.properties file override/augment
   #       those in the webapp's main log4j.properties.
   #    
   #-----------------------------------------------------------------------
  
   log4j.logger.org.alfresco.module.avmCompare.AvmCompare=info


Those who wish to configure log4j properties in Alfresco extensions that aren't packaged as AMP modules can create log4j.properties file of the form: {name}-log4j.properties and place it within some alfresco/extension directory on the server's classpath. These override the module log4.properties files described above. For example:

  WEB-INF/classes/alfresco/extension/SIMPLE_EXAMPLE-log4j.properties

Finally, developers may also wish to maintain a dev-log4j.properties file outside of the webapp. This allows for changing log4j settings without touching the "shipping product", or any of a customer's local settings. Your optional dev-log4j.properties file should be in some alfresco/extension directory within the server's classpath, and outside the webapp itself (so you don't accidentally delete it). The dev-log4j.properties file augments/overrides all others. For example:

   $TOMCAT_HOME/shared/classes/alfresco/extension/dev-log4j.properties

Best Log4j Configuration Practices

  • Local customizations/licences are kept outside of the webapp. For example:
    $TOMCAT_HOME/shared/classes/alfresco/extension/...-log4j.properties
  • Shipping config files should be kept/installed within webapp. For example:
    WEB-INF/classes/alfresco/extension/...-log4j.properties
  • A dev-log4j.properties file should never be used in an ongoing during production, nor packaged as a part of any product.


Advanced log4j.properties Configuration

In the unlikely event that you need to configure the set directories Alfresco uses to find module-specific log4j.properties files, look at WEB-INF/classes/alfresco/core-services-context.xml and find a bean definition fragment that looks something like:

        <property name="overriding_log4j_properties">
            <list>
                <!-- NOTE: value entries are listed from lowest precedence to highest.  -->

                <!--  Installed  AMP modules  -->
                <value>classpath*:alfresco/module/*/log4j.properties</value>

                <!--  Other installed extensions  -->
                <value>classpath*:alfresco/extension/*-log4j.properties</value>

                <!--  private developer overrides -->
                <value>classpath*:alfresco/extension/dev-log4j.properties</value>
            </list>
        </property>

You may add additional path expressions here; for more information on the somewhat curious classpath*: notation used here, see Spring's PathMatchingResourcePatternResolver documentation. Again, you probably won't ever have to make changes to this low-level file.

Disable and Uninstall Contexts

Importing Module Data

As part of your content model and module, you can import some data that the module uses. These can be categories, ftl scripts in the data dictionary, project template hierarchies, ACP files, etc.

For more information see Bootstrap Data.

Adding a Custom Model

Custom content models can be bootstraped into the repository using the following Spring configuration added to module-context.xml:

  <bean id="myModule_dictionaryBootstrap" parent="dictionaryModelBootstrap" depends-on="dictionaryBootstrap">
     <property name="models">
        <list>
            <value>alfresco/module/myModuleId/myCustomModel.xml</value>
        </list>
     </property>
  </bean>

Go to the Data Dictionary Guide for information on how to develop a custon content model

Importing Users/Groups

A module can create users or groups as part of it initialisation. This can be achieved by first creating an import file containing the users/groups you want to import.

This example shows an import file that creates a group:

  <?xml version="1.0" encoding="UTF-8"?>
  <view:view xmlns:view="http://www.alfresco.org/view/repository/1.0"
          xmlns:cm="http://www.alfresco.org/model/content/1.0"
          xmlns:sys="http://www.alfresco.org/model/system/1.0"
          xmlns:usr="http://www.alfresco.org/model/user/1.0"
          xmlns:app="http://www.alfresco.org/model/application/1.0">
     
     <usr:authorityContainer view:childName="usr:GROUP_MyGroup">
       <view:aspects>
         <sys:referenceable></sys:referenceable>
       </view:aspects>
       <view:properties>
         <sys:store-protocol>user</sys:store-protocol>
         <cm:name>GROUP_MyGroup</cm:name>
         <sys:node-uuid>GROUP_MyGroup</sys:node-uuid>
         <usr:authorityName>GROUP_MyGroup</usr:authorityName>
         <sys:store-identifier>alfrescoUserStore</sys:store-identifier>
       </view:properties>
       <view:associations></view:associations>
     </usr:authorityContainer>
  </view:view>

This group can then be imported by adding the following bean definition to the module-context.xml:

  <bean id="myModule_bootstrapGroups" class="org.alfresco.repo.module.ImporterModuleComponent" parent="module.baseComponent">
       <property name="moduleId" value="myModuleId" />
       <property name="name" value="nameGivenToImport" />
       <property name="description" value="descriptionOfThisImport" />
       <property name="sinceVersion" value="1.0" />        
       <property name="appliesFromVersion" value="1.0" />  
       <property name="importer" ref="userBootstrap"/>
       <property name="bootstrapViews">
           <list>
               <props>
                   <prop key="path">/${alfresco_user_store.system_container.childname}/sys:authorities</prop>
                   <prop key="location">alfresco/module/myModuleId/myGroupImport.xml</prop>
               </props>
           </list>
       </property>
   </bean>

Since 3.1, authorities are now in the spaces store, and groups should be associated to zones.

This example shows an import file that creates a group:

  <?xml version="1.0" encoding="UTF-8"?>
  <view:view xmlns:view="http://www.alfresco.org/view/repository/1.0"
     xmlns:cm="http://www.alfresco.org/model/content/1.0"
     xmlns:sys="http://www.alfresco.org/model/system/1.0"
     xmlns:usr="http://www.alfresco.org/model/user/1.0"
     xmlns:app="http://www.alfresco.org/model/application/1.0">
     
     <view:reference view:pathref="${system.authorities_container.childname}">
        <view:associations>
           <sys:children>
              <cm:authorityContainer view:childName="cm:GROUP_MyGroup">
                 <view:aspects>
                    <sys:referenceable />
                 </view:aspects>
                 <view:properties>
                    <sys:node-uuid>GROUP_MyGroup</sys:node-uuid>
                    <cm:name>GROUP_MyGroup</cm:name>
                    <cm:authorityName>GROUP_MyGroup</cm:authorityName>
                 </view:properties>
              </cm:authorityContainer>
           </sys:children>
        </view:associations>
     </view:reference>
     
     <view:reference view:pathref="${system.zones_container.childname}/cm:AUTH.ALF">
        <view:associations>
           <cm:inZone>
              <view:reference
                 view:pathref="${system.authorities_container.childname}/cm:GROUP_MyGroup"
                 view:childName="cm:GROUP_MyGroup" />
           </cm:inZone>
        </view:associations>
     </view:reference>
     
     <view:reference view:pathref="${system.zones_container.childname}/cm:APP.DEFAULT">
        <view:associations>
           <cm:inZone>
              <view:reference
                 view:pathref="${system.authorities_container.childname}/cm:GROUP_MyGroup"
                 view:childName="cm:GROUP_MyGroup" />
           </cm:inZone>
        </view:associations>
     </view:reference>
  </view:view>

This group can then be imported by adding the following bean definition to the module-context.xml:

  <bean id="myModule_bootstrapGroups" class="org.alfresco.repo.module.ImporterModuleComponent" parent="module.baseComponent">
       <property name="moduleId" value="myModuleId" />
       <property name="name" value="nameGivenToImport" />
       <property name="description" value="descriptionOfThisImport" />
       <property name="sinceVersion" value="1.0" />        
       <property name="appliesFromVersion" value="1.0" />   
       <property name="importer" ref="spacesBootstrap"/>
       <property name="bootstrapViews">
           <list>
               <props>
                   <prop key="path">/${alfresco_user_store.system_container.childname}</prop>
                   <prop key="location">alfresco/module/myModuleId/myGroupImport.xml</prop>
               </props>
           </list>
       </property>
   </bean>

Adding a Custom Permission Model

Note: This capability was added in 3.0

A custom permission model can be bootstraped by a module by adding the following bean definition to module-context.xml:

   <bean id="myModule_permissionBootstrap" parent="permissionModelBootstrap">
   	<property name="model" value="alfresco/module/myModuleId/myPermissionDefinitions.xml"/>
   </bean>

Adding Custom Client Configuration

Custom web client configuration can be added by a module by adding a bean defintion to module-context.xml:

   <bean id="myModule_configBootstrap" class="org.alfresco.web.config.WebClientConfigBootstrap" init-method="register">
      <property name="configs">
        <list>
           <value>classpath:alfresco/module/myModuleId/my-web-client-custom.xml</value>
        </list>
      </property>
   </bean>

NOTE: Since 3.3 SP2 the order of config files loaded by the org.alfresco.web.config.WebClientConfigBootstrap and org.springframework.extensions.config.ConfigBootstrap spring beans are loaded in a deterministic order. The beans are now processed in alphabetical order using the bean id as the key.

Adding a custom faces-config.xml

Custom JSF configuration should be put in the module jar-file, in the META-INF directory.

For more information see JSF configuration files in Packaging And Deploying Extensions.

Adding Additional I18N Strings

Building A Module

Personal tools
© 2014 Alfresco Software, Inc. All Rights Reserved. Legal | Privacy | Accessibility