Service Configuration in Detail
Service Configuration in Detail
Related documents
1 Objectives
This article shows in the first chapters 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.2 Requirements
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 do not forget that the terms service and component are interchangeable in eXo Products.3 Sample Service
3.1 Java Class
Imagine you are working for a publishing company called "La Verdad" that is going to use eXo platform. Your boss asks you be able to calculate the number of sentences of an article. You remember in eXo product everything is a service so you decide to create a simple class. In future you want to be able to plug different implementations of your service, so that you should define an interface that defines your service.
package com.laverdad.services;
public interface ArticleStatsService {
public abstract int calcSentences(String article);
}
A very simple implementation:
public class ArticleStatsServiceImpl implements ArticleStatsService {
public int calcSentences(String article) {
throw new RuntimeException("Not implemented");
}
}
That's it! You see there are no special prerequisites for a service. You should already have prepared your working environment, where you have a base folder (let's call it our service base folder). If you wish to try out this example create this class in the com/laverdad/services/ArticleStatsService subfolder.
3.2 First configuration file
When creating a service you also should declare its existence to the Container, therefore you create a first simple configuration file. Copy the following code to a file that is called "configuration.xml" and place this file in a /conf subdirectory of your service base folder. As you already know the container looks for a "/conf/configuration.xml" file in each jar-file.<?xml version="1.0" encoding="UTF8"?> <configuration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.exoplaform.org/xml/ns/kernel_1_0.xsd http://www.exoplaform.org/xml/ns/kernel_1_0.xsd" xmlns="http://www.exoplaform.org/xml/ns/kernel_1_0.xsd"> <component> <key>com.laverdad.services.ArticleStatsService</key> <type>com.laverdad.services.ArticleStatsServiceImpl</type> </component> </configuration>
3.3 Init Parameters
You see your service has a configuration file, but you wonder how the file could possibly access to its configuration. Imagine 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>
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"); } }
3.4 Service Access
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:- ExoContainer myContainer = ExoContainerContext.getCurrentContainer();
- myContainer.getComponentInstance(class)
- ArticleStatsService statsService = (ArticleStatsService) myContainer.getComponentInstance(ArticleStatsService.class);
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)); } }
4 Parameters
4.1 Value-Param
Here is an extract of http://fisheye.exoplatform.org/browse/projects/portal/trunk/web/portal/src/main/webapp/WEB-INF/conf/portal/portal-configuration.xml:<component> <key>org.exoplatform.portal.config.UserACL</key> <type>org.exoplatform.portal.config.UserACL</type> <init-params> ... <value-param> <name>access.control.workspace</name> <description>groups with memberships that have the right to access the User Control Workspace</description> <value>*:/platform/administrators,*:/organization/management/executive-board</value> </value-param> ... </component>
package org.exoplatform.portal.config; public class UserACL { public UserACL(InitParams params) { UserACLMetaData md = new UserACLMetaData(); ValueParam accessControlWorkspaceParam = params.getValueParam("access.control.workspace"); if(accessControlWorkspaceParam != null) md.setAccessControlWorkspace(accessControlWorkspaceParam.getValue()); ...
4.2 Properties-Param
Properties are name-value pairs. Both the name and the value are Java Strings. Here you see the hibernate configuration "portal/trunk/web/portal/src/main/webapp/WEB-INF/conf/database/database-configuration.xml":<component> <key>org.exoplatform.services.database.HibernateService</key> <type>org.exoplatform.services.database.impl.HibernateServiceImpl</type> <init-params> <properties-param> <name>hibernate.properties</name> <description>Default Hibernate Service</description> <property name="hibernate.show_sql" value="false"/> <property name="hibernate.cglib.use_reflection_optimizer" value="true"/> <property name="hibernate.connection.url" value="jdbc:hsqldb:file:../temp/data/exodb"/> <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver"/> ... </properties-param> </init-params> </component>
package org.exoplatform.services.database.impl; public class HibernateServiceImpl implements HibernateService, ComponentRequestLifecycle { public HibernateServiceImpl(InitParams initParams, CacheService cacheService) { PropertiesParam param = initParams.getPropertiesParam("hibernate.properties"); ... }
4.3 Object-Param
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>
package org.exoplatform.services.ldap.impl; public class LDAPServiceImpl implements LDAPService { ... public LDAPServiceImpl(InitParams params) { LDAPConnectionConfig config = (LDAPConnectionConfig) params.getObjectParam("ldap.config") .getObject(); ...
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"; ...
- string, int, long, boolean, date, double
4.4 Collection
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>
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; ... }
4.5 Map
TODO: find example4.6 Native-Array
TODO: find example5 External Plugin
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>- addTaxonomyPlugin(org.exoplatform.services.cms.categories.impl.TaxonomyPlugin plugin)
6 Import
The import tag allows to link to other configuration files. These imported files can be placed anywhere. If you write a default configuration which is part of your jar file you should not import files from outside your jar.- war: Imports from portal.war/WEB-INF
- jar or classpath: Uses the classloader, you can use this prefix in the default configuration for importing an other configuration file which is accessible by the classloader.
- file: Uses an absolute path, you also can put a URL.
- without any prefix:
- Standalone mode: user directory
- Portal mode: $AS-HOME, that means the application server home, for example "exo-tomcat".
TODO: examples for jar, file, withoutwar:/conf/common/common-configuration.xml war:/conf/common/logs-configuration.xml war:/conf/database/database-configuration.xml war:/conf/jcr/jcr-configuration.xml war:/conf/common/portlet-container-configuration.xml …
7 System properties
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. In portal/trunk/web/portal/src/main/webapp/WEB-INF/conf/database/database-configuration.tmpl.xml you find an example for 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>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"