Alfresco Subsystems

From alfrescowiki

Jump to: navigation, search

What is an Alfresco Subsystem?

In Alfresco terminology, a subsystem is a configurable module responsible for a sub-part of Alfresco functionality. Typically, a subsystem wraps an optional functional area such as IMAP binding, or one with several alternative implementations, such as authentication.

A subsystem can be thought of as miniature Alfresco server embedded within the main one, in that

  • it can be started, stopped and configured independently
  • it has its own isolated Spring Application Context and configuration.

This Application Context is a 'child' of the main one, meaning that it can reference all the beans in the main application context. However, the subsystem's beans cannot be seen by the main application context and communication with the subsystem must be through explicitly imported interfaces. This means that subsystems:

  • Can have multiple 'instances' of the same type
    • The same template spring configuration can be initialized with different parameters in different instances. This enables, e.g. the 'chaining' of multiple authentication subsystems through property file edits alone (or JMX edits in the Enterprise edition)
  • Can be brought in and out of existence dynamically
    • Why not try out the JMX-based server configuration capabilities in Alfresco Enterprise edition?
  • Own their own bean namespace
    • There is no problem of bean name uniqueness if you need multiple instances of the same subsystem. Again, this vastly simplifies the problem of building an authentication chain as there is no need to edit any template Spring configuration.
  • Clearly define their interfaces with the rest of the system
    • A subsystem's interfaces must be 'imported' in order to be used anywhere else in the system. This is done by mounting them as dynamic proxies, as explained below.
  • Hide implementation specifics
    • Due to the hiding of all its beans in a private container
  • Can easily be swapped with an alternative implementation
    • E.g. in order to switch over from native Alfresco authentication to NTLM passthru authentication, you just switch to a passthru authentication subsystem and suddenly the correct components are swapped in. There is no longer a need to edit web.xml or other core configuration in order to do this.
  • Clearly separate product from configuration
    • A subsystem binds its configuration settings to overridable properties and can even do this for composite data. There is no longer a need to edit or extend prepackaged Spring configuration in order to configure a subsystem for your own needs

What are the Alfresco Subsystems?

As of Alfresco version 3.2, the subsystems are:

Authentication
Handles all Alfresco's authentication related functions, including:
  • Password-based authentication
  • Single Sign-on (SSO) for WebClient, WebDAV, Web Scripts and Sharepoint
  • CIFS and FTP authentication
  • User registry export (LDAP only)
The subsystem is 'chained' so that multiple instances of different types can be configured and used together. See Alfresco Authentication Subsystems.
Synchronization
Performs regular synchronization of local user and group information with the user registry exporters (usually LDAP directories) in the authentication chain. See The Synchronization Subsystem.
File Servers
Owns the CIFS, FTP and NFS servers. See File Server Subsystem.
Third Party
Owns the OpenOffice, Swftools and ImageMagick content transformers
IMAP
Owns the IMAP service The IMAP Subsystem
WCM Deployment Receiver 
Part of WCM. WCM_Deployment_Engine
sysAdmin
Server Administration Parameters. See The sysAdmin Subsystem.

Prepackaged Subsystem Configuration

Generally, the prepackaged subsystem configuration files described below should not be edited (except when developing your own subsystem or contributing enhancements). Essentially they are parameterized template configuration that forms part of the core product. Alfresco and its contributors reserve the right to maintain these files and replace them with new versions in product patches and upgrades, so they are definitely not the place to put your own configuration. See #Configuring Subsystems for how to configure a subsystem for your own needs.

Basics

Every subsystem has a category and a type.

category 
a broad description of the subsystem's function, e.g. "Authentication"
type 
should be a name for the particular flavour of implementation where multiple alternative implementations exist, e.g. "ldap". Where a subsystem only has one implementation it is permissable to use the default type name of "default".

The Subsystem Directory

A subsystem's prepackaged configuration files should live under

alfresco/subsystems/<category>/<type>

in the classpath. In reality this means they will be under WEB-INF/classes/alfresco/subsystems in alfresco.war. Configuration for subsystems in the Repository project can currently be found here in SVN (although we may eventually decide to move the subsystems into their own project).

Prepackaged Spring Configuration

A subsystem's directory should contain one or more Spring XML bean definition metadata files with names matching the following pattern:

*-context.xml

These files will be loaded by the child application context that belongs to the subsystem instance.

The XML bean definitions may contain placeholders for properties that correspond to configuration parameters of the subsystem. As per standard Spring conventions, these placeholders begin with ${ and end with }. For example, in the following example the value of the ooo.user configuration parameter will be substituted into the bean definition when it is loaded.

   <bean id="userInstallationURI" class="org.alfresco.util.OpenOfficeURI">
      <constructor-arg>
         <value>${ooo.user}</value>
      </constructor-arg>
   </bean>

Note that there is no need to declare a PropertyPlaceholderConfigurer bean. An appropriate one is added into the application context automatically.

Property Defaults

A subsystem should declare default values for all the properties it requires in one or more .properties files in its subsystem directory. So in this example we might have a mysubsystem.properties file containing

ooo.user=${dir.root}/oouser

Note that it is perfectly legal to include placeholders for system-wide properties such as dir.root in the -context.xml and .properties file, as the child application context will recursively expand placeholders for its own properties and all the placeholders recognized by its parent.

Note the properties files in the subsystem directory just declare the existence of configuration parameters and provide default values where these have not been supplied elsewhere. These files should not be edited in order to configure the subsystem. Read #Configuring Subsystems to find out how to configure a prepackaged subsystem.

Mounting a Subsystem

Despite how painful this might sound, a subsystem can be mounted quite easily, that is, its existence can be declared to the main server, through the ChildApplicationContextFactory bean. This is an object that wraps the Spring Application Context that owns the subsystem and its beans. It initializes its application context as a child of the main Alfresco context with an appropriate PropertyPlaceholderConfigurer that will expand its configuration parameters.

Note that any instances you define should extend the abstractPropertyBackedBean definition. The ID you define for the bean automatically becomes the subsystem's category and hence defines where the factory will look for configuration files, in the search paths described above.

For example, in the core bootstrap-context.xml file (the file that controls the startup of beans and their order), we have

    <!--  Third party transformer Subsystem -->
    <bean id="thirdparty" class="org.alfresco.repo.management.subsystems.ChildApplicationContextFactory" parent="abstractPropertyBackedBean">
        <property name="autoStart">
            <value>true</value>
        </property>
    </bean>

The autoStart property has been set to true, meaning that the child application context will be refreshed when the server boots up, thus activating the beans it contains. For subsystems containing background processes or daemons (e.g. the file server subsystem) it is very important to set this property, because nothing else will ever cause the subsystem to 'wake up'.

Advanced Case: Mounting a Subsystem with Composite Properties

It may have dawned on you that, because a subsystem is limited to flat property sets for its configuration, it would be rather difficult to allow structured data in this configuration. This is where the concept of composite properties comes in.

A composite property is a special property whose value is a list of beans.

For example, the IMAP subsystem is mounted thus:

    <!-- IMAP Subsystem -->
    <bean id="imap" class="org.alfresco.repo.management.subsystems.ChildApplicationContextFactory" parent="abstractPropertyBackedBean">
        <property name="autoStart">
            <value>true</value>
        </property>
        <property name="compositePropertyTypes">
            <map>
                <entry key="imap.config.server.mountPoints">
                    <value>org.alfresco.repo.imap.config.ImapConfigMountPointsBean</value>
                </entry>
            </map>
        </property>
    </bean>

The above subsystem declares a single composite property called imap.config.server.mountPoints with component type org.alfresco.repo.imap.config.ImapConfigMountPointsBean.

The configured value of this composite property is actually materialized in the child application context as a ListFactoryBean. The bean's ID should match the name of the composite property. So for example, in our IMAP subsystem configuration we have the following.

    <!--The configurable list of mount points - actually a post-processed composite property! -->
    <bean id="imap.config.server.mountPoints" class="org.springframework.beans.factory.config.ListFactoryBean">
        <property name="sourceList">
            <list>
                <!-- Anything declared in here will actually be ignored and replaced by the configured composite propery value, resolved on initialization -->
                <bean id="Repository_virtual" class="org.alfresco.repo.imap.config.ImapConfigMountPointsBean">
                    <property name="mode">
                        <value>virtual</value>
                    </property>
                    <property name="store">
                        <value>${spaces.store}</value>
                    </property>
                    <property name="path">
                        <value>/${spaces.company_home.childname}</value>
                    </property>
                </bean>
                <bean id="Repository_archive" class="org.alfresco.repo.imap.config.ImapConfigMountPointsBean">
                    <property name="mode">
                        <value>archive</value>
                    </property>
                    <property name="store">
                        <value>${spaces.store}</value>
                    </property>
                    <property name="path">
                        <value>/${spaces.company_home.childname}</value>
                    </property>
                </bean>
            </list>
        </property>
    </bean>

Other beans in the subsystem application context can use imap.config.server.mountPoints as though it were a regular list of ImapConfigMountPointsBeans (which indeed it is!).

See the later sections for how to configure values for a composite property.

Programmatic use of a Subsystem

The other way of causing a subsystem to start up (if it hasn't already) is to actually programmatically call one of its interfaces.

The only way to call into a subsystem programmatically from outside (e.g. in the main server or a different subsystem) is to 'import' one or more of its interfaces into the main application context. This is done by using SubsystemProxyFactory to create a dynamic proxy that proxies one or more of the subsystem's interfaces to the main application context.

For example, in swf-transform-context.xml we have:

   <!-- Import the swftools transformer worker from the third party subsystem -->
   <bean id="transformer.worker.Pdf2swf" class="org.alfresco.repo.management.subsystems.SubsystemProxyFactory">
      <property name="sourceApplicationContextFactory">
         <ref bean="thirdparty" />
      </property>
      <property name="sourceBeanName">
         <value>transformer.worker.Pdf2swf</value>
      </property>
      <property name="interfaces">
         <list>
            <value>org.alfresco.repo.content.transform.ContentTransformerWorker</value>
         </list>
      </property>
   </bean>

You can see how it ties the ContentTransformerWorker interface to the thirdparty subsystem. In this specific case, it also names the specific bean that the proxy should target, because there may be multiple instances of ContentTransformerWorker in the child application context. If there were only one singleton ContentTransformerWorker in the child application context, it would not be necessary to name the bean, and it would be found automatically by its interface.

You should be very careful about which interfaces you choose to import in this way, as they effectively define the public interface of your subsystem. The more interfaces you mount, the more tightly coupled you become to the subsystem and the less easier it becomes to replace it with an alternative implementation. Try to make sure you do not mount interfaces that expose any internal implementation details of your subsystem.

Configuring Subsystems

As explained above, one of the strengths of subsystems is that they do not require you to edit or override core Spring configuration in order to configure Alfresco to meet your needs. You simply have to bind suitable values to the subsystem's properties. Depending on the version you are using, there are several options open to you for doing this.

JMX (Enterprise only)

In Alfresco Enterprise versions, your subsystems and all their composite properties will show up under the Alfresco:Type=Configuration tree in JConsole. When you edit a property the subsystem is stopped automatically. You can then continue to edit further properties. All property edits are persisted in the database and will be remembered on server restarts and synchronized across your cluster. Once you have finished editing properties and want to try out your changes, you can start the subsystem by calling its start operation. This allows complex configuration to be built up without endless server restarts.

Below is an example of the default authentication objects:

** objectName: Alfresco:Type=Configuration,Category=Authentication,id1=manager
++ ClassName: Authentication$manager
	chain: alfrescoNtlm1:alfrescoNtlm


** objectName: Alfresco:Type=Configuration,Category=Authentication,id1=managed,id2=alfrescoNtlm1
++ ClassName: Authentication$managed$alfrescoNtlm1
	$type: alfrescoNtlm
	alfresco.authentication.allowGuestLogin: true
	alfresco.authentication.authenticateCIFS: true
	ntlm.authentication.mapUnknownUserToGuest: false
	ntlm.authentication.sso.enabled: false

Note that the persisted data in the database takes precedence over the data in the properties files. So if you consider an authentication chain "authentication.chain=ldap1:ldap ", even if you have in shared/classes/alfresco-global.properties or in shared/classes/alfresco/extension/subsystems/Authentication/ldap/ldap1/ldap-authentication.properties a line:

ldap.synchronization.active=true

this will not be taken into account as the value in SQL take precedence.

alfresco-global.properties

Overall defaults for subsystem properties are set in this file, and in community editions it is your best option for configuring single-instance subsystems. Simply add the property you want to set to this file and restart your server. Refer to Repository_Configuration#Global_Property_Overrides for more information on alfresco-global.properties.

Setting composite properties

Recall the imap.config.server.mountPoints property we defined above. The ImapConfigMountPointsBean class that holds the component beans has four properties of its own:

  • beanName
  • store
  • rootPath
  • mode

Firstly, suppose we want to set some overall defaults for all component instances. These values would show up, for example when you added a new component instance but did not specify its properties. We use the format

<property>.default.<component property>

to specify default values for each component property. For example:

imap.config.server.mountPoints.default.store=${spaces.store}
imap.config.server.mountPoints.default.rootPath=/${spaces.company_home.childname}
imap.config.server.mountPoints.default.mode=virtual

We did not define a default for beanName because we have a special way of populating that for each instance, as we will explain soon. Now suppose we want to set up the imap.config.server.mountPoints with an actual composite value.

First, we set the master composite property using a comma-separated list

imap.config.server.mountPoints=Repository_virtual,Repository_archive

This defines that the property contains two ImapConfigMountPointsBean instances, named Repository_virtual and Repository_archive. Because ImapConfigMountPointsBean implements the BeanNameAware Spring interface and has a beanName property, these instance names are automatically set as the bean names. If we did nothing further, we would end up with two component instances with bean names set and the other default component property values defined above. But how do we define component properties specific to each component instance?

We use the format

<property>.value.<component instance name>.<component property>

So in this case, we set

imap.config.server.mountPoints.value.Repository_virtual.mode=virtual
imap.config.server.mountPoints.value.Repository_archive.mode=archive

Extension classpath

As its name suggests, alfresco-global.properties can only be used to define properties that are global to the whole system. So how do we control the properties of subsystems that can have multiple instances, e.g. the Authentication subsystems? We need to be able to target different values for the same properties to each subsystem instance. This is where the special extension classpath mechanism comes in handy.

Properties

If you add a property file to your application server's global classpath (e.g. under $TOMCAT_HOME/shared/classes) with a path matching the following pattern you can override specific properties of a subsystem instance.

alfresco/extension/subsystems/<category>/<type>/<id>/*.properties

Here, the ID is the subsystem instance identifier, which will be default for single instance subsystems, or the provided ID for chained subsystems.

So, for example, suppose your authentication chain looked like this:

authentication.chain=alfrescoNtlm1:alfrescoNtlm,ldap1:ldap

Then you could put property overrides for alfrescoNtlm1 in

alfresco/extension/subsystems/Authentication/alfrescoNtlm/alfrescoNtlm1/mychanges.properties

Remembering that the default type and ID of non-chained subsystems is default you could put overrides for file server properties in

alfresco/extension/subsystems/fileServers/default/default/mychanges.properties

OutboundSMTP and InboundSMTP are unusual subsystems in that they declare their own <type> and <id>. The correct paths for these subsystems are

alfresco/extension/subsystems/email/OutboundSMTP/outbound/mychanges.properties

and

alfresco/extension/subsystems/email/InboundSMTP/inbound/mychanges.properties

Below, we show how the properties displayed in JMX relates to the path rule. The path rule:

alfresco/extension/subsystems/<category>/<type>/<id>/*.properties

translates for the JMX object:

** objectName: Alfresco:Type=Configuration,Category=Authentication,id1=managed,id2=alfrescoNtlm1
++ ClassName: Authentication$managed$alfrescoNtlm1
	$type: alfrescoNtlm
	alfresco.authentication.allowGuestLogin: true
	alfresco.authentication.authenticateCIFS: true
	ntlm.authentication.mapUnknownUserToGuest: false
	ntlm.authentication.sso.enabled: false

into:

alfresco/extension/subsystems/Authentication/alfrescoNtlm/alfrescoNtlm1/mychanges.properties

Spring Beans

For advanced purposes, you can also extend or override the Spring Bean definitions of the subsystem.

If you add a Spring Bean file to your application server's global classpath (e.g. under $TOMCAT_HOME/shared/classes) with a path matching the following pattern you can add to or override the subsystem bean definitions.

alfresco/extension/subsystems/<category>/<type>/<id>/*-context.xml

Here, the ID is the subsystem instance identifier, which will be default for single instance subsystems, or the provided ID for chained subsystems.

So, for example, suppose your authentication chain looked like this:

authentication.chain=alfrescoNtlm1:alfrescoNtlm,ldap1:ldap

Then you could put bean definition overrides for alfrescoNtlm1 in

alfresco/extension/subsystems/Authentication/alfrescoNtlm/alfrescoNtlm1/custom-context.xml

Remembering that the default type and ID of non-chained subsystems is default you could put overrides for file server beans in

alfresco/extension/subsystems/fileServers/default/default/custom-file-servers-context.xml

Here are some 4.0 examples for SOLR and Lucene subsystems

shared/classes/alfresco/extension/subsystems/Search/solr/solr/custom-solr-context.xml
shared/classes/alfresco/extension/subsystems/Search/lucene/lucene/custom-lucene-context.xml

Debugging Alfresco Subsystems

As shown above, configuration changes can be made using

  1. file changes (alfresco-global.properties) and properties files following the 'path' rule above.
  2. JMX interface (for Entreprise versions), in which case changes are persisted in the database.

In any case, the JMX interface holds the current values of a running system. So when talking to Support, it can be handy to make a dump of your JMX settings. A JMX dumper is available on network (see file jmx-dumper-3.2.0.jar)

It can be run using:

java -jar jmx-dumper-3.2.0.jar >  envLog.log

The usage of the jar can be found using the --help option.

java -jar jmx-dumper.jar --help
Usage: java -jar jmx-dumper.jar [-u userName] [-p password] [URL]*
If no URL is specified, will attempt to locate local Alfresco processes
If no user name or password is specified, will use Alfresco defaults
Example: java -jar jmx-dumper.jar -u controlRole -p change_asap service:jmx:rmi://localhost/jndi/rmi://localhost:50500/alfresco/jmxrmi


Another (simpler) option is to use the web JMX dumper that can be accessed as 'admin' at the URL:

http://localhost:8080/alfresco/faces/jsp/admin/jmx-dumper.jsp

At this address you get the same information as running the JAR dumper.

Precedence

As discussed above, the precedence for the configuration properties files for subsystems are :

JMX changes persisted in the database 

>> precedes >>

attribute changes made in a specific subsystem 
* shared/classes/alfresco/extension/subsystems/<category>/<type>/<id>

>> precedes >>

attribute changes made in the default subsystem 
* shared/classes/alfresco/extension/subsystems/<category>/default/default

>> precedes >>

attribute changes made in the global.properties
* shared/classes/alfresco-global.properties

>> precedes >>

default attribute values set in:

* webapps/alfresco/WEB-INF/classes/alfresco/subsystems/<category>/<type>/<id>
Personal tools
© 2014 Alfresco Software, Inc. All Rights Reserved. Legal | Privacy | Accessibility