JBoss.orgCommunity Documentation

Chapter 55. Service Configuration in Detail

55.1. Objectives
55.2. Requirements
55.3. Sample Service
55.3.1. Java Class
55.3.2. First configuration file
55.3.3. Init Parameters
55.3.4. Service Access
55.4. Parameters
55.4.1. Value-Param
55.4.2. Properties-Param
55.4.3. Object-Param
55.4.4. Collection
55.5. External Plugin
55.6. Import
55.7. System properties
55.8. Understanding the prefixes supported by the configuration manager

Related documents

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

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 could possibly 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: https://anonsvn.jboss.org/repos/exo-jcr/kernel/trunk/exo.kernel.container/src/test/resources/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 import tag allows to import other configuration files using URLs that are configuration manager specific, for more details about what are the supported URLs please refer to the next section about the configuration manager.

See below an example of a configuration file composed of imports:

<import>war:/conf/common/common-configuration.xml</import>
<import>war:/conf/common/logs-configuration.xml</import>
<import>war:/conf/database/database-configuration.xml</import>
<import>war:/conf/jcr/jcr-configuration.xml</import>
<import>war:/conf/common/portlet-container-configuration.xml</import>
... 

Since kernel 2.0.7 and 2.1, it is possible to use system properties in literal values of component configuration meta data. This makes it possible to resolve properties at runtime instead of providing a value at packaging time.

See below an example of a configuration file based on system properties:

  <component>
    <key>org.exoplatform.services.database.HibernateService</key>
    <jmx-name>database:type=HibernateService</jmx-name>
    <type>org.exoplatform.services.database.impl.HibernateServiceImpl</type>
    <init-params>
      <properties-param>
        <name>hibernate.properties</name>
        <description>Default Hibernate Service</description>
...
        <property name="hibernate.connection.url" value="${connectionUrl}"/>
        <property name="hibernate.connection.driver_class" value="${driverClass}"/>
        <property name="hibernate.connection.username" value="${username}"/>
        <property name="hibernate.connection.password" value="${password}"/>
        <property name="hibernate.dialect" value="${dialect}"/>
...
      </properties-param>
    </init-params>
  </component>

As these are system properties you use the -D command: java -DconnectionUrl=jdbc:hsqldb:file:../temp/data/exodb -DdriverClass=org.hsqldb.jdbcDriver Or better use the parameters of eXo.bat / eXo.sh when you start eXo Portal: set EXO_OPTS="-DconnectionUrl=jdbc:hsqldb:file:../temp/data/exodb -DdriverClass=org.hsqldb.jdbcDriver"

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