eXo Services: How we leverage IoC

The container package is responsible of building a hierarchy of containers. Each service will then be registered in one container or the other according to the XML configuration file it is defined in. It is important to understand that there can be several PortalContainer instances that all are children of the RootContainer.

The behavior of the hierarchy is similar to a class loader one, hence when you will lookup a service that depends on another one the container will look for it on the current container and if it does not find it then it will look in the parent container. That way you can load all the reusable business logic components in the same container (here the RootContainer) and differentiate the service implementation from one portal instance to the other by just loading different service implementation in two siblings PortalContainers.

Therefore, if you look at the Portal Container as a service repository for all the business logic in a portal instance then you understand why having several PortalContainers allows you to manage several portals (each one deployed as a single war) in the same server by just changing XML configuration files.

The default configuration XML files are packaged in the service jar. There exists three configuration.xml files, one for each container type. In that XML file, we define the list of services and their init parameters that will be loaded in the corresponding container.

1 Portal Instance

As there can be several portal container instances per JVM it is important to be able to configure the loaded services per instance. Therefore all the default configuration files located in the service impl jar can be overridden from the portal war. For more information refer to the technical documentation of the portal product.

2 Introduction to the XML schema of the configuration.xml file

Use component registration tags. We have introduced the key tag that defines the interface and the type tag that defines the implementation. Note that the key tag is not mandatory but it improves performance to reference it.

<!-- Portlet container hooks -->
  <component>
  <key>org.exoplatform.services.portletcontainer.persistence.PortletPreferencesPersister</key>
   <type>org.exoplatform.services.portal.impl.PortletPreferencesPersisterImpl</type>
  </component>

Register plugins that can act as listeners or external plugin to bundle some plugin class in other jar modules. The usual example is the hibernate service to which we can add hbm mapping files even if those are deployed in another maven artifact.

<external-component-plugins>
    <target-component>org.exoplatform.services.database.HibernateService</target-component>
    <component-plugin> 
      <name>add.hibernate.mapping</name>
      <set-method>addPlugin</set-method>
      <type>org.exoplatform.services.database.impl.AddHibernateMappingPlugin</type>
      <init-params>
        <values-param>
          <name>hibernate.mapping</name>
          <value>org/exoplatform/services/portal/impl/PortalConfigData.hbm.xml</value>
          <value>org/exoplatform/services/portal/impl/PageData.hbm.xml</value>
          <value>org/exoplatform/services/portal/impl/NodeNavigationData.hbm.xml</value>
        </values-param>        
      </init-params>
    </component-plugin>
  </external-component-plugins>

In that sample we target the HibernateService and we will call its addPlugin() method with an argument of the type AddHibernateMappingPlugin. That object will first have been filled with the init parameters.

Therefore, it is possible to define services that will be able to receive plugins without implementing any framework interface.

Another example of use is the case of listeners as in the following code where a listener is added to the OrganisationService and will be called each time a new user is created:

<external-component-plugins>
    <target-component>org.exoplatform.services.organization.OrganizationService</target-component>
    <component-plugin>
      <name>portal.new.user.event.listener</name>
      <set-method>addListenerPlugin</set-method>
      <type>org.exoplatform.services.portal.impl.PortalUserEventListenerImpl</type>
      <description>this listener create the portal configuration for the new user</description>
      <init-params>
        <object-param>
          <name>configuration</name>
          <description>description</description>
          <object type="org.exoplatform.services.portal.impl.NewPortalConfig">
            <field  name="predefinedUser">
              <collection type="java.util.HashSet">
                <value><string>admin</string></value>
                <value><string>exo</string></value>
                <value><string>company</string></value>
                <value><string>community</string></value>
                <value><string>portal</string></value>
                <value><string>exotest</string></value>
              </collection>
            </field>
            <field  name="templateUser"><string>template</string></field>
            <field  name="templateLocation"><string>war:/conf/users</string></field>
          </object>
        </object-param>
      </init-params>
</component-plugin>

In the previous XML configuration we reference the organization service and will call its method addListenerPlugin with an object of type PortalUserEventListenerImpl. Each time a new user will be created (apart the predefined ones in the list above) methods of the PortalUserEventListenerImpl will be called by the service.

As you can see there exists several type of init parameters from simple value params which bind a key with a value or more complex object mapping that fill a JavaBean with the info defined in the XML.

Many other examples exist such as for the Scheduler Service where you can add a job with a simple XML configuration or the JCR Service where you can add NodeType from your own configuration.xml file.

3 JMX auto wiring

Each component loaded in the container will be automatically wrapped by a MBean that will be registered in an associated MBean server. There exist one MBean server per eXo container and you can get the instance from the ExoContainer class using the method

public MBeanServer getMBeanServer()

By default the created MBean will be given a conventional name that you can override from the configuration.xml file using the jmx-name XML tag:

<component> 
    <key>org.exoplatform.services.database.HibernateService</key>
    <jmx-name>exo-service:type=HibernateService</jmx-name>
    <type>org.exoplatform.services.database.impl.HibernateServiceImpl</type>
    [...]
  </component>

Before kernel 2.0.7, you can access to your MBeanServers by adding the patch jar attached to this page to your classpath. By default this new component will bind your MBean servers to a RMI registry on localhost at 9999. You will be able to access your MBeans from any JMX Console which supports remote access like the JConsole by using the following URLs :

Container TypeAccess URL
RootContainer or StandaloneContainerservice:jmx:rmi:///jndi/rmi://localhost:9999/eXo/root
PortalContainer (in Portal mode)service:jmx:rmi:///jndi/rmi://localhost:9999/eXo/root/PORTAL_NAME
RepositoryContainerservice:jmx:rmi:///jndi/rmi://localhost:9999/eXo/root/PORTAL_NAME/REPOSITORY_NAME in Portal mode or service:jmx:rmi:///jndi/rmi://localhost:9999/eXo/root/REPOSITORY_NAME in Standalone mode
WorkspaceContainerservice:jmx:rmi:///jndi/rmi://localhost:9999/eXo/root/PORTAL_NAME/REPOSITORY_NAME/WORKSPACE_NAME in Portal mode or service:jmx:rmi:///jndi/rmi://localhost:9999/eXo/root/REPOSITORY_NAME/WORKSPACE_NAME in Standalone mode

You can of course change these properties by overloading them. Here are all the parameters

<init-params>
  <value-param>
	<name>protocol</name>
	<description>protocol is a short string that represent the protocol such as "rmi", "iiop", "jmxmp" or "soap"</description>
	<value>rmi:///jndi/rmi</value>
  </value-param>
  <value-param>
	<name>host</name>
	<description>optional hostname</description>
	<value>localhost</value>
  </value-param>
  <value-param>
	<name>port</name>
	<description>optional port</description>
	<value>9999</value>
  </value-param>
  <value-param>
	<name>path-prefix</name>
	<description>optional path prefix</description>
	<value>eXo/</value>
  </value-param>
  <value-param>
	<name>name-separator</name>
	<description>the separator used between the container names</description>
	<value>/</value>
  </value-param>
  <properties-param>
	<name>environment</name>
	<description>a set of attributes to control the new connector server's behaviour</description>
	<property name="jmx.remote.jndi.rebind" value="true"/>
  </properties-param>
</init-params>

Please, make sure that an RMI registry has been properly started (locally on 9999) before launching eXo. If you use the RMI registry provided in JDK 1.5, you can just launch rmiregistry 9999.

jconsole2.png

Since kernel 2.0.7, it is possible to register all eXo MBeans in a single local MBeanServer in order to be able to manage them through the JConsole or any another JMX Console.

  • The JVM system property org.exoplatform.container.jmx.useExistingServer can set to specify that we would like to use a local MBean server. By default, the default behavior will be preserved.
java -Dorg.exoplatform.container.jmx.useExistingServer ...
  • The JVM system property org.exoplatform.container.jmx.findExistingServer can set to specify that we would like to find a specific local MBean server. The value of this parameter is the MBean server agent id. By default, the platform MBean Server will be used.
java -Dorg.exoplatform.container.jmx.findExistingServer=${agent_id} ...
If you have no agent id to set just set the JVM system property without any value as below:
java -Dorg.exoplatform.container.jmx.findExistingServer ...
  • The JVM system property org.exoplatform.container.jmx.findExistingServerFromDefaultDomain can set to specify our local MBean server research. The value of this parameter is the MBean server default domain name. By default, only the agent id is used to find the local MBean server so if several MBean servers have the same agent id, the first one will be used.
java -Dorg.exoplatform.container.jmx.findExistingServerFromDefaultDomain=${default_domain} ...

jconsole.png

4 Configurations search and log of the search

When the RootContainer is starting the configuration search looks for configuration files in each jar available from the classpath at jar path /conf/portal/configuration.xml and from each war at path /WEB-INF/conf/configuration.xml. And adds these configurations to a set. If a component was configured in previous jar and the current jar contains one new configuration of that component the latest (from the current jar) will replace one previous configuration.

After the processing of all configuration available on system the container will initialize it and start each component in order of dependency injection (DI).

So, in common case the user/developer should be careful in configuration of same components in different configuration files. It's recommended to configure service in own jar only. Or, in case of portal configuration, strictly reconfigure the component in portal files.

But, there are components that can be (or should be) configured few times. It's individual and depends on business logic of the component. A component may initialize same resource (shared for other players) or may add a particular object to a set of objects (shared for other players too). In first case it's a critical who will be the last, i.e. whose configuration will be used. In second case it's no matter who the first who the last (if parameter objects are independent).

In case of problems with configuration of component it's important to know from which jar/war it comes. For that purpose user/developer can set JVM system property org.exoplatform.container.configuration.debug, in command line:

java -Dorg.exoplatform.container.configuration.debug ...

With that property container configuration manager will report configuration adding process to the standard output (System.out).

......
   Add configuration jar:file:/D:/Projects/eXo/dev/exo-working/exo-tomcat/lib/exo.kernel.container-trunk.jar!/conf/portal/configuration.xml
   Add configuration jar:file:/D:/Projects/eXo/dev/exo-working/exo-tomcat/lib/exo.kernel.component.cache-trunk.jar!/conf/portal/configuration.xml
   Add configuration jndi:/localhost/portal/WEB-INF/conf/configuration.xml
        import jndi:/localhost/portal/WEB-INF/conf/common/common-configuration.xml
        import jndi:/localhost/portal/WEB-INF/conf/database/database-configuration.xml
        import jndi:/localhost/portal/WEB-INF/conf/ecm/jcr-component-plugins-configuration.xml
        import jndi:/localhost/portal/WEB-INF/conf/jcr/jcr-configuration.xml 
   ......

Creator: Benjamin Mestrallet on 2007/05/22 14:19
Copyright (c) 2000-2009. Allright reserved - eXo platform SAS
1.6.13286