Service Configuration for Beginners
Service Configuration for Beginners
Related documents
1 Objective
We are going to talk about service configuration. You will learn about modes, services and containers, you will find out where the service configuration files have to 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.2 Requirements
By reading this article you are already glancing at the heart of eXo Kernel. Therefore you should have read Building+from+sources? and deployed at least eXo Portal. Even you will read in this article to open the directory "exo-tomcat", you may have installed eXo Portal on any application server, just replace "exo-tomcat" by your folder name. You certainly already discovered eXo's fisheye URL (eXo is open source!) - http://fisheye.exoplatform.org - which allows you to surf in the source code of all classes, if you wish to do so.3 Services
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:
- exo.core.component.database-2.1.3.jar
- exo.kernel.component.cache-2.0.5.jar
- exo.core.component.organization.ldap-2.1.3.jar
- exo.jcr.component.ftp-1.10.1.jar
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.
4 Configuration File
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> ...
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.
5 Execution Modes
One important thing to understand concerns execution modes. There are only two modes:- Portal mode: The service runs embedded in the eXo Portal. In this mode a PortalContainer is used.
- Standalone mode: The service runs without the portal. For example, the JCR service can run standalone, and also the eXo Portlet Container. This mode is used by eXo developers for unit tests. As the name suggests a StandaloneContainer is used.
6 Containers
In order to access to a service you need to use a Container. Just open http://fisheye.exoplatform.org/browse/projects-kernel/trunk/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.
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 chapter.
7 Configuration Retrieval
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.7.1 RootContainer
As both containers, PortalContainer and StandaloneContainer, depend on the RootContainer, we will start by looking into this one. The retrieval sequence in short:- Services default RootContainer configurations from JAR files /conf/configuration.xml
- External RootContainer configuration, to be found at exo-tomcat/exo-conf/configuration.xml
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.
7.2 PortalContainer
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 :- Take over the configurations of the RootContainer
- Default PortalContainer configurations from all JAR files (folder /conf/portal/configuration.xml)
- Web application configurations from the portal.war file - or the portal weppapp (folder /WEB-INF/conf/configuration.xml)
- External configuration for services of a named portal, it will be found at exo-tomcat/exo-conf/portal/$portal_name/configuration.xml (as of Portal 2.5)
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 eXo Portal 2.5 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.
7.3 StandaloneContainer
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 chapter. After taking over RootContainer's configuration, there are three cases which depend on the URL initialization, : Independent configuration by URLNo other configuration file is taken in consideration. The configuration provided by the URL is used without any default configs. That means that the container creates a new empty hashtable and not any bit of previous configuration is used. Apply the following code to do this:
StandaloneContainer.setConfigurationURL(containerConf);
The StandaloneContainer is initialized very similar to the PortalContainer, but the last step is slightly different. A configuration file that is provided by the URL is used to replace some of the service configurations. The code looks like this:
StandaloneContainer.addConfigurationURL(containerConf);
- Take over the configurations of the RootContainer
- Default StandaloneContainer configurations from JAR files (folder /conf/portal/configuration.xml)
- Web application configurations from WAR files (folder /WEB-INF/conf/configuration.xml)
- Configuration from added URL containerConf overrides only services configured in the file
No URL is involved, in this case the sequence is:
- Take over the configurations of the RootContainer
- Default StandaloneContainer configurations from JAR files (folder /conf/portal/configuration.xml)
- Web applications configurations from WAR files (folder /WEB-INF/conf/configuration.xml)
- External configuration for StandaloneContainer services, it will be found at $user_home/exo-configuration.xml. If $user_home/exo-configuration.xml doesn't exist and the StandaloneContainer instance obtained with the dedicated configuration classloader the container will try to retrieve the resource conf/exo-configuration.xml within the given classloader (user_home is your home directory like "C:/Documents and Settings/Smith").
8 Service instantiation
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 http://fisheye.exoplatform.org/browse/projects-core/trunk/component/organization/jdbc/src/main/java/org/exoplatform/services/organization/jdbc/OrganizationServiceImpl.java?r=20166 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.
9 Miscellaneous
9.1 Startable interface
Your service can implement the startable interface which defines a start() and a stop() method. These methods are called by the container at the beginning and the end of the container's lifecycle. This way the lifecycle of your service is managed by the container.9.2 Inversion of Control
RetrospectionDo 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:
- Each of your service had a proprietary configuration mechanism.
- The service lifecycles were managed inside of each service or were arbitrary.
- The dependencies between your services were implementation-dependent and tightly coupled in your source code.
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:
- The configuration is injected by external configuration files.
- The lifecycle is managed from outside, because the constructors are called by the container. You can achieve an even finer lifecycle management if you use the startable interface.
- The dependencies are injected by the service instantiation process.
You also saw two types of dependency injections:
- Constructor injection: The constructor is called by the container.
- Setter injection: Whenever you use external-plugins to provide your service with plugins (see Service Configuration in Detail.
9.3 More Containers
There are two more Containers called RepositoryContainer and WorkspaceContainer. These are specificities of eXo JCR, for the sake of simplicity. You don't need them.9.4 Single Implementation Services
In some case the developer of a service does not expect that there will be several implementations for his service. Therefore he does not create an interface. In this case the configuration looks like this:<key>org.exoplatform.services.database.jdbc.DBSchemaCreator</key> <type>org.exoplatform.services.database.jdbc.DBSchemaCreator</type>
9.5 Configuration properties
Since kernel 2.0.7 and 2.1, it is possible to use system properties in literal values of component configuration meta data. Thus it is possible to resolve properties at runtime instead of providing a value at packaging time.<component>
...
<init-params>
<value-param>
<name>simple_param</name>
<value>${simple_param_value}</value>
</value-param>
<properties-param>
<name>properties_param</name>
<property name="value_1" value="properties_param_value_1"/>
<property name="value_2" value="${properties_param_value_2}"/>
</properties-param>
<object-param>
<name>object_param</name>
<object type="org.exoplatform.xml.test.Person">
<field name="address"><string>${person_address}</string></field>
<field name="male"><boolean>${person_male}</boolean></field>
<field name="age"><int>${age_value}</int></field>
<field name="size"><double>${size_value}</double></field>
</object>
</object-param>
</init-params>
</component>9.6 Configuration Logging
In case you need to solve problems with your service configuration, you have to know from which JAR/WAR causes your troubles. Add the JVM system property org.exoplatform.container.configuration.debug to your eXo.bat or eXo.sh file (exo-tomcat/bin/).set EXO_CONFIG_OPTS="-Dorg.exoplatform.container.configuration.debug"
......
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
......