JBoss.orgCommunity Documentation

Chapter 2. eXoKernel

2.1. ExoContainer info
2.1.1. Container hierarchy
2.2. Service Configuration for Beginners
2.2.1. Requirements
2.2.2. Services
2.2.3. Configuration File
2.2.4. Execution Modes
2.2.5. Containers
2.2.6. Configuration Retrieval
2.2.7. Service instantiation
2.2.8. Miscellaneous
2.2.9. Further Reading
2.3. Service Configuration in Detail
2.3.1. Requirements
2.3.2. Sample Service
2.3.3. Parameters
2.3.4. External Plugin
2.3.5. Import
2.3.6. System properties
2.3.7. Understanding the prefixes supported by the configuration manager
2.4. Container Configuration
2.4.1. Kernel configuration namespace
2.4.2. Understanding how configuration files are loaded
2.4.3. System property configuration
2.4.4. Variable Syntaxes
2.4.5. Runtime configuration profiles
2.4.6. Component request life cycle
2.4.7. Thread Context Holder
2.5. Inversion Of Control
2.5.1. How
2.5.2. Injection
2.5.3. Side effects
2.6. Services Wiring
2.6.1. Portal Instance
2.6.2. Introduction to the XML schema of the configuration.xml file
2.6.3. Configuration retrieval and log of this retrieval
2.7. Component Plugin Priority
2.8. Understanding the ListenerService
2.8.1. What is the ListenerService ?
2.8.2. How does it work?
2.8.3. How to configure a listener?
2.8.4. Concrete Example
2.9. Initial Context Binder
2.9.1. API
2.10. Job Scheduler Service
2.10.1. Where is Job Scheduler Service used in eXo Products?
2.10.2. How does Job Scheduler work?
2.10.3. Reference
2.11. eXo Cache
2.11.1. Basic concepts
2.11.2. Advanced concepts
2.11.3. eXo Cache extension
2.11.4. eXo Cache based on JBoss Cache
2.11.5. eXo Cache based on Infinispan
2.12. TransactionService
2.12.1. Existing TransactionService implementations
2.13. The data source provider
2.13.1. Configuration
2.14. JNDI naming
2.14.1. Prerequisites
2.14.2. How it works
2.14.3. Configuration examples
2.14.4. Recommendations for Application Developers
2.15. Logs configuration
2.15.1. Logs configuration initializer
2.15.2. Configuration examples
2.15.3. Tips and Troubleshooting
2.16. Manageability
2.16.1. Managed framework API
2.16.2. JMX Management View
2.16.3. Example
2.17. RPC Service
2.17.1. Configuration
2.17.2. The SingleMethodCallCommand

eXo Kernel is the basis of all eXo platform products and modules. Any component available in eXo Platform is managed by the Exo Container, our micro container responsible for gluing the services through dependency injection

Therefore, each product is composed of a set of services and plugins registered to the container and configured by XML configuration files.

The Kernel module also contains a set of very low level services.

ExoContainer is the main IoC kernel object. The container is responsible for loading services/components.

This section provides you the basic knowledge about modes, services and containers. You will find out where the service configuration files should be placed, and you will also see the overriding mechanism of configurations.

Finally, you will understand how the container creates the services one after the other and what Inversion of Control really means.

Related documents

Nearly everything could be considered a service! To get a better idea, let's look into the exo-tomcat/lib folder where you find all deployed jar files.

For example you find services for databases, caching, ldap and ftp:

Of course, there are many more services, in fact a lot of these jar files are services. To find out you have to open the jar file and then look into its /conf or /conf/portal directory. Only if there is a file named configuration.xml, you are sure to have found a service.

Interface - Implementation It's important to get the idea that you separate the interface and implementation for a service. That is a good concept to reduce dependencies on specific implementations. This concept is well known for JDBC. If you use standard JDBC (=interface), you can connect any database (=implementation) to your application. In a similar way any service in eXo is defined by a java interface and may have many different implementations. The service implementation is then injected by a container into the application.

Singleton Each service has to be implemented as a singleton, which means that each service is created only once - in one single instance.

Service = Component You always read about services, and you imagine a service as a large application which does big things, but that's not true, a service can be just a little component that reads or transforms a document, therefore the term component is often used instead of service - so bear in mind: a service and a component can safely be considered to be the same thing.

The jar file of a service should contain a default configuration, you find this configuration in the configuration.xml file which comes with the jar. A configuration file can specify several services, as well as there can be several services in one jar file.

For example open the exo.kernel.component.cache-2.0.5.jar file and inside this jar open /conf/portal/configuration.xml. You will see:

 
<component>
<key>org.exoplatform.services.cache.CacheService</key> 
<type>org.exoplatform.services.cache.impl.CacheServiceImpl</type> 
...

Here you will note that a service is specified between the <component> tags. Each service has got a key, which defines the kind of service. As you imagine the content of the <key> tag matches the qualified java interface name (org.exoplatform.services.cache.CacheService) of the service. The specific implementation class of the CacheService is defined in the <type> tag.

Parameters You have already opened some configuration files and seen that there are more than just <key> and <type> tags. You can provide your service with init parameters. The parameters can be simple parameters, properties, or object-params. There are also plugins and they are special because the container calls the setters of your service in order to inject your plugin in your service (called setter injection) see Service Configuration in Detail. In general your service is free to use init parameters, they are not required.

If you ever need to create your own service, the minimum is to create an empty interface, an empty class and a constructor for your class - that's all. Ok, you also should put your class and the interface in a jar file and add a default configuration file.

In order to access to a service you need to use a Container. Just open https://github.com/exoplatform/kernel/tree/stable/2.3.x/exo.kernel.container/src/main/java/org/exoplatform/container.

Among the classes you see in this directory, you only will be interested in these three container types:

  • RootContainer: This is a base container. This container plays an important role during startup, but you should not use it directly.

  • PortalContainer: Created at the startup of the portal web application (in the init() method of the PortalController servlet)

  • StandaloneContainer: A context independent eXo Container. The StandaloneContainer is also used for unit tests.

Use only one container Even if there are several container types you always use exactly one. The RootContainer is never directly used and it depends on the execution mode if you use the PortalContainer or the StandaloneContainer. You will ask how to find out the execution mode in my application and how to manage these two modes. It's easy, you don't have to worry about it because the ExoContainerContext class provides a static method that allows you to get the right container from anywhere (see info box).

PicoContainer All containers inherit from the ExoContainer class which itself inherits from a PicoContainer. PicoContainer is a framework which allows eXo to apply the IoC (Inversion of Control) principles. The precise implementations of any service is unknown at compile time. Various implementations can be used, eXo supplies different implementations but they also may be delivered by other vendors. The decision which service to use during runtime is made in configuration files.

These configuration files are read by the container, the container adds all services to a list or more exactly a java HashTable. It's completely correct to suppose that the configuration.xml you already saw plays an important role. But there are more places where a configuration for a service can be defined as you see in the next section.

Note

"In your java code you have to use

ExoContainer myContainer = ExoContainerContext.getCurrentContainer();

in order to access to the current container. It doesn't greatly matter to your application if the current container is a PortalContainer or a StandaloneContainer. Once you have your container you may access to any service registered in this container using

MyService myService = (MyService) myContainer.getComponentInstance(MyService.class);

You easily realize that MyService.class is the name of the service interface.

The configuration you find inside the jar file is considered as the default configuration. If you want to override this default configuration you can do it in different places outside the jar. When the container finds several configurations for the same service, the configuration which is found later replaces completely the one found previously. Let's call this the configuration override mechanism.

As both containers, PortalContainer and StandaloneContainer, depend on the RootContainer, we will start by looking into this one.

The retrieval sequence in short:

HashTable The RootContainer creates a java HashTable which contains key-value pairs for the services. The qualified interface name of each service is used as key for the hashtable. Hopefully you still remember that the <key> tag of the configuration file contains the interface name? The value of each hashtable pair is an object that contains the service configuration (yes, this means the whole structure between the <component> tags of your configuration.xml file).

The RootContainer runs over all jar files you find in exo-tomcat/lib and looks if there is a configuration file at /conf/configuration.xml, the services configured in this file are added to the hashtable. That way - at the end of this process - the default configurations for all services are stored in the hashtable.

If you wish to provide your own configurations for one or several services, you can do it in a general configuration file that has to be placed at exo-tomcat/exo-conf/configuration.xml. Do not search for such a file on your computer - you won't find one, because this option is not used in the default installation. Here again the same rule applies: The posterior configuration replaces the previous one.

The further configuration retrieval depends on the container type.

The PortalContainer takes the hashtable filled by the RootContainer and continues to look in some more places. Here you get the opportunity to replace RootContainer configurations by those which are specific to your portal. Again, the configurations are overridden whenever necessary.

In short PortalContainer configurations are retrieved in the following lookup sequence :

You see, here the /conf/portal/configuration.xml file of each jar enters the game, they are searched at first. Next, there is nearly always a configuration.xml in the portal.war file (or in the portal webapp folder), you find this file at /WEB-INF/conf/configuration.xml. If you open it, you will find a lot of import statements that point to other configuration files in the same portal.war (or portal webapp).

Multiple Portals Be aware that you might set up several different portals ("admin", "mexico", etc.), and each of these portals will use a different PortalContainer. And each of these PortalContainers can be configured separately. As of GateIn you also will be able to provide configurations from outside the jars and wars or webapps. Put a configuration file in exo-tomcat/exo-conf/portal/$portal_name/configuration.xml where $portal_name is the name of the portal you want to configure for . But normally you only have one portal which is called "portal" so you use exo-tomcat/exo-conf/portal/portal/configuration.xml.

In the same way as the PortalContainer the StandaloneContainer takes over the configuration of the RootContainer. After that our configuration gets a little bit more tricky because standalone containers can be initialized using an URL. This URL contains a link to an external configuration. As you probably never need a standalone configuration you can safely jump over the remaining confusing words of this section.

After taking over RootContainer's configuration, there are three cases which depend on the URL initialization, :

As you have already learned the services are all singletons, so that the container creates only one single instance of each container. The services are created by calling the constructors (called constructor injection). If there are only zero-arguments constructors (Foo public Foo(){}) there are no problems to be expected. That's easy.

But now look at OrganizationServiceImpl.java

This JDBC implementation of BaseOrganizationService interface has only one constructor:

public OrganizationServiceImpl(ListenerService listenerService, DatabaseService dbService);

You see this service depends on two other services. In order to be able to call this constructor the container first needs a ListenerService and a DatabaseService. Therefore these services must be instantiated before BaseOrganizationService, because BaseOrganizationService depends on them.

For this purpose the container first looks at the constructors of all services and creates a matrix of service dependencies in order to call the services in a proper order. If for any reason there are interdependencies or circular dependencies you will get a java Exception. In this way the dependencies are injected by the container.

Note

What happens if one service has more than one constructor? The container always tries first to use the constructor with a maximum number of arguments, if this is not possible the container continues step by step with constructors that have less arguments until arriving at the zero-argument constructor (if there is any).

Retrospection. Do you remember your last project where you had some small components and several larger services? How was this organized? Some services had their own configuration files, others had static values in the source code. Most components were probably tightly coupled to the main application, or you called static methods whenever you needed a service in your java class. Presumably you even copied the source code of an earlier project in order to adapt the implementation to your needs. In short:

New Approach. You have seen that eXo uses the Inversion of Control (IoC) pattern which means that the control of the services is given to an independent outside entity, in this case a container. Now the container takes care of everything:

Dependency Injection. You also saw two types of dependency injections:

Do you feel yourself to be an expert now? Not yet? Get a deeper look and read this Services Wiring article. You read so much about configuration, that you should wonder what the XML Schema of the configuration file looks like.

If you wish to see examples of service configurations you should study the Core. Where you find descriptions of some eXo's core services. Finally you might wish to read more about PicoContainer.

This section shows you how to set up a sample service with some configurations and how to access the configuration parameters. The later sections describe all details of the configuration file (parameters, object-params, plugins, imports, and more). It also shows how to access the configuration values. You may consider this document as a reference, but you can also use this document as a tutorial and read it from the beginning to the end.

Related documents

You should have read and understood Service Configuration for Beginners. Obviously you should know java and xml. We are working with examples that are created for teaching reasons only and you will see extracts from the eXo Products default installation. When reading this article, you do not forget that the terms service and component are interchangeable in eXo Products.

You see your service has a configuration file, but you wonder how the file can gain access to its configuration. Imagine that you are asked to implement two different calculation methods: fast and exact.

You create one init parameter containing the calculation methods. For the exact method, you wish to configure more details for the service. Let's enhance the word service configuration file:

  <component>
    <key>com.laverdad.services.ArticleStatsService</key>
    <type>com.laverdad.services.ArticleStatsServiceImpl</type>
    <init-params>
      <value-param>
        <name>calc-method</name>
        <description>calculation method: fast, exact</description>
        <value>fast</value>
      </value-param>
      <properties-param>
        <name>details-for-exact-method</name>
        <description>details for exact phrase counting</description>
        <property name="language" value="English" />
        <property name="variant" value="us" />
      </properties-param>
    </init-params>
  </component>

Now let's see how our service can read this configuration. The implementation of the calcSentences() method serves just as a simple example. It's up to your imagination to implement the exact method.

public class ArticleStatsServiceImpl implements ArticleStatsService {

  private String calcMethod = "fast";
  private String variant = "French";
  private String language = "France";
  
  public ArticleStatsServiceImpl(InitParams initParams) {
    super();
    calcMethod = initParams.getValueParam("calc-method").getValue();
    PropertiesParam detailsForExactMethod = initParams.getPropertiesParam("details-for-exact-method");
    if ( detailsForExactMethod != null) {
      language = detailsForExactMethod.getProperty("language");
      variant = detailsForExactMethod.getProperty("variant");
    }
  }
  
  public int calcSentences(String article) {
    if (calcMethod == "fast") {
      // just count the number of periods "." 
      int res = 0;
      int period = article.indexOf('.');
      while (period != -1) {
        res++;
        article = article.substring(period+1);
        period = article.indexOf('.');
      }      
      return  res;
    } 
    throw new RuntimeException("Not implemented");
    }
}

You see you just have to declare a parameter of org.exoplatform.container.xml.InitParams in your constructor. The container provides an InitParams object that correspond to the xml tree of init-param.

As you want to follow the principle of Inversion of Control, you must not access the service directly. You need a Container to access the service.

With this command you get your current container:

This might be a PortalContainer or a StandaloneContainer, dependant on the execution mode in which you are running your application.

Whenever you need one of the services that you have configured use the method:

  • myContainer.getComponentInstance(class)

In our case:

  • ArticleStatsService statsService = (ArticleStatsService) myContainer.getComponentInstance(ArticleStatsService.class);

Recapitulation:

package com.laverdad.common;

import org.exoplatform.container.ExoContainer;
import org.exoplatform.container.ExoContainerContext;
import com.laverdad.services.*;

public class Statistics {

  public int makeStatistics(String articleText) {
    ExoContainer myContainer = ExoContainerContext.getCurrentContainer();
    ArticleStatsService statsService = (ArticleStatsService)
        myContainer.getComponentInstance(ArticleStatsService.class);    
    int numberOfSentences = statsService.calcSentences(articleText);
    return numberOfSentences;
  }
  
  public static void main( String args[]) {
   Statistics stats = new Statistics();
   String newText = "This is a normal text. The method only counts the number of periods. "
   + "You can implement your own implementation with a more exact counting. "
   + "Let`s make a last sentence.";
  System.out.println("Number of sentences: " + stats.makeStatistics(newText));
  }
}

If you test this sample in standalone mode, you need to put all jars of eXo Kernel in your buildpath, furthermore picoContainer is needed.

Let's have a look at the configuration of the LDAPService. It's not important to know LDAP, we only discuss the parameters.

<component>
    <key>org.exoplatform.services.ldap.LDAPService</key>
    <type>org.exoplatform.services.ldap.impl.LDAPServiceImpl</type>
    <init-params>
      <object-param>
        <name>ldap.config</name>
        <description>Default ldap config</description>
        <object type="org.exoplatform.services.ldap.impl.LDAPConnectionConfig">         
   <field  name="providerURL"><string>ldaps://10.0.0.3:636</string></field>
   <field  name="rootdn"><string>CN=Administrator,CN=Users,DC=exoplatform,DC=org</string></field>
   <field  name="password"><string>exo</string></field>
   <field  name="version"><string>3</string></field>
     <field  name="minConnection"><int>5</int></field>
       <field  name="maxConnection"><int>10</int></field>    
       <field  name="referralMode"><string>ignore</string></field>
       <field  name="serverName"><string>active.directory</string></field>
       </object>
      </object-param>
    </init-params>
</component>

You see here an object-param is being used to pass the parameters inside an object (actually a java bean). It consists of a name, a description and exactly one object. The object defines the type and a number of fields.

Here you see how the service accesses the object:

package org.exoplatform.services.ldap.impl;

public class LDAPServiceImpl implements LDAPService {
...
  public LDAPServiceImpl(InitParams params) {
    LDAPConnectionConfig config = (LDAPConnectionConfig) params.getObjectParam("ldap.config")
                                                               .getObject();
...

The passed object is LDAPConnectionConfig which is a classic java bean. It contains all fields and also the appropriate getters and setters (not listed here). You also can provide default values. The container creates a new instance of your bean and calls all setters whose values are configured in the configuration file.

package org.exoplatform.services.ldap.impl;

public class LDAPConnectionConfig {
  private String providerURL        = "ldap://127.0.0.1:389";
  private String rootdn;
  private String password;                                  
  private String version;                                   
  private String authenticationType = "simple";
  private String serverName         = "default";
  private int    minConnection;
  private int    maxConnection;
  private String referralMode       = "follow";
...

You see that the types (String, int) of the fields in the configuration correspond with the bean. A short glance in the kernel_1_0.xsd file let us discover more simple types:

Have a look on this type test xml file: object.xml.

You also can use java collections to configure your service. In order to see an example, let's open the database-organization-configuration.xml file. This file defines a default user organization (users, groups, memberships/roles) of your portal. They use component-plugins which are explained later. You wil see that object-param is used again.

There are two collections: The first collection is an ArrayList. This ArrayList contains only one value, but there could be more. The only value is an object which defines the field of the NewUserConfig$JoinGroup bean.

The second collection is a HashSet that is a set of strings.

    <component-plugin>
      <name>new.user.event.listener</name>
      <set-method>addListenerPlugin</set-method>
      <type>org.exoplatform.services.organization.impl.NewUserEventListener</type>
      <description>this listener assign group and membership to a new created user</description>
      <init-params>
        <object-param>
          <name>configuration</name>
          <description>description</description>
          <object type="org.exoplatform.services.organization.impl.NewUserConfig">
            <field  name="group">
              <collection type="java.util.ArrayList">
                <value>
                  <object type="org.exoplatform.services.organization.impl.NewUserConfig$JoinGroup">
                    <field  name="groupId"><string>/platform/users</string></field>
                    <field  name="membership"><string>member</string></field>
                  </object>
                </value>               
              </collection>
            </field>
            <field  name="ignoredUser">
              <collection type="java.util.HashSet">
                <value><string>root</string></value>
                <value><string>john</string></value>
                <value><string>marry</string></value>
                <value><string>demo</string></value>
                <value><string>james</string></value>
              </collection>
            </field>
          </object>
        </object-param>
      </init-params>
    </component-plugin>

Let's look at the org.exoplatform.services.organization.impl.NewUserConfig bean:

public class NewUserConfig {
  private List    role;
  private List    group;
  private HashSet ignoredUser;

  ...

  public void setIgnoredUser(String user) {
    ignoredUser.add(user);

  ...

  static public class JoinGroup {
    public String  groupId;
    public String  membership;
  ...
}

You see the values of the HashSet are set one by one by the container, and it's the responsibility of the bean to add these values to its HashSet.

The JoinGroup object is just an inner class and implements a bean of its own. It can be accessed like any other inner class using NewUserConfig.JoinGroup.

The External Plugin allows you to add configuration on the fly.

As you have carefully read Service Configuration for Beginners you know that normally newer configurations always replaces previous configurations. An external plugin allows you to add configuration without replacing previous configurations.

That can be interesting if you adapt a service configuration for your project-specific needs (country, language, branch, project, etc.).

Let's have a look at the configuration of the TaxonomyPlugin of the CategoriesService:

 <external-component-plugins>
    <target-component>org.exoplatform.services.cms.categories.CategoriesService</target-component>    
    <component-plugin>
     <name>predefinedTaxonomyPlugin</name>
      <set-method>addTaxonomyPlugin</set-method>
      <type>org.exoplatform.services.cms.categories.impl.TaxonomyPlugin</type>
      <init-params>
       <value-param>
          <name>autoCreateInNewRepository</name>
          <value>true</value>
         </value-param>         
         <value-param>
          <name>repository</name>
          <value>repository</value>
         </value-param>         
       <object-param>
        <name>taxonomy.configuration</name>
           <description>configuration predefined taxonomies to inject in jcr</description>
           <object type="org.exoplatform.services.cms.categories.impl.TaxonomyConfig">            
            <field  name="taxonomies">
             <collection type="java.util.ArrayList">
               <!-- cms taxonomy -->
              <value>
               <object type="org.exoplatform.services.cms.categories.impl.TaxonomyConfig$Taxonomy">
                 <field  name="name"><string>cmsTaxonomy</string></field>                              
                <field  name="path"><string>/cms</string></field>                                              
               </object>
              </value>
              <value> 
               <object type="org.exoplatform.services.cms.categories.impl.TaxonomyConfig$Taxonomy">
                 <field  name="name"><string>newsTaxonomy</string></field>                              
                <field  name="path"><string>/cms/news</string></field>                                              
               </object>
              </value>
            </field>                     
           </object>
       </object-param>
     </init-params>
   </component-plugin>
<external-component-plugins>

The <target-component> defines the service for which the plugin is defined. The configuration is injected by the container using a method that is defined in <set-method>. The method has exactly one argument of the type org.exoplatform.services.cms.categories.impl.TaxonomyPlugin:

  • addTaxonomyPlugin(org.exoplatform.services.cms.categories.impl.TaxonomyPlugin plugin)

The content of <init-params> corresponds to the structure of the TaxonomyPlugin object.

Note

You can configure the component CategoriesService using the addTaxonomyPlugin as often as you wish, you can also call addTaxonomyPlugin in different configuration files. The method addTaxonomyPlugin is then called several times, everything else depends on the implementation of the method.

The configuration manager allows you to find files using URL with special prefixes that we describe in details below.

GateIn uses PicoContainer, which implements the Inversion of Control (IoC) design pattern. All eXo containers inherit from a PicoContainer. There are mainly two eXo containers used, each of them can provide one or several services. Each container service is delivered in a JAR file. This JAR file may contain a default configuration. The use of default configurations is recommended and most services provide it.

When a Pico Container searches for services and its configurations, each configurable service may be reconfigured to override default values or set additional parameters. If the service is configured in two or more places the configuration override mechanism will be used.

Confused? - You might be interested in the Service Configuration for Beginners section to understand the basics.

GateIn uses PicoContainer, which implements the Inversion of Control (IoC) design pattern. All eXo containers inherit from a PicoContainer. There are mainly two eXo containers used, each of them can provide one or several services. Each container service is delivered in a JAR file. This JAR file may contain a default configuration. The use of default configurations is recommended and most of services provide it.

When a Pico Container searches for services and its configurations, each configurable service may be reconfigured to override default values or set additional parameters. If the service is configured in two or more places, the configuration override mechanism will be used.

The container performs the following steps to make eXo Container configuration retrieval, depending on the container type.

After the processing of all configurations available in system, the container will initialize it and start each service in order of the dependency injection (DI).

The user/developer should be careful when configuring the same service in different configuration files. It's recommended to configure a service in its own JAR only. Or, in case of a portal configuration, strictly reconfigure the services in portal WAR files or in an external configuration.

There are services that can be (or should be) configured more than one time. This depends on business logic of the service. A service may initialize the same resource (shared with other services) or may add a particular object to a set of objects (shared with other services too). In the first case, it's critical who will be the last, i.e. whose configuration will be used. In the second case, it's no matter who is the first and who is the last (if the parameter objects are independent).

Since eXo JCR 1.12, we added a set of new features that have been designed to extend portal applications such as GateIn.

Now we can define precisely a portal container and its dependencies and settings thanks to the PortalContainerDefinition that currently contains the name of the portal container, the name of the rest context, the name of the realm, the web application dependencies ordered by loading priority (i.e. the first dependency must be loaded at first and so on..) and the settings.

To be able to define a PortalContainerDefinition, we need to ensure first of all that a PortalContainerConfig has been defined at the RootContainer level, see an example below:

  <component>
    <!-- The full qualified name of the PortalContainerConfig -->
    <type>org.exoplatform.container.definition.PortalContainerConfig</type>
    <init-params>
      <!-- The name of the default portal container -->
      <value-param>
        <name>default.portal.container</name>
        <value>myPortal</value>
      </value-param>
      <!-- The name of the default rest ServletContext -->
      <value-param>
        <name>default.rest.context</name>
        <value>myRest</value>
      </value-param>
      <!-- The name of the default realm -->
      <value-param>
        <name>default.realm.name</name>
        <value>my-exo-domain</value>
      </value-param>
     <!-- Indicates whether the unregistered webapps have to be ignored -->
     <value-param>
        <name>ignore.unregistered.webapp</name>
        <value>true</value>
     </value-param>
      <!-- The default portal container definition -->
      <!-- It cans be used to avoid duplicating configuration -->
      <object-param>
        <name>default.portal.definition</name>
        <object type="org.exoplatform.container.definition.PortalContainerDefinition">
          <!-- All the dependencies of the portal container ordered by loading priority -->
          <field name="dependencies">
            <collection type="java.util.ArrayList">
              <value>
                <string>foo</string>
              </value>
              <value>
                <string>foo2</string>
              </value>
              <value>
                <string>foo3</string>
              </value>
            </collection>
          </field>        
          <!-- A map of settings tied to the default portal container -->
          <field name="settings">
            <map type="java.util.HashMap">
              <entry>
                <key>
                  <string>foo5</string>
                </key>
                <value>
                  <string>value</string>
                </value>
              </entry>
              <entry>
                <key>
                  <string>string</string>
                </key>
                <value>
                  <string>value0</string>
                </value>
              </entry>
              <entry>
                <key>
                  <string>int</string>
                </key>
                <value>
                  <int>100</int>
                </value>
              </entry>
            </map>
          </field>
          <!-- The path to the external properties file -->
          <field name="externalSettingsPath">
            <string>classpath:/org/exoplatform/container/definition/default-settings.properties</string>
          </field>
        </object>
      </object-param>
    </init-params>
  </component>

Note

All the value of the parameters marked with a (*) can be defined thanks to System properties like any values in configuration files but also thanks to variables loaded by the PropertyConfigurator. For example in GateIn by default, it would be all the variables defined in the file configuration.properties.

A new PortalContainerDefinition can be defined at the RootContainer level thanks to an external plugin, see an example below:

  <external-component-plugins>
    <!-- The full qualified name of the PortalContainerConfig -->
    <target-component>org.exoplatform.container.definition.PortalContainerConfig</target-component>
    <component-plugin>
      <!-- The name of the plugin -->
      <name>Add PortalContainer Definitions</name>
      <!-- The name of the method to call on the PortalContainerConfig in order to register the PortalContainerDefinitions -->
      <set-method>registerPlugin</set-method>
      <!-- The full qualified name of the PortalContainerDefinitionPlugin -->
      <type>org.exoplatform.container.definition.PortalContainerDefinitionPlugin</type>
      <init-params>
        <object-param>
          <name>portal</name>
          <object type="org.exoplatform.container.definition.PortalContainerDefinition">
            <!-- The name of the portal container -->
            <field name="name">
              <string>myPortal</string>
            </field>
            <!-- The name of the context name of the rest web application -->
            <field name="restContextName">
              <string>myRest</string>
            </field>
            <!-- The name of the realm -->
            <field name="realmName">
              <string>my-domain</string>
            </field>
            <!-- All the dependencies of the portal container ordered by loading priority -->
            <field name="dependencies">
              <collection type="java.util.ArrayList">
                <value>
                  <string>foo</string>
                </value>
                <value>
                  <string>foo2</string>
                </value>
                <value>
                  <string>foo3</string>
                </value>
              </collection>
            </field>
            <!-- A map of settings tied to the portal container -->
            <field name="settings">
              <map type="java.util.HashMap">
                <entry>
                  <key>
                    <string>foo</string>
                  </key>
                  <value>
                    <string>value</string>
                  </value>
                </entry>
                <entry>
                  <key>
                    <string>int</string>
                  </key>
                  <value>
                    <int>10</int>
                  </value>
                </entry>
                <entry>
                  <key>
                    <string>long</string>
                  </key>
                  <value>
                    <long>10</long>
                  </value>
                </entry>
                <entry>
                  <key>
                    <string>double</string>
                  </key>
                  <value>
                    <double>10</double>
                  </value>
                </entry>
                <entry>
                  <key>
                    <string>boolean</string>
                  </key>
                  <value>
                    <boolean>true</boolean>
                  </value>
                </entry>                                
              </map>
            </field>            
            <!-- The path to the external properties file -->
            <field name="externalSettingsPath">
              <string>classpath:/org/exoplatform/container/definition/settings.properties</string>
            </field>
          </object>
        </object-param>
      </init-params>
    </component-plugin>
  </external-component-plugins>

Table 2.2. Descriptions of the fields of a PortalContainerDefinition when it is used to define a new portal container

name (*)The name of the portal container. This field is mandatory .
restContextName (*)The name of the context name of the rest web application. This field is optional. The default value will be defined at the PortalContainerConfig level.
realmName (*)The name of the realm. This field is optional. The default value will be defined at the PortalContainerConfig level.
dependenciesAll the dependencies of the portal container ordered by loading priority. This field is optional. The default value will be defined at the PortalContainerConfig level. The dependencies are in fact the list of the context names of the web applications from which the portal container depends. This field is optional. The dependency order is really crucial since it will be interpreted the same way by several components of the platform. All those components, will consider the 1st element in the list less important than the second element and so on. It is currently used to:
  • Know the loading order of all the dependencies.

  • If we have several PortalContainerConfigOwner

    • The ServletContext of all the PortalContainerConfigOwner will be unified, if we use the unified ServletContext (PortalContainer.getPortalContext()) to get a resource, it will try to get the resource in the ServletContext of the most important PortalContainerConfigOwner (i.e. last in the dependency list) and if it cans find it, it will try with the second most important PortalContainerConfigOwner and so on.

    • The ClassLoader of all the PortalContainerConfigOwner will be unified, if we use the unified ClassLoader (PortalContainer.getPortalClassLoader()) to get a resource, it will try to get the resource in the ClassLoader of the most important PortalContainerConfigOwner (i.e. last in the dependency list) and if it can find it, it will try with the second most important PortalContainerConfigOwner and so on.

settingsA java.util.Map of internal parameters that we would like to tie the portal container. Those parameters could have any type of value. This field is optional. If some internal settings are defined at the PortalContainerConfig level, the two maps of settings will be merged. If a setting with the same name is defined in both maps, it will keep the value defined at the PortalContainerDefinition level.
externalSettingsPathThe path of the external properties file to load as default settings to the portal container. This field is optional. If some external settings are defined at the PortalContainerConfig level, the two maps of settings will be merged. If a setting with the same name is defined in both maps, it will keep the value defined at the PortalContainerDefinition level. The external properties files can be either of type "properties" or of type "xml". The path will be interpreted as follows:
  1. The path doesn't contain any prefix of type "classpath:", "jar:" or "file:", we assume that the file could be externalized so we apply the following rules:

    1. A file exists at ${exo-conf-dir}/portal/${portalContainerName}/${externalSettingsPath}, we will load this file.

    2. No file exists at the previous path, we then assume that the path cans be interpreted by the ConfigurationManager.

  2. The path contains a prefix, we then assume that the path cans be interpreted by the ConfigurationManager.


Table 2.3. Descriptions of the fields of a PortalContainerDefinition when it is used to define the default portal container

name (*)The name of the portal container. This field is optional. The default portal name will be:
  1. If this field is not empty, then the default value will be the value of this field.

  2. If this field is empty and the value of the parameter default.portal.container is not empty, then the default value will be the value of the parameter.

  3. If this field and the parameter default.portal.container are both empty, the default value will be "portal".

restContextName (*)The name of the context name of the rest web application. This field is optional. The default value wil be:
  1. If this field is not empty, then the default value will be the value of this field.

  2. If this field is empty and the value of the parameter default.rest.context is not empty, then the default value will be the value of the parameter.

  3. If this field and the parameter default.rest.context are both empty, the default value will be "rest".

realmName (*)The name of the realm. This field is optional. The default value wil be:
  1. If this field is not empty, then the default value will be the value of this field.

  2. If this field is empty and the value of the parameter default.realm.name is not empty, then the default value will be the value of the parameter.

  3. If this field and the parameter default.realm.name are both empty, the default value will be "exo-domain".

dependenciesAll the dependencies of the portal container ordered by loading priority. This field is optional. If this field has a non empty value, it will be the default list of dependencies.
settingsA java.util.Map of internal parameters that we would like to tie the default portal container. Those parameters could have any type of value. This field is optional.
externalSettingsPathThe path of the external properties file to load as default settings to the default portal container. This field is optional. The external properties files can be either of type "properties" or of type "xml". The path will be interpreted as follows:
  1. The path doesn't contain any prefix of type "classpath:", "jar:" or "file:", we assume that the file could be externalized so we apply the following rules:

    1. A file exists at ${exo-conf-dir}/portal/${externalSettingsPath}, we will load this file.

    2. No file exists at the previous path, we then assume that the path cans be interpreted by the ConfigurationManager.

  2. The path contains a prefix, we then assume that the path cans be interpreted by the ConfigurationManager.


Note

All the value of the parameters marked with a (*) can be defined thanks to System properties like any values in configuration files but also thanks to variables loaded by the PropertyConfigurator. For example in GateIn by default, it would be all the variables defined in the file configuration.properties.

Internal and external settings are both optional, but if we give a non empty value for both the application will merge the settings. If the same setting name exists in both settings, we apply the following rules:

  1. The value of the external setting is null, we ignore the value.

  2. The value of the external setting is not null and the value of the internal setting is null, the final value will be the external setting value that is of type String.

  3. Both values are not null, we will have to convert the external setting value into the target type which is the type of the internal setting value, thanks to the static method valueOf(String), the following sub-rules are then applied:

    1. The method cannot be found, the final value will be the external setting value that is of type String.

    2. The method can be found and the external setting value is an empty String, we ignore the external setting value.

    3. The method can be found and the external setting value is not an empty String but the method call fails, we ignore the external setting value.

    4. The method can be found and the external setting value is not an empty String and the method call succeeds, the final value will be the external setting value that is of type of the internal setting value.

We can inject the value of the portal container settings into the portal container configuration files thanks to the variables which name start with "portal.container.", so to get the value of a setting called "foo", just use the following syntax ${portal.container.foo}. You can also use internal variables, such as:


You can find below an example of how to use the variables:

<configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd http://www.exoplatform.org/xml/ns/kernel_1_2.xsd"
  xmlns="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd">
  <component>
    <type>org.exoplatform.container.TestPortalContainer$MyComponent</type>
    <init-params>
      <!-- The name of the portal container -->
      <value-param>
        <name>portal</name>
        <value>${portal.container.name}</value>
      </value-param>
      <!-- The name of the rest ServletContext -->
      <value-param>
        <name>rest</name>
        <value>${portal.container.rest}</value>
      </value-param>
      <!-- The name of the realm -->
      <value-param>
        <name>realm</name>
        <value>${portal.container.realm}</value>
      </value-param>
      <value-param>
        <name>foo</name>
        <value>${portal.container.foo}</value>
      </value-param>
      <value-param>
        <name>before foo after</name>
        <value>before ${portal.container.foo} after</value>
      </value-param>
    </init-params>
  </component>
</configuration>

In the properties file corresponding to the external settings, you can reuse variables previously defined (in the external settings or in the internal settings) to create a new variable. In this case, the prefix "portal.container." is not needed, see an example below:

my-var1=value 1
my-var2=value 2
complex-value=${my-var1}-${my-var2}

In the external and internal settings, you can also use create variables based on value of System paramaters. The System parameters can either be defined at launch time or thanks to the PropertyConfigurator (see next section for more details). See an example below:

temp-dir=${java.io.tmpdir}${file.separator}my-temp

However, for the internal settings, you can use System parameters only to define settings of type java.lang.String.

It cans be also very usefull to define a generic variable in the settings of the default portal container, the value of this variable will change according to the current portal container. See below an example:

my-generic-var=value of the portal container "${name}"

If this variable is defined at the default portal container level, the value of this variable for a portal container called "foo" will be value of the portal container "foo".

It is possible to use component-plugin elements in order to dynamically change a PortalContainerDefinition. In the example below, we add the dependency foo to the default portal container and to the portal containers called foo1 and foo2:

<external-component-plugins>
  <!-- The full qualified name of the PortalContainerConfig -->
  <target-component>org.exoplatform.container.definition.PortalContainerConfig</target-component>
  <component-plugin>
    <!-- The name of the plugin -->
    <name>Change PortalContainer Definitions</name>
    <!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions -->
    <set-method>registerChangePlugin</set-method>
    <!-- The full qualified name of the PortalContainerDefinitionChangePlugin -->
    <type>org.exoplatform.container.definition.PortalContainerDefinitionChangePlugin</type>
    <init-params>
      <value-param>
        <name>apply.default</name>
        <value>true</value>
      </value-param>
      <values-param>
        <name>apply.specific</name>
        <value>foo1</value>
        <value>foo2</value>
      </values-param>  
      <object-param>
        <name>change</name>
        <object type="org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependencies">
          <!-- The list of name of the dependencies to add -->
          <field name="dependencies">
            <collection type="java.util.ArrayList">
              <value>
                <string>foo</string>
              </value>
            </collection>
          </field>
        </object>
      </object-param>     
    </init-params>
  </component-plugin>
</external-component-plugins>

Note

All the value of the parameters marked with a (*) can be defined thanks to System properties like any values in configuration files but also thanks to variables loaded by the PropertyConfigurator. For example in GateIn by default, it would be all the variables defined in the file configuration.properties.

To identify the portal containers to which the changes have to be applied, we use the follwing algorithm:

  1. The parameter apply.all has been set to true. The corresponding changes will be applied to all the portal containers. The other parameters will be ignored.

  2. The parameter apply.default has been set to true and the parameter apply.specific is null. The corresponding changes will be applied to the default portal container only.

  3. The parameter apply.default has been set to true and the parameter apply.specific is not null. The corresponding changes will be applied to the default portal container and the given list of specific portal containers.

  4. The parameter apply.default has been set to false or has not been set and the parameter apply.specific is null. The corresponding changes will be applied to the default portal container only.

  5. The parameter apply.default has been set to false or has not been set and the parameter apply.specific is not null. The corresponding changes will be applied to the given list of specific portal containers.

The modifications that can be applied to a PortalContainerDefinition must be a class of type PortalContainerDefinitionChange. The product proposes out of the box some implementations that we describe in the next sub sections.

This modification adds a list of dependencies at the end of the list of dependencies defined into the PortalContainerDefinition. The full qualified name is org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependencies.


See an example below, that will add foo at the end of the dependency list of the default portal container:

<external-component-plugins>
  <!-- The full qualified name of the PortalContainerConfig -->
  <target-component>org.exoplatform.container.definition.PortalContainerConfig</target-component>
  <component-plugin>
    <!-- The name of the plugin -->
    <name>Change PortalContainer Definitions</name>
    <!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions -->
    <set-method>registerChangePlugin</set-method>
    <!-- The full qualified name of the PortalContainerDefinitionChangePlugin -->
    <type>org.exoplatform.container.definition.PortalContainerDefinitionChangePlugin</type>
    <init-params>
      <value-param>
        <name>apply.default</name>
        <value>true</value>
      </value-param>
      <object-param>
        <name>change</name>
        <object type="org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependencies">
          <!-- The list of name of the dependencies to add -->
          <field name="dependencies">
            <collection type="java.util.ArrayList">
              <value>
                <string>foo</string>
              </value>
            </collection>
          </field>
        </object>
      </object-param>     
    </init-params>
  </component-plugin>
</external-component-plugins>

This modification adds a list of dependencies before a given target dependency defined into the list of dependencies of the PortalContainerDefinition. The full qualified name is org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependenciesBefore.


See an example below, that will add foo before foo2 in the dependency list of the default portal container:

<external-component-plugins>
  <!-- The full qualified name of the PortalContainerConfig -->
  <target-component>org.exoplatform.container.definition.PortalContainerConfig</target-component>
  <component-plugin>
    <!-- The name of the plugin -->
    <name>Change PortalContainer Definitions</name>
    <!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions -->
    <set-method>registerChangePlugin</set-method>
    <!-- The full qualified name of the PortalContainerDefinitionChangePlugin -->
    <type>org.exoplatform.container.definition.PortalContainerDefinitionChangePlugin</type>
    <init-params>
      <value-param>
        <name>apply.default</name>
        <value>true</value>
      </value-param>
      <object-param>
        <name>change</name>
        <object type="org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependenciesBefore">
          <!-- The list of name of the dependencies to add -->
          <field name="dependencies">
            <collection type="java.util.ArrayList">
              <value>
                <string>foo</string>
              </value>
            </collection>
          </field>
          <!-- The name of the target dependency -->
          <field name="target">
            <string>foo2</string>
          </field>
        </object>
      </object-param>     
    </init-params>
  </component-plugin>
</external-component-plugins>

This modification adds a list of dependencies after a given target dependency defined into the list of dependencies of the PortalContainerDefinition. The full qualified name is org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependenciesAfter.


See an example below, that will add foo after foo2 in the dependency list of the default portal container:

<external-component-plugins>
  <!-- The full qualified name of the PortalContainerConfig -->
  <target-component>org.exoplatform.container.definition.PortalContainerConfig</target-component>
  <component-plugin>
    <!-- The name of the plugin -->
    <name>Change PortalContainer Definitions</name>
    <!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions -->
    <set-method>registerChangePlugin</set-method>
    <!-- The full qualified name of the PortalContainerDefinitionChangePlugin -->
    <type>org.exoplatform.container.definition.PortalContainerDefinitionChangePlugin</type>
    <init-params>
      <value-param>
        <name>apply.default</name>
        <value>true</value>
      </value-param>
      <object-param>
        <name>change</name>
        <object type="org.exoplatform.container.definition.PortalContainerDefinitionChange$AddDependenciesAfter">
          <!-- The list of name of the dependencies to add -->
          <field name="dependencies">
            <collection type="java.util.ArrayList">
              <value>
                <string>foo</string>
              </value>
            </collection>
          </field>
          <!-- The name of the target dependency -->
          <field name="target">
            <string>foo2</string>
          </field>
        </object>
      </object-param>     
    </init-params>
  </component-plugin>
</external-component-plugins>

This modification adds new settings to a PortalContainerDefinition. The full qualified name is org.exoplatform.container.definition.PortalContainerDefinitionChange$AddSettings.


See an example below, that will add the settings string and stringX to the settings of the default portal container:

<external-component-plugins>
  <!-- The full qualified name of the PortalContainerConfig -->
  <target-component>org.exoplatform.container.definition.PortalContainerConfig</target-component>
  <component-plugin>
    <!-- The name of the plugin -->
    <name>Change PortalContainer Definitions</name>
    <!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions -->
    <set-method>registerChangePlugin</set-method>
    <!-- The full qualified name of the PortalContainerDefinitionChangePlugin -->
    <type>org.exoplatform.container.definition.PortalContainerDefinitionChangePlugin</type>
    <init-params>
      <value-param>
        <name>apply.default</name>
        <value>true</value>
      </value-param>
      <object-param>
        <name>change</name>
        <object type="org.exoplatform.container.definition.PortalContainerDefinitionChange$AddSettings">
          <!-- The settings to add to the to the portal containers -->
          <field name="settings">
            <map type="java.util.HashMap">
              <entry>
                <key>
                  <string>string</string>
                </key>
                <value>
                  <string>value1</string>
                </value>
              </entry>
              <entry>
                <key>
                  <string>stringX</string>
                </key>
                <value>
                  <string>value1</string>
                </value>
              </entry>
            </map>
          </field>
        </object>
      </object-param>     
    </init-params>
  </component-plugin>
</external-component-plugins>

It is possible to use component-plugin elements in order to dynamically disable one or several portal containers. In the example below, we disable the portal container named foo:

<external-component-plugins>
  <!-- The full qualified name of the PortalContainerConfig -->
  <target-component>org.exoplatform.container.definition.PortalContainerConfig</target-component>
  <component-plugin>
    <!-- The name of the plugin -->
    <name>Disable a PortalContainer</name>
    <!-- The name of the method to call on the PortalContainerConfig in order to register the changes on the PortalContainerDefinitions -->
    <set-method>registerDisablePlugin</set-method>
    <!-- The full qualified name of the PortalContainerDefinitionDisablePlugin -->
    <type>org.exoplatform.container.definition.PortalContainerDefinitionDisablePlugin</type>
    <init-params>
      <!-- The list of the name of the portal containers to disable -->
      <values-param>
        <name>names</name>
        <value>foo</value>
      </values-param>
    </init-params>
  </component-plugin>
</external-component-plugins>

Note

All the value of the parameters marked with a (*) can be defined thanks to System properties like any values in configuration files but also thanks to variables loaded by the PropertyConfigurator. For example in GateIn by default, it would be all the variables defined in the file configuration.properties.

To prevent any accesses to a web application corresponding to PortalContainer that has been disabled, you need to make sure that the following Http Filter (or a sub class of it) has been added to your web.xml in first position as below:

<filter>
  <filter-name>PortalContainerFilter</filter-name>
  <filter-class>org.exoplatform.container.web.PortalContainerFilter</filter-class>
</filter>  

<filter-mapping>
  <filter-name>PortalContainerFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

Note

It is only possible to disable a portal container when at least one PortalContainerDefinition has been registered.

A new property configurator service has been developed for taking care of configuring system properties from the inline kernel configuration or from specified property files.

The services is scoped at the root container level because it is used by all the services in the different portal containers in the application runtime.

The kernel configuration is able to handle configuration profiles at runtime (as opposed to packaging time).

Profiles are configured in the configuration files of the eXo kernel.

A configuration element is profiles capable when it carries a profiles element.

The services are not responsible for the instantiation of the components on which they depend.

This architecture provides a loosely coupled design where the implementation of dependant services can be transparently exchanged.

This pattern has several names:

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 in the current container and if it cannot be found, 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 implementations in two sibling 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 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 are 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.

After deploying you find the configuration.xml file in webapps/portal/WEB-INF/conf Use component registration tags. Let's look at 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.

<!-- 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 classes 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 an other 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 refer the organization service and we 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 are several types of init parameters, from a simple value param which binds a key with a value to a more complex object mapping that fills 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 a NodeType from your own configuration.xml file.

When the RootContainer is starting the configuration retrieval 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. These configurations are added to a set. If a component was configured in a previous jar and the current jar contains a new configuration of that component the latest (from the current jar) will replace the previous configuration.

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

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

But, there are components that can be (or should be) configured more than one time. This depends on the business logic of the component. A component may initialize the same resource (shared with other players) or may add a particular object to a set of objects (shared with other players too). In the first case it's critical who will be the last, i.e. whose configuration will be used. In second case it doesn't matter who is the first and who is the last (if the 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 
   ......

Since kernel version 2.0.6 it is possible to setup order of loading for ComponentPlugin. Use the ' priority' tag to define plugin's load priority. By default all plugins get priority '0'; they will be loaded in the container's natural way. If you want one plugin to be loaded later than the others then just set priority for it higher than zero.

Simple example of fragment of a configuration.xml.

...
<component>
  <type>org.exoplatform.services.Component1</type>
</component>

<external-component-plugins>
  <target-component>org.exoplatform.services.Component1</target-component>

  <component-plugin>
    <name>Plugin1</name>
    <set-method>addPlugin</set-method>
    <type>org.exoplatform.services.plugins.Plugin1</type>
    <description>description</description>
    <priority>1</priority>
  </component-plugin>

  <component-plugin>
    <name>Plugin2</name>
    <set-method>addPlugin</set-method>
    <type>org.exoplatform.services.plugins.Plugin2</type>
    <description>description</description>
    <priority>2</priority>
  </component-plugin>

</external-component-plugins>

<external-component-plugins>
  <target-component>org.exoplatform.services.Component1</target-component>
  <component-plugin>
    <name>Plugin3</name>
    <set-method>addPlugin</set-method>
    <type>org.exoplatform.services.plugins.Plugin3</type>
    <description>description</description>
  </component-plugin>
</external-component-plugins>
...

In the above example plugin 'Plugin3' will be loaded first because it has the default priority '0'. Then, plugin 'Plugin1' will be loaded and last one is plugin 'Plugin2'.

This section will first describe how the ListenerService works and then it will show you how to configure the ListenerService.

Related documents

Listeners must be subclasses of org.exoplatform.services.listener.Listener registered by the ListenerService.

To trigger an event, an application can call one of the broadcast() methods of ListenerService.

/**
 * This method is used to broadcast an event. This method should: 1. Check if
 * there is a list of listener that listen to the event name. 2. If there is a
 * list of listener, create the event object with the given name , source and
 * data 3. For each listener in the listener list, invoke the method
 * onEvent(Event)
 * 
 * @param <S> The type of the source that broadcast the event
 * @param <D> The type of the data that the source object is working on
 * @param name The name of the event
 * @param source The source object instance
 * @param data The data object instance
 * @throws Exception 
 */
public <S, D> void broadcast(String name, S source, D data) throws Exception {
   ...
}

/**
 * This method is used when a developer want to implement his own event object
 * and broadcast the event. The method should: 1. Check if there is a list of
 * listener that listen to the event name. 2. If there is a list of the
 * listener, For each listener in the listener list, invoke the method
 * onEvent(Event)
 * 
 * @param <T> The type of the event object, the type of the event object has
 *          to be extended from the Event type
 * @param event The event instance
 * @throws Exception
 */
public <T extends Event> void broadcast(T event) throws Exception {
   ...
}

The boadcast() methods retrieve the name of the event and find the registered listeners with the same name and call the method onEvent() on each listener found.

Each listener is a class that extends org.exoplatform.services.listener.Listener, as you can see below:

public abstract class Listener<S, D> extends BaseComponentPlugin {

   /**
    * This method should be invoked when an event with the same name is
    * broadcasted
    */
   public abstract void onEvent(Event<S, D> event) throws Exception;
}

Each listener is also a ComponentPlugin with a name and a description, in other words, the name of the listener will be the name given in the configuration file, for more details see the next section.

public interface ComponentPlugin {
   public String getName();

   public void setName(String name);

   public String getDescription();

   public void setDescription(String description);
}

The org.exoplatform.services.security.ConversationRegistry uses the ListenerService to notify that a user has just signed in or just left the application. For example, when a new user signs in, the following code is called:

listenerService.broadcast("exo.core.security.ConversationRegistry.register", this, state);

This code will in fact create a new Event which name is "exo.core.security.ConversationRegistry.register", which source is the current instance of ConversationRegistry and which data is the given state. The ListenerService will call the method onEvent(Event<ConversationRegistry, ConversationState> event) on all the listeners which name is "exo.core.security.ConversationRegistry.register".

In the example below, we define a Listener that will listen the event "exo.core.security.ConversationRegistry.register".

<?xml version="1.0" encoding="ISO-8859-1"?>
<configuration>
...
  <external-component-plugins>
    <!-- The full qualified name of the ListenerService --> 
    <target-component>org.exoplatform.services.listener.ListenerService</target-component>

    <component-plugin>
      <!-- The name of the listener that is also the name of the target event -->
      <name>exo.core.security.ConversationRegistry.register</name>
      <!-- The name of the method to call on the ListenerService in order to register the Listener -->
      <set-method>addListener</set-method>
      <!-- The full qualified name of the Listener -->
      <type>org.exoplatform.forum.service.AuthenticationLoginListener</type>
    </component-plugin>

  </external-component-plugins>
</configuration>
...

Initial Context Binder is responsible for binding references at runtime, persisting in file and automatically rebinding. Java temp directory is used to persist references in bind-references.xml file by default. In case when need to definde special file it can be done by add parameter to InitialContextInitializer configuration.

Job scheduler defines a job to execute a given number of times during a given period. It is a service that is in charge of unattended background executions, commonly known for historical reasons as batch processing. It is used to create and run jobs automatically and continuously, to schedule event-driven jobs and reports.

Jobs are scheduled to run when a given Trigger occurs. Triggers can be created with nearly any combination of the following directives:

Jobs are given names by their creator and can also be organized into named groups. Triggers may also be given names and placed into groups, in order to easily organize them within the scheduler. Jobs can be added to the scheduler once, but registered with multiple Triggers. Within a J2EE environment, Jobs can perform their work as part of a distributed (XA) transaction.

(Source: quartz-scheduler.org)

Kernel leverages Quartz for its scheduler service and wraps org.quartz.Scheduler in org.exoplatform.services.scheduler.impl.QuartzSheduler for easier service wiring and configuration like any other services. To work with Quartz in Kernel, you will mostly work with org.exoplatform.services.scheduler.JobSchedulerService (implemented by org.exoplatform.services.scheduler.impl.JobSchedulerServiceImpl.

To use JobSchedulerService, you can configure it as a component in the configuration.xml. Because JobSchedulerService requires QuartzSheduler and QueueTasks, you also have to configure these two components.

<?xml version="1.0" encoding="UTF-8"?>
<configuration
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd http://www.exoplatform.org/xml/ns/kernel_1_2.xsd"
  xmlns="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd">

  <component>
    <type>org.exoplatform.services.scheduler.impl.QuartzSheduler</type>
  </component>

  <component>
    <type>org.exoplatform.services.scheduler.QueueTasks</type>
  </component>

  <component>
    <key>org.exoplatform.services.scheduler.JobSchedulerService</key>
    <type>org.exoplatform.services.scheduler.impl.JobSchedulerServiceImpl</type>
  </component>

</configuration>

Work with JobSchedulerService by creating a sample project and use GateIn-3.1.0-GA for testing.

Firstly, create a project by using maven archetype plugin:

mvn archetype:generate
  • For project type: select maven-archetype-quickstart

  • For groupId: select org.exoplatform.samples

  • For artifactId: select exo.samples.scheduler

  • For version: select 1.0.0-SNAPSHOT

  • For package: select org.exoplatform.samples.scheduler

Edit the pom.xml as follows:

<project
  xmlns="http://maven.apache.org/POM/4.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

  <modelVersion>4.0.0</modelVersion>

  <parent>
    <artifactId>exo.portal.parent</artifactId>
    <groupId>org.exoplatform.portal</groupId>
    <version>3.1.0-GA</version>
  </parent>

  <groupId>org.exoplatform.samples</groupId>
  <artifactId>exo.samples.scheduler</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <name>eXo Samples For Scheduler</name>
  <description>eXo Samples Code For Scheduler</description>
</project>    

Generate an eclipse project by using maven eclipse plugin and then import into eclipse:

mvn eclipse:eclipse

eXo Kernel makes it easier to work with job scheduler service. All you need is just to define your "job" class to be performed by implementing org.quartz.Job interface and add configuration for it.

After defining the "job", the only next step is to configure it by using external-component-plugin configuration for org.exoplatform.services.scheduler.JobSchedulerService. You can use these methods below for setting component plugin:

public void addPeriodJob(ComponentPlugin plugin) throws Exception;

The component plugin for this method must be the type of org.exoplatform.services.scheduler.PeriodJob. This type of job is used to perform actions that are executed in a period of time. You have to define when this job is performed, when it ends, when it performs the first action, how many times it is executed and the period of time to perform the action. See the configuration sample below to understand more clearly:

<external-component-plugins>
   <target-component>org.exoplatform.services.scheduler.JobSchedulerService</target-component>
    <component-plugin>
      <name>PeriodJob Plugin</name>
      <set-method>addPeriodJob</set-method>
      <type>org.exoplatform.services.scheduler.PeriodJob</type>
      <description>period job configuration</description>
      <init-params>
        <properties-param>
          <name>job.info</name>
          <description>dumb job executed  periodically</description>
          <property name="jobName" value="DumbJob"/>
          <property name="groupName" value="DumbJobGroup"/>
          <property name="job" value="org.exoplatform.samples.scheduler.jobs.DumbJob"/>
          <property name="repeatCount" value="0"/>
          <property name="period" value="60000"/>
          <property name="startTime" value="+45"/>
          <property name="endTime" value=""/>
        </properties-param>
      </init-params>
    </component-plugin>
 </external-component-plugins>
public void addCronJob(ComponentPlugin plugin) throws Exception;

The component plugin for this method must be the type of org.exoplatform.services.scheduler.CronJob. This type of job is used to perform actions at specified time with Unix 'cron-like' definitions. The plugin uses "expression" field for specifying the 'cron-like' definitions to execute the job. This is considered as the most powerful and flexible job to define when it will execute. For example, at 12pm every day => "0 0 12 * * ?"; or at 10:15am every Monday, Tuesday, Wednesday, Thursday and Friday => "0 15 10 ? * MON-FRI". To see more about Cron expression, please refer to this article:

CRON expression.

See the configuration sample below to understand more clearly:

<external-component-plugins>
    <target-component>org.exoplatform.services.scheduler.JobSchedulerService</target-component>
    <component-plugin>
      <name>CronJob Plugin</name>
      <set-method>addCronJob</set-method>
      <type>org.exoplatform.services.scheduler.CronJob</type>
      <description>cron job configuration</description>
      <init-params>
        <properties-param>
          <name>cronjob.info</name>
          <description>dumb job executed by cron expression</description>
          <property name="jobName" value="DumbJob"/>
          <property name="groupName" value="DumbJobGroup"/>
          <property name="job" value="org.exoplatform.samples.scheduler.jobs.DumbJob"/>
          <!-- The job will be performed at 10:15am every day -->
          <property name="expression" value="0 15 10 * * ?"/> 
        </properties-param>
      </init-params>
    </component-plugin>
  </external-component-plugins>
public void addGlobalJobListener(ComponentPlugin plugin) throws Exception;
public void addJobListener(ComponentPlugin plugin) throws Exception;

The component plugin for two methods above must be the type of org.quartz.JobListener. This job listener is used so that it will be informed when a org.quartz.JobDetail executes.

public void addGlobalTriggerListener(ComponentPlugin plugin) throws Exception;
public void addTriggerListener(ComponentPlugin plugin) throws Exception;

The component plugin for two methods above must be the type of org.quartz.TriggerListener. This trigger listener is used so that it will be informed when a org.quartz.Trigger fires.

Create conf.portal package in your sample project. Add the configuration.xml file with the content as follows:

<?xml version="1.0" encoding="UTF-8"?>
<configuration
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd http://www.exoplatform.org/xml/ns/kernel_1_2.xsd"
  xmlns="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd">

  <component>
    <type>org.exoplatform.services.scheduler.impl.QuartzSheduler</type>
  </component>
  <component>
    <type>org.exoplatform.services.scheduler.QueueTasks</type>
  </component>
  <component>
    <key>org.exoplatform.services.scheduler.JobSchedulerService</key>
    <type>org.exoplatform.services.scheduler.impl.JobSchedulerServiceImpl</type>
  </component>

  <external-component-plugins>
    <target-component>org.exoplatform.services.scheduler.JobSchedulerService</target-component>
    <component-plugin>
      <name>PeriodJob Plugin</name>
      <set-method>addPeriodJob</set-method>
      <type>org.exoplatform.services.scheduler.PeriodJob</type>
      <description>period job configuration</description>
      <init-params>
        <properties-param>
          <name>job.info</name>
          <description>dumb job executed periodically</description>
          <property name="jobName" value="DumbJob"/>
          <property name="groupName" value="DumbJobGroup"/>
          <property name="job" value="org.exoplatform.samples.scheduler.jobs.DumbJob"/>
          <property name="repeatCount" value="0"/>
          <property name="period" value="60000"/>
          <property name="startTime" value="+45"/>
          <property name="endTime" value=""/>
        </properties-param>
      </init-params>
    </component-plugin>
  </external-component-plugins>
</configuration>

mvn clean install the project. Copy .jar file to lib in tomcat bundled with GateIn-3.1.0-GA. Run bin/gatein.sh to see the DumbJob to be executed on the terminal when portal containers are initialized. Please look at the terminal to see the log message of DumbJob.

From now on, you can easily create any job to be executed in GateIn's portal by defining your job and configuring it.

This section will provide you all the basic knowledge about eXo Cache, from basic concepts to advanced concepts, sample codes, and more.

All applications on the top of eXo JCR that need a cache, can rely on an org.exoplatform.services.cache.ExoCache instance that is managed by the org.exoplatform.services.cache.CacheService. The main implementation of this service is org.exoplatform.services.cache.impl.CacheServiceImpl which depends on the org.exoplatform.services.cache.ExoCacheConfig in order to create new ExoCache instances. See the below example of org.exoplatform.services.cache.CacheService definition:

  <component>
    <key>org.exoplatform.services.cache.CacheService</key>
    <jmx-name>cache:type=CacheService</jmx-name>
    <type>org.exoplatform.services.cache.impl.CacheServiceImpl</type>
    <init-params>
      <object-param>
        <name>cache.config.default</name>
        <description>The default cache configuration</description>
        <object type="org.exoplatform.services.cache.ExoCacheConfig">
          <field name="name"><string>default</string></field>
          <field name="maxSize"><int>300</int></field>
          <field name="liveTime"><long>600</long></field>
          <field name="distributed"><boolean>false</boolean></field>
          <field name="implementation"><string>org.exoplatform.services.cache.concurrent.ConcurrentFIFOExoCache</string></field> 
        </object>
      </object-param>
    </init-params>
  </component>

See the below example about how to define a new ExoCacheConfig thanks to a external-component-plugin:

  <external-component-plugins>
    <target-component>org.exoplatform.services.cache.CacheService</target-component>
    <component-plugin>
      <name>addExoCacheConfig</name>
      <set-method>addExoCacheConfig</set-method>
      <type>org.exoplatform.services.cache.ExoCacheConfigPlugin</type>
      <description>Configures the cache for query service</description>
      <init-params>
        <object-param>
          <name>cache.config.wcm.composer</name>
          <description>The default cache configuration</description>
          <object type="org.exoplatform.services.cache.ExoCacheConfig">
            <field name="name"><string>wcm.composer</string></field>
            <field name="maxSize"><int>300</int></field>
            <field name="liveTime"><long>600</long></field>
            <field name="distributed"><boolean>false</boolean></field>
            <field name="implementation"><string>org.exoplatform.services.cache.concurrent.ConcurrentFIFOExoCache</string></field> 
          </object>
        </object-param>
      </init-params>
    </component-plugin>
  </external-component-plugins>

In case, you have big values or non serializable values and you need a replicated cache to at list invalidate the data when it is needed, you can use the invalidation mode that will work on top of any replicated cache implementations. This is possible thanks to the class InvalidationExoCache which is actually a decorator whose idea is to replicate the the hash code of the value in order to know if it is needed or not to invalidate the local data, if the new hash code of the value is the same as the old value, we assume that it is the same value so we don't invalidate the old value. This is required to avoid the following infinite loop that we will face with invalidation mode proposed out of the box by JBoss Cache for example:

In the use case above, thanks to the InvalidationExoCache since the value loaded at step #3 has the same hash code as the value loaded as step #1, the step #4 won't invalidate the data on the cluster node #1.

It exists 2 ways to use the invalidation mode which are the following:

In the previous versions of eXo kernel, it was quite complex to implement your own ExoCache because it was not open enough. Since kernel 2.0.8, it is possible to easily integrate your favorite cache provider in eXo Products.

You just need to implement your own ExoCacheFactory and register it in an eXo container, as described below:

package org.exoplatform.services.cache;
...
public interface ExoCacheFactory {
  
  /**
   * Creates a new instance of {@link org.exoplatform.services.cache.ExoCache}
   * @param config the cache to create
   * @return the new instance of {@link org.exoplatform.services.cache.ExoCache}
   * @exception ExoCacheInitException if an exception happens while initializing the cache
   */
  public ExoCache createCache(ExoCacheConfig config) throws ExoCacheInitException;  
}

As you can see, there is only one method to implement which can be seen as a converter of an ExoCacheConfig to get an instance of ExoCache. Once, you created your own implementation, you can simply register your factory by adding a file conf/portal/configuration.xml with a content of the following type:

<configuration>
  <component>
    <key>org.exoplatform.services.cache.ExoCacheFactory</key>
    <type>org.exoplatform.tutorial.MyExoCacheFactoryImpl</type>
    ...
  </component>   
</configuration>

When you add, the eXo library in your classpath, the eXo service container will use the default configuration provided in the library itself but of course you can still redefined the configuration if you wish as you can do with any components.

The default configuration of the factory is:

<configuration>  
  <component>
    <key>org.exoplatform.services.cache.ExoCacheFactory</key>
    <type>org.exoplatform.services.cache.impl.jboss.ExoCacheFactoryImpl</type>
    <init-params>
      <value-param>
        <name>cache.config.template</name>
        <value>jar:/conf/portal/cache-configuration-template.xml</value>
      </value-param>
      <value-param>
        <name>allow.shareable.cache</name>
        <value>true</value>
      </value-param>
    </init-params>
  </component>   
</configuration>

The factory for jboss cache, delegates the cache creation to ExoCacheCreator that is defined as below:

package org.exoplatform.services.cache.impl.jboss;
...
public interface ExoCacheCreator {

  /**
   * Creates an eXo cache according to the given configuration {@link org.exoplatform.services.cache.ExoCacheConfig}
   * @param config the configuration of the cache to apply
   * @param cache the cache to initialize
   * @exception ExoCacheInitException if an exception happens while initializing the cache
   */
  public ExoCache create(ExoCacheConfig config, Cache<Serializable, Object> cache) throws ExoCacheInitException;
  
  /**
   * Returns the type of {@link org.exoplatform.services.cache.ExoCacheConfig} expected by the creator  
   * @return the expected type
   */
  public Class<? extends ExoCacheConfig> getExpectedConfigType();
  
  /**
   * Returns the name of the implementation expected by the creator. This is mainly used to be backward compatible
   * @return the expected by the creator
   */
  public String getExpectedImplementation();
}

The ExoCacheCreator allows you to define any kind of jboss cache instance that you would like to have. It has been designed to give you the ability to have your own type of configuration and to always be backward compatible.

In an ExoCacheCreator, you need to implement 3 methods which are:

By default, no cache creator are defined, so you need to define them yourself by adding them in your configuration files.

You have 2 ways to define a cache which are:

...
       <object-param>
        <name>lru</name>
        <description>The lru cache configuration</description>
        <object type="org.exoplatform.services.cache.impl.jboss.lru.LRUExoCacheConfig">
          <field name="name"><string>lru</string></field>
          <field name="maxNodes"><int>${my-value}</int></field>
          <field name="minTimeToLive"><long>${my-value}</long></field>
          <field name="maxAge"><long>${my-value}</long></field>
          <field name="timeToLive"><long>${my-value}</long></field>
        </object>
      </object-param> 
...

  • Old configuration

...
      <object-param>
        <name>lru-with-old-config</name>
        <description>The lru cache configuration</description>
        <object type="org.exoplatform.services.cache.ExoCacheConfig">
          <field name="name"><string>lru-with-old-config</string></field>
          <field name="maxSize"><int>${my-value}</int></field>
          <field name="liveTime"><long>${my-value}</long></field>
          <field name="implementation"><string>LRU</string></field>
        </object>
      </object-param> 
...

Note

For the fields maxAge and timeToLive needed by JBoss cache, we will use the default values provided by the creator.

...
       <object-param>
        <name>fifo</name>
        <description>The fifo cache configuration</description>
        <object type="org.exoplatform.services.cache.impl.jboss.fifo.FIFOExoCacheConfig">
          <field name="name"><string>fifo</string></field>
          <field name="maxNodes"><int>${my-value}</int></field>
          <field name="minTimeToLive"><long>${my-value}</long></field>
        </object>
      </object-param>
...

  • Old configuration

...
       <object-param>
        <name>fifo-with-old-config</name>
        <description>The fifo cache configuration</description>
        <object type="org.exoplatform.services.cache.ExoCacheConfig">
          <field name="name"><string>fifo-with-old-config</string></field>
          <field name="maxSize"><int>${my-value}</int></field>
          <field name="liveTime"><long>${my-value}</long></field>
          <field name="implementation"><string>FIFO</string></field>
        </object>
      </object-param>
...

...
       <object-param>
        <name>mru</name>
        <description>The mru cache configuration</description>
        <object type="org.exoplatform.services.cache.impl.jboss.mru.MRUExoCacheConfig">
          <field name="name"><string>mru</string></field>
          <field name="maxNodes"><int>${my-value}</int></field>
          <field name="minTimeToLive"><long>${my-value}</long></field>
        </object>
      </object-param> 
...

  • Old configuration

...
      <object-param>
        <name>mru-with-old-config</name>
        <description>The mru cache configuration</description>
        <object type="org.exoplatform.services.cache.ExoCacheConfig">
          <field name="name"><string>mru-with-old-config</string></field>
          <field name="maxSize"><int>${my-value}</int></field>
          <field name="liveTime"><long>${my-value}</long></field>
          <field name="implementation"><string>MRU</string></field>
        </object>
      </object-param> 
...

...
       <object-param>
        <name>lfu</name>
        <description>The lfu cache configuration</description>
        <object type="org.exoplatform.services.cache.impl.jboss.lfu.LFUExoCacheConfig">
          <field name="name"><string>lfu</string></field>
          <field name="maxNodes"><int>${my-value}</int></field>
          <field name="minNodes"><int>${my-value}</int></field>
          <field name="minTimeToLive"><long>${my-value}</long></field>
        </object>
      </object-param> 
...

  • Old configuration

...
      <object-param>
        <name>lfu-with-old-config</name>
        <description>The lfu cache configuration</description>
        <object type="org.exoplatform.services.cache.ExoCacheConfig">
          <field name="name"><string>lfu-with-old-config</string></field>
          <field name="maxSize"><int>${my-value}</int></field>
          <field name="liveTime"><long>${my-value}</long></field>
          <field name="implementation"><string>LFU</string></field>
        </object>
      </object-param> 
...

Note

For the fields minNodes and timeToLive needed by JBoss cache, we will use the default values provided by the creator.

...
       <object-param>
        <name>ea</name>
        <description>The ea cache configuration</description>
        <object type="org.exoplatform.services.cache.impl.jboss.ea.EAExoCacheConfig">
          <field name="name"><string>ea</string></field>
          <field name="maxNodes"><int>${my-value}</int></field>
          <field name="minTimeToLive"><long>${my-value}</long></field>
          <field name="expirationTimeout"><long>${my-value}</long></field>
        </object>
      </object-param> 
...

  • Old configuration

...
      <object-param>
        <name>ea-with-old-config</name>
        <description>The ea cache configuration</description>
        <object type="org.exoplatform.services.cache.ExoCacheConfig">
          <field name="name"><string>lfu-with-old-config</string></field>
          <field name="maxSize"><int>${my-value}</int></field>
          <field name="liveTime"><long>${my-value}</long></field>
          <field name="implementation"><string>EA</string></field>
        </object>
      </object-param> 
...

Note

For the fields expirationTimeout needed by JBoss cache, we will use the default values provided by the creator.

The factory for infinispan, delegates the cache creation to ExoCacheCreator that is defined as below:

package org.exoplatform.services.cache.impl.infinispan;
...
public interface ExoCacheCreator {

   /**
    * Creates an eXo cache according to the given configuration {@link org.exoplatform.services.cache.ExoCacheConfig}
    * @param config the configuration of the cache to apply
    * @param confBuilder the configuration builder of the infinispan cache
    * @param cacheGetter a {@link Callable} instance from which we can get the cache
    * @exception ExoCacheInitException if an exception happens while initializing the cache
    */
   public ExoCache<Serializable, Object> create(ExoCacheConfig config, ConfigurationBuilder confBuilder, 
            Callable<Cache<Serializable, Object>> cacheGetter) throws ExoCacheInitException;

   /**
    * Returns the type of {@link org.exoplatform.services.cache.ExoCacheConfig} expected by the creator  
    * @return the expected type
    */
   public Class<? extends ExoCacheConfig> getExpectedConfigType();

   /**
    * Returns a set of all the implementations expected by the creator. This is mainly used to be backward compatible
    * @return the expected by the creator
    */
   public Set<String> getExpectedImplementations();
}

The ExoCacheCreator allows you to define any kind of infinispan cache instance that you would like to have. It has been designed to give you the ability to have your own type of configuration and to always be backward compatible.

In an ExoCacheCreator, you need to implement 3 methods which are:

By default, no cache creator are defined, so you need to define them yourself by adding them in your configuration files.

This is the generic cache creator that allows you to use any eviction strategies defined by default in Infinispan.

..
<object-param>
  <name>GENERIC</name>
  <description>The generic cache creator</description>
  <object type="org.exoplatform.services.cache.impl.infinispan.generic.GenericExoCacheCreator">
    <field name="implementations">
      <collection type="java.util.HashSet">
         <value>
            <string>NONE</string>
         </value>
         <value>
            <string>FIFO</string>
         </value>
         <value>
            <string>LRU</string>
         </value>
         <value>
            <string>UNORDERED</string>
         </value>
         <value>
            <string>LIRS</string>
         </value>
      </collection>        
    </field>
    <field name="defaultStrategy"><string>${my-value}</string></field>
    <field name="defaultMaxIdle"><long>${my-value}</long></field>
    <field name="defaultWakeUpInterval"><long>${my-value}</long></field>
  </object>
</object-param>
...

All the eviction strategies proposed by default in infinispan rely on the generic cache creator.

...
       <object-param>
        <name>myCache</name>
        <description>My cache configuration</description>
        <object type="org.exoplatform.services.cache.impl.infinispan.generic.GenericExoCacheConfig">
          <field name="name"><string>myCacheName</string></field>
          <field name="strategy"><int>${my-value}</int></field>
          <field name="maxEntries"><long>${my-value}</long></field>
          <field name="lifespan"><long>${my-value}</long></field>
          <field name="maxIdle"><long>${my-value}</long></field>
          <field name="wakeUpInterval"><long>${my-value}</long></field>
        </object>
      </object-param> 
...

  • Old configuration

...
      <object-param>
        <name>myCache</name>
        <description>My cache configuration</description>
          <field name="name"><string>lru-with-old-config</string></field>
          <field name="maxSize"><int>${my-value}</int></field>
          <field name="liveTime"><long>${my-value}</long></field>
          <field name="implementation"><string>${my-value}</string></field>
        </object>
      </object-param> 
...

Note

For the fields maxIdle and wakeUpInterval needed by infinispan, we will use the default values provided by the creator.

In order to be able to use infinispan in distributed mode with the ability to launch external JVM instances that will manage a part of the cache, we need to configure the DistributedCacheManager. In the next sections, we will show how to configure the component and how to launch external JVM instances.

The DistributedCacheManager is the component that will manage all the cache instances that we expect to be distributed, it must be unique in the whole JVM which means that it must be declared at RootContainer level in portal mode or at StandaloneContainer in standalone mode. See below an example of configuration.

<component>
  <type>org.exoplatform.services.ispn.DistributedCacheManager</type>
  <init-params>
    <value-param>
      <name>infinispan-configuration</name>
      <value>jar:/conf/distributed-cache-configuration.xml</value>
    </value-param>
    <properties-param>
      <name>parameters</name>
      <description>The parameters of the configuration</description>
      <property name="configurationFile" value="${gatein.jcr.jgroups.config}"></property>
      <property name="invalidationThreshold" value="0"></property>
      <property name="numOwners" value="3"></property>
      <property name="numVirtualNodes" value="2"></property>
    </properties-param>     
  </init-params>
</component>

As described above, the configuration of infinispan must defined explicitly each cache using the nameCache block no dynamic configuration of cache is supported. Indeed to ensure that the whole cluster is consistent in term of defined cache, it is required to configure all the cache that you will need and register it using its future name.

For now, we have 2 supported cache name which are JCRCache and eXoCache. JCRCache is the name of the cache that we use in case we would like to store the data of the JCR into a distributed cache. eXoCache is the name of the cache that we use in case we would like to store the data of some eXo Cache instances into a distributed cache.

See below an example of infinispan configuration with both eXoCache and JCRCache defined:

<infinispan xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:infinispan:config:5.1 http://www.infinispan.org/schemas/infinispan-config-5.1.xsd"
  xmlns="urn:infinispan:config:5.1">
   <global>
      <globalJmxStatistics jmxDomain="exo" enabled="true" allowDuplicateDomains="true"/>
      <transport transportClass="org.infinispan.remoting.transport.jgroups.JGroupsTransport" clusterName="JCR-cluster" distributedSyncTimeout="20000">
        <properties>
          <property name="configurationFile" value="${configurationFile}"/>
        </properties>
      </transport>
      <shutdown hookBehavior="DEFAULT"/>
   </global>
   <namedCache name="JCRCache">
      <locking isolationLevel="READ_COMMITTED" lockAcquisitionTimeout="120000" writeSkewCheck="false" concurrencyLevel="500" useLockStriping="true" />
      <transaction transactionManagerLookupClass="org.infinispan.transaction.lookup.GenericTransactionManagerLookup" syncRollbackPhase="true" syncCommitPhase="true" eagerLockSingleNode="true" transactionMode="TRANSACTIONAL"/>
      <jmxStatistics enabled="true"/>
      <clustering mode="distribution">
        <l1 enabled="true" invalidationThreshold="${invalidationThreshold}"/>
         <hash numOwners="${numOwners}" numVirtualNodes="${numVirtualNodes}" rehashRpcTimeout="120000">
           <groups enabled="true"/>
         </hash>
         <sync replTimeout="180000"/>
      </clustering>
   </namedCache>
   <namedCache name="eXoCache">
      <locking isolationLevel="READ_COMMITTED" lockAcquisitionTimeout="120000" writeSkewCheck="false" concurrencyLevel="500" useLockStriping="true" />
      <transaction transactionManagerLookupClass="org.infinispan.transaction.lookup.GenericTransactionManagerLookup" syncRollbackPhase="true" syncCommitPhase="true" eagerLockSingleNode="true" transactionMode="TRANSACTIONAL"/>
      <jmxStatistics enabled="true"/>
      <clustering mode="distribution">
         <l1 enabled="true" invalidationThreshold="${invalidationThreshold}"/>
         <hash numOwners="${numOwners}" numVirtualNodes="${numVirtualNodes}" rehashRpcTimeout="120000"/>
         <sync replTimeout="180000"/>
      </clustering>
   </namedCache>
</infinispan>

In case you intend to use the distribued mode, you can launch external JVM in standalone mode to provide more memory to your current cache. To do so, you will need to get the file of type exo.jcr.component.core.impl.infinispan.v5-binary.zip in which you will find scripts to launch your cache servers. These scripts allow optional arguments that are described below:

help|?|<configuration-file-path>|udp|tcp <initial-hosts>


Note

If you intend to use the CacheServer in order to manage some of your eXo Cache instances, don't forget to add the jar files that define both the keys and the values in the lib directory of the CacheServer distribution and restarts your CacheServer instances otherwise the unmarshalling will fail with java.lang.ClassNotFoundException.

TransactionServices provides access to the TransactionManager and the UserTransaction (See JTA specification for details).


eXo JCR proposes out of the box several implementations, they all implement the abstract class org.exoplatform.services.transaction.impl.AbstractTransactionService. This main class implement the biggest part of all the methods proposed by the TransactionService. For each sub-class of AbstractTransactionService, you can set the transaction timeout by configuration using the value parameter timeout that is expressed in seconds.

The DataSourceProvider is a service used to give access to a data source in an uniform manner in order to be able to support data sources that are managed by the application server.


The configuration of the DataSourceProvider should be defined only if you use managed data sources since by default all the data sources are considered as not managed. See below the default configuration

<configuration>
....  
   <component>
      <key>org.exoplatform.services.jdbc.DataSourceProvider</key>
      <type>org.exoplatform.services.jdbc.impl.DataSourceProviderImpl</type>
      <init-params>
         <!--  Indicates that the data source needs to check if a tx is active
              to decide if the provided connection needs to be managed or not.
              If it is set to false, the data source will provide only
              managed connections if the data source itself is managed.  -->
         <!--value-param>
            <name>check-tx-active</name>
            <value>true</value>
         </value-param-->
         <!-- Indicates that all the data sources are managed 
              If set to true the parameter never-managed and 
              managed-data-sources will be ignored -->
         <!--value-param>
            <name>always-managed</name>
            <value>true</value>
         </value-param-->
         <!-- Indicates the list of all the data sources that are 
              managed, each value tag can contain a list of
              data source names separated by a comma, in the
              example below we will register ds-foo1, ds-foo2 
              and ds-foo3 as managed data source. If always-managed
              and/or never-managed is set true this parameter is ignored -->
         <!--values-param>
            <name>managed-data-sources</name>
            <value>ds-foo1, ds-foo2</value>
            <value>ds-foo3</value>
         </values-param-->
      </init-params>
   </component>  
...
</configuration>

This section provides you the basic knowledge about JNDI naming, such as, what it is, how it works and how it is used.

Make sure you understand the Java Naming and Directory InterfaceTM (JNDI) concepts before using this service.

The InitialContextInitializer configuration example:

  <component>
    <type>org.exoplatform.services.naming.InitialContextInitializer</type>
    <init-params>
      <value-param>.
        <name>bindings-store-path</name>.
        <value>bind-references.xml</value>.
      </value-param>.
      <value-param> 
        <name>overload-context-factory</name> 
        <value>true</value> 
      </value-param>
      <properties-param>
        <name>default-properties</name>
        <description>Default initial context properties</description>
        <property name="java.naming.factory.initial" value="org.exoplatform.services.naming.SimpleContextFactory"/>
      </properties-param>
      <properties-param>
        <name>mandatory-properties</name>
        <description>Mandatory initial context properties</description>
        <property name="java.naming.provider.url" value="rmi://localhost:9999"/>
      </properties-param>
    </init-params>
  </component>

where

binding-store-path is file path which stores binded datasources in runtime

overload-context-factory allows to overload the default initial context factory by a context factory that is ExoContainer aware and that is able to delegate to the original initial context factory if it detects that it is not in the eXo scope. By default the feature is disabled since it is only required on AS that don't share the objects by default like tomcat but unlike JBoss AS

The BindReferencePlugin component plugin configuration example (for JDBC datasource):

  <component-plugins> 
    <component-plugin> 
      <name>bind.datasource</name>
      <set-method>addPlugin</set-method>
      <type>org.exoplatform.services.naming.BindReferencePlugin</type>
      <init-params>
        <value-param>
          <name>bind-name</name>
          <value>jdbcjcr</value>
        </value-param>
        <value-param>
          <name>class-name</name>
          <value>javax.sql.DataSource</value>
        </value-param>  
        <value-param>
          <name>factory</name>
          <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
        </value-param>
        <properties-param>
          <name>ref-addresses</name>
          <description>ref-addresses</description>
          <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
          <property name="url" value="jdbc:hsqldb:file:target/temp/data/portal"/>
          <property name="username" value="sa"/>
          <property name="password" value=""/>
        </properties-param>     
      </init-params>    
  </component-plugin>

In order to accommodate to the different target runtime where it can be deployed, eXo is capable of leveraging several logging systems. eXo lets you choose the underlying logging engine to use and even configure that engine (as a quick alternative to doing it directly in your runtime environment).

The currently supported logging engines are:

Log4J is a very popular and flexible logging system. It is a good option for JBoss.

  <component>
    <type>org.exoplatform.services.log.LogConfigurationInitializer</type>
    <init-params>
      <value-param>
        <name>configurator</name>
        <value>org.exoplatform.services.log.impl.Log4JConfigurator</value>
      </value-param>
      <properties-param>
        <name>properties</name>
        <description>Log4J properties</description>
        <property name="log4j.rootLogger" value="DEBUG, stdout, file"/>
        <property name="log4j.appender.stdout" value="org.apache.log4j.ConsoleAppender"/>
        <property name="log4j.appender.stdout.layout" value="org.apache.log4j.PatternLayout"/>
        <property name="log4j.appender.stdout.layout.ConversionPattern" value="%d {dd.MM.yyyy HH:mm:ss} %c {1}: %m (%F, line %L) %n"/>
        <property name="log4j.appender.file" value="org.apache.log4j.FileAppender"/>
        <property name="log4j.appender.file.File" value="jcr.log"/>
        <property name="log4j.appender.file.layout" value="org.apache.log4j.PatternLayout"/>
        <property name="log4j.appender.file.layout.ConversionPattern" value="%d{dd.MM.yyyy HH:mm:ss} %m (%F, line %L) %n"/>
      </properties-param >
    </init-params>
  </component>

The kernel has a framework for exposing a management view of the various sub systems of the platform. The management view is a lose term for defining how we can access relevant information about the system and how we can apply management operations. JMX is the de facto standard for exposing a management view in the Java Platform but we take in consideration other kind of views such as REST web services. Therefore, the framework is not tied to JMX, yet it provides a JMX part to define more precisely details related to the JMX management view. The legacy framework is still in use but is deprecated in favor of the new framework as it is less tested and less efficient. It will be removed by sanitization in the future.

The managed frameworks defines an API for exposing a management view of objects. The API is targeted for internal use and is not a public API. The framework leverages Java 5 annotations to describe the management view from an object.

The cache service delegates most of the work to the CacheServiceManaged class by using the @ManagedBy annotation. At runtime when a new cache is created, it calls the CacheServiceManaged class in order to let the CacheServiceManaged object register the cache.

@ManagedBy(CacheServiceManaged.class)
public class CacheServiceImpl implements CacheService {

  CacheServiceManaged managed;
  ...
  synchronized private ExoCache createCacheInstance(String region) throws Exception {
    ...
    if (managed != null) {
      managed.registerCache(simple);
    }
    ...
  }
}

The ExoCache interface is annotated to define its management view. The @NameTemplate is used to produce object name values when ExoCache instance are registered.

@Managed
@NameTemplate({@Property(key="service", value="cache"), @Property(key="name", value="{Name}")})
@ManagedDescription("Exo Cache")
public interface ExoCache {

  @Managed
  @ManagedName("Name")
  @ManagedDescription("The cache name")
  public String getName();

  @Managed
  @ManagedName("Capacity")
  @ManagedDescription("The maximum capacity")
  public int getMaxSize();

  @Managed
  @ManagedDescription("Evict all entries of the cache")
  public void clearCache() throws Exception;

  ...
}

The CacheServiceManaged is the glue code between the CacheService and the management view. The main reason is that only exo services are registered automatically against the management view. Any other managed bean must be registered manually for now. Therefore, it needs to know about the management layer via the management context. The management context allows an object implementing the ManagementAware interface to receive a context to perform further registration of managed objects.

@Managed
public class CacheServiceManaged implements ManagementAware {

  /** . */
  private ManagementContext context;

  /** . */
  private CacheServiceImpl cacheService;

  public CacheServiceManaged(CacheServiceImpl cacheService) {
    this.cacheService = cacheService;

    //
    cacheService.managed = this;
  }

  public void setContext(ManagementContext context) {
    this.context = context;
  }

  void registerCache(ExoCache cache) {
    if (context != null) {
      context.register(cache);
    }
  }
}

The RPCService is only needed in a cluser environment, it is used to communicate with the other cluster nodes. It allows to execute a command on all the cluster nodes or on the coordinator i.e. the oldest node in the cluster. The RPCService has been designed to rely on JGroups capabilities and should not be used for heavy load. It can be used, for example, to notify other nodes that something happened or to collect some information from the other nodes.

The RPCService relies on 3 main interfaces which are:

The arguments that will be given to the RemoteCommand must be Serializable and its return type also in order to prevent any issue due to the serialization. To prevent to execute any RemoteCommand that could be malicious and to allow to use non Serializable command, you need to register the command first before using it. Since the service will keep only one instance of RemoteCommand per command Id, the implementation of the RemoteCommand must be thread safe.

To be usable, all the RemoteCommands must be registered before being used on all the cluster nodes, which means that the command registration must be done in the constructor of your component in other words before that the RPCService is started. If you try to launch a command that has been registered but the RPCService is not yet launched, you will get an RPCException due to an illegal state. This has for consequences that you will be able to execute a command only once your component will be started.

See an example below:

public class MyService implements Startable
{
   private RPCService rpcService;
   private RemoteCommand sayHelloCommand;
   
   public MyService(RPCService rpcService)
   {
      this.rpcService = rpcService;
      // Register the command before that the RPCService is started
      sayHelloCommand = rpcService.registerCommand(new RemoteCommand()
      {
         public Serializable execute(Serializable[] args) throws Throwable
         {
            System.out.println("Hello !");
            return null;
         }

         public String getId()
         {
            return "hello-world-command";
         }
      });
   }

   public void start()
   {
      // Since the RPCService is a dependency of RPCService, it will be started before
      // so I can execute my command
      try
      {
         // This will make all the nodes say "Hello !"
         rpcService.executeCommandOnAllNodes(sayHelloCommand, false);
      }
      catch (SecurityException e)
      {
         e.printStackTrace();
      }
      catch (RPCException e)
      {
         e.printStackTrace();
      }
   }

   public void stop()
   {
   }
}

In the previous example, we register the command sayHelloCommand in the constructor of MyService and we execute this command in the start method.

The configuration of the RPCService should be added only in a cluster environment. See below an example of configuration in case you intend to use JGroups 2 (which is mandatory if you use JBoss Cache as underlying cache):

<configuration>
....  
  <component>
    <key>org.exoplatform.services.rpc.RPCService</key>
    <type>org.exoplatform.services.rpc.impl.RPCServiceImpl</type>
    <init-params>
      <value-param>
        <name>jgroups-configuration</name>
        <value>classpath:/udp.xml</value>
      </value-param>
      <value-param>
        <name>jgroups-cluster-name</name>
        <value>RPCService-Cluster</value>
      </value-param>
      <value-param>
        <name>jgroups-default-timeout</name>
        <value>0</value>
      </value-param>
      <value-param>
        <name>allow-failover</name>
        <value>true</value>
      </value-param>
      <value-param>
        <name>retry-timeout</name>
        <value>20000</value>
      </value-param>
    </init-params>
  </component>   
...
</configuration>

See below an example of configuration in case you intend to use JGroups 3 (which is mandatory if you use Infinispan as underlying cache):

<configuration>
....  
  <component>
    <key>org.exoplatform.services.rpc.RPCService</key>
    <type>org.exoplatform.services.rpc.jgv3.RPCServiceImpl</type>
    <init-params>
      <value-param>
        <name>jgroups-configuration</name>
        <value>classpath:/udp.xml</value>
      </value-param>
      <value-param>
        <name>jgroups-cluster-name</name>
        <value>RPCService-Cluster</value>
      </value-param>
      <value-param>
        <name>jgroups-default-timeout</name>
        <value>0</value>
      </value-param>
      <value-param>
        <name>allow-failover</name>
        <value>true</value>
      </value-param>
      <value-param>
        <name>retry-timeout</name>
        <value>20000</value>
      </value-param>
    </init-params>
  </component>   
...
</configuration>

The implementation for JGroups 3 is available in the library exo.kernel.component.ext.rpc.impl.jgroups.v3-X.Y.Z.jar.