JBoss.orgCommunity Documentation
Related documents
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.
By reading this article you are already glancing at the heart of eXo Kernel.
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.
If you only installed the all-in-one package for the eXo Portal, the folder paths are a slightly different. You have to replace exo-tomcat by exo-eXoPortal-2.5.1-tomcat (obviously depending on your version). Furthermore the webapps are delivered as war files.
You certainly already discovered eXo's fisheye URL (eXo is open source!) - https://anonsvn.jboss.org/repos/exo-jcr/ - which allows you to surf in the source code of all classes, if you wish to do so.
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
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.
Why are there 2 different places to look for the
configuration.xml? Because the /conf directory is
used by the RootContainer
and the
/conf/portal directory is used by the
PortalContainer
. Later you will see more details
about these containers.
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.
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.
In order to access to a service you need to use a Container. Just open https://anonsvn.jboss.org/repos/exo-jcr/kernel/trunk/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 chapter.
"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:
Services default RootContainer
configurations from JAR files
/conf/configuration.xml
External RootContainer
configuration,
to be found at
exo-tomcat/exo-conf/configuration.xml
Naturally you always have to replace
exo-tomcat
by your own folder name. In case of
a Java Standalone application you have to use the
user.dir
JVM system property value.
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.
What happens if the same service - recognized by the same qualified interface name - is configured in different jars? As the service only can exist one time the configuration of the jar found later overrides the previous configuration. You know that the loading order of the jars is unpredictable you must not depend on this.
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 :
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)
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 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.
As of eXo Portal 2.5 you can override the external configuration location with the system property exo.conf.dir. If the property exists its value will be used as path to the eXo configuration directory, that means this is an alternative to exo-tomcat/exo-conf. Just put this property in the command line: java -Dexo.conf.dir=/path/to/exo/conf or use eXo.bat or eXo.sh. In this particular use case, you have no need to use any prefixes in your configuration file to import other files. For example, if your configuration file is exo-tomcat/exo-conf/portal/PORTAL_NAME/configuration.xml and you want to import the configuration file exo-tomcat/exo-conf/portal/PORTAL_NAME/mySubConfDir/myConfig.xml, you can do it by adding <import>mySubConfDir/myConfig.xml</import> to your configuration file.
Under JBoss application server exo-conf will be looked up in directory described by JBoss System property jboss.server.config.url. If the property is not found or empty exo-jboss/exo-conf will be asked (since kernel 2.0.4).
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 URL No 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);
Additional configuration by URL 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
File based configuration 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").
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.
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.
What happens if one service has more than one constructor? The container always tries first to use the constructor with a maximum 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 one).
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.
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:
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.
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:
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.
Dependency Injection 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.
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.
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>
The key and type tags contain equally the qualified class name.
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>
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"
If this property is set the container configuration manager reports during startup the configuration retrieval 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 ......
Do you feel 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 a 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.