eXo Platform 3.5

- Prev
- Next

## Chapter 8. Advanced Development

## Foundations

### GateIn Kernel

GateIn 3.2 is built as a set of services on top of the dependency injection kernel. The kernel provides configuration, lifecycle handling, component scopes, and some core services.

Service components exist in two scopes. The first scope is represented by RootContainer - it contains services that exist independently of any portal, and can be accessed by all portals.

The second scope is portal-private in the form of PortalContainer. Each portal lives in an instance of PortalContainer. This scope contains services that are common for a set of portals, and services which should not be shared by all portals.

Whenever a specific service is looked up through PortalContainer, and the service is not available, the lookup is delegated further up to RootContainer. We can therefore have the default instance of a certain component in RootContainer, and portal specific instances in some or all PortalContainers which override the default instance.

Whenever your portal application has to be integrated more closely with GateIn services, the way to do it is by looking up these services through PortalContainer. Be careful though - only officially documented services should be accessed this way, and used according to documentation, as most of the services are an implementation detail of GateIn, and subject to change without notice.

### Configure services

GateIn Kernel uses the dependency injection to create services based on the configuration.xml configuration files. The location of the configuration files determines if services are placed into the RootContainer scope, or into the PortalContainer scope. All configuration.xml files located at conf/configuration.xml in the classpath (any directory, or any jar in the classpath) will have their services configured at the RootContainer scope. All configuration.xml files located at conf/portal/configuration.xml in the classpath will have their services configured at the PortalContainer scope. Additionally, portal extensions can contain configuration in WEB-INF/conf/configuration.xml, and will also have their services configured at the PortalContainer scope.

### Note

Portal extensions are described later.

### Configuration syntax

#### Components

A service component is defined in the configuration.xml file by using <component> element.

There is only one required information when defining a service - the service implementation class, specified using <type>

Every component has a <key> that identifies it. If not explicitly set, a key defaults to the value of <type>. If the key is loaded as a class, the Class or String object will be used as a key.

The usual approach is to specify an interface as a key.

Example 8.1. Example of service component configuration:

    <?xml version="1.0" encoding="ISO-8859-1"?>

    <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>org.exoplatform.services.database.HibernateService</key>

          <type>org.exoplatform.services.database.impl.HibernateServiceImpl</type>

          ...

       </component>

    </configuration>

#### External Plugins

GateIn Kernel supports non-component objects that can be configured, instantiated, and injected into registered components, using method calls. The mechanism is called 'plugins', and allows portal extensions to add additional configurations to core services.

The external plugin is defined by using the <external-component-plugins> wrapper element which contains one or more <component-plugin> definitions. <external-component-plugins> uses <target-component> to specify a target service component that will receive injected objects.

Every <component-plugin> defines an implementation type, and a method on the target component to use for the injection (<set-method>).

A plugin implementation class has to implement the org.exoplatform.container.component. ComponentPlugin interface.

In the following example, PortalContainerDefinitionPlugin implements ComponentPlugin:

Example 8.2. PortalContainerDefinitionPlugin

    <?xml version="1.0" encoding="UTF-8"?>

    <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">

       <external-component-plugins>

          <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 fully qualified name of the PortalContainerDefinitionPlugin -->

             <type>org.exoplatform.container.definition.PortalContainerDefinitionPlugin</type>

             ...

          </component-plugin>

       </external-component-plugins>

    </configuration>

#### Includes, and special URLs

It is possible to break the configuration.xml file into many smaller files, that are then included into a 'master' configuration file. The included files are complete configuration.xml documents, not fragments of text.

The following is an example configuration.xml which 'outsources' its content into several files:

    <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">

       <import>war:/conf/sample-ext/jcr/jcr-configuration.xml</import>

       <import>war:/conf/sample-ext/portal/portal-configuration.xml</import>

    </configuration>

The special URL is used to refer to another configuration file. The URL schema 'war:' means that the path following is resolved that is related to the current PortalContainer's servlet context resource path, starting at WEB-INF as the root.

### Note

The current PortalContainer is really a newly created PortalContainer, as war: URLs only make sense for PortalContainer scoped configuration.

Also, thanks to the extension mechanism, the servlet context used for resource loading is a unified servlet context (as explaned in a later section).

To include the resolved path related to the current classpath (context classloader), use the 'jar:' URL schema.

#### Special variables

The configuration files may contain a special variable reference ${container.name.suffix}. This variable resolves to the name of the current portal container, prefixed by underscore (_). This facilitates reuse of configuration files in cases where portal specific unique names need to be assigned to some resources (For example, JNDI names, Database / DataSource names, JCR repository names).

This variable is only defined when there is a current PortalContainer available - only for PortalContainer scoped services.

A good example for this is HibernateService:

Example 8.3. HibernateService using variables

    <?xml version="1.0" encoding="ISO-8859-1"?>

    <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>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.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${container.name.suffix}" />

                <property name="hibernate.connection.driver_class" value="org.hsqldb.jdbcDriver" />

                <property name="hibernate.connection.autocommit" value="true" />

                <property name="hibernate.connection.username" value="sa" />

                <property name="hibernate.connection.password" value="" />

                <property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />

                <property name="hibernate.c3p0.min_size" value="5" />

                <property name="hibernate.c3p0.max_size" value="20" />

                <property name="hibernate.c3p0.timeout" value="1800" />

                <property name="hibernate.c3p0.max_statements" value="50" />

             </properties-param>

          </init-params>

       </component>

    </configuration>

### InitParams configuration object

InitParams are the configuration object that is essentially a map of key-value pairs, where key is always a String, and value can be any type that can be described using the kernel configuration.xml.

Service components that form GateIn 3.2 insfrastructure use the InitParams object to configure themselves. A component can have one instance of InitParams injected at most. If the service component's constructor takes InitParams as any of the parameters, it will automatically be injected at the component instantiation time. The xml configuration for a service component that expects the InitParams object must include the <init-params> element (even if the empty one).

To learn about how the kernel xml configuration syntax looks for creating InitParams instances, see the following example.

Example 8.4. InitParams - properties-param

    <component>

       <key>org.exoplatform.services.naming.InitialContextInitializer</key>

       <type>org.exoplatform.services.naming.InitialContextInitializer</type>

       <init-params>

          <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>

       </init-params>

    </component>

The InitParams object description begins with the <init-params> element. It can have zero or more children elements, each of which is one of <value-param>, <values-param>, <properties-param>, or <object-param>. Each of these child elements takes a <name> that serves as a map entry key, and an optional <description>. It also takes a type-specific value specification.

For <properties-param>, the value specification is in the form of one or more <property> elements, each of which specifies two strings - a property name, and a property value. Each <properties-params> defines one java.util.Properties instance. Also, see Example 8.3, “HibernateService using variables” for an example.

Example 8.5. InitParams - value-param

    <component>

       <key>org.exoplatform.services.transaction.TransactionService</key>

       <type>org.exoplatform.services.transaction.impl.jotm.TransactionServiceJotmImpl</type>

       <init-params>

          <value-param>

             <name>timeout</name>

             <value>5</value>

          </value-param>

       </init-params>

    </component>

For <value-param>, the value specification is in the form of <value> element, which defines one String instance.

Example 8.6. InitParams - values-param

    <component>

      <key>org.exoplatform.services.resources.ResourceBundleService</key>

      <type>org.exoplatform.services.resources.impl.SimpleResourceBundleService</type>

        <init-params>

          <values-param>

            <name>classpath.resources</name>

            <description>The resources  that start with the following package name should be load from file system</description>

            <value>locale.portlet</value>

          </values-param>

          <values-param>

            <name>init.resources</name>

            <description>Store the following resources into the db for  the first launch </description>

            <value>locale.test.resources.test</value>

          </values-param>

          <values-param>

            <name>portal.resource.names</name>

            <description>The properties files of  the portal ,  those file will be merged

              into one ResourceBundle properties </description>

            <value>local.portal.portal</value>

            <value>local.portal.custom</value>

          </values-param>

        </init-params>

    </component>

For <values-param>, the value specification is in the form of one or more <value> elements, each of which represents one String instance, where all the String values are then collected into a java.util.List instance.

Example 8.7. InitParams - object-param

    <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>300</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>

For <object-param>, the value specification comes in a form of the <object> element, which is used for the POJO style object specification (you specify an implementation class - <type>, and property values - <field>).

Also see Example 8.8, “Portal container declaration example” for an example of specifying a field of the Collection type.

The InitParams structure - the names and types of entries is specific for each service, as it is the code inside service components's class that decides what entry names to look up and what types it expects to find.

### Configure a portal container

A portal container is defined by several attributes.

First, there is a portal container name, which is always equal to the URL context to which the current portal is bound.

Second, there is a REST context name, which is used for REST access to portal application - every portal has exactly one (unique) REST context name.

Then, there is a realm name which is the name of security realm used for authentication when users log into the portal.

Finally, there is a list of Dependencies - other web applications, whose resources are visible to current portal (via extension mechanism described later), and are searched in the specified order.

Example 8.8. Portal container declaration example

    <?xml version="1.0" encoding="UTF-8"?>

    <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">

       <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>portal</string></field>

                      <!-- The name of the context name of the rest web application -->

                      <field name="restContextName"><string>rest</string></field>

                      <!-- The name of the realm -->

                      <field name="realmName"><string>exo-domain</string></field>

                      <!-- All the dependencies of the portal container ordered by loading priority -->

                      <field name="dependencies">

                         <collection type="java.util.ArrayList">

                            <value>

                               <string>eXoResources</string>

                            </value>

                            <value>

                               <string>portal</string>

                            </value>

                            <value>

                               <string>dashboard</string>

                            </value>

                            <value>

                               <string>exoadmin</string>

                            </value>

                            <value>

                               <string>eXoGadgets</string>

                            </value>

                            <value>

                               <string>eXoGadgetServer</string>

                            </value>

                            <value>

                               <string>rest</string>

                            </value>

                            <value>

                               <string>web</string>

                            </value>

                            <value>

                               <string>wsrp-producer</string>

                            </value>

                            <!-- The sample-ext has been added at the end of the dependency list

                                 in order to have the highest priority -->

                            <value>

                               <string>sample-ext</string>

                            </value>

                         </collection>

                      </field>

                   </object>

                </object-param>

             </init-params>

          </component-plugin>

       </external-component-plugins>

    </configuration>

### Note

Dependencies are part of the extension mechanism.

Every portal container is represented by a PortalContainer instance, including:

- Associated ExoContainerContext containing information about the portal.

- Unified servlet context for web-archive-relative resource loading.

- Unified classloader for classpath based resource loading.

- Methods for retrieving services.

Unified servlet context, and unified classloader are part of the extension mechanism (explained in next section), and provide standard APIs (ServletContext, ClassLoader) with the specific resource loading behavior - visibility into the associated web application archives, configured with Dependencies property of PortalContainerDefinition. Resources from other web applications are queried in the order specified by Dependencies. The later entries in the list override the previous ones.

### GateIn Extension Mechanism and Portal Extensions

Extension mechanism is a functionality that makes it possible to override the portal resources in an almost plug-and-play fashion - just drop in a .war archive with the resources, and configure its position on the portal's classpath. This way any customizations of the portal do not have to involve unpacking and repacking the original portal .war archives. Instead, create your own .war archive with changed resources that override the resources in the original archive.

A web archive packaged in a way to be used through the extension mechanism is called portal extension.

There are two steps necessary to create a portal extension.

First, declare PortalConfigOwner servlet context listener in web.xml of your web application.

Example 8.9. Example of a portal extension called sample-ext:

    <?xml version="1.0" encoding="ISO-8859-1" ?>

    <!DOCTYPE web-app PUBLIC -//Sun Microsystems, Inc.//DTD Web Application 2.3//EN

         http://java.sun.com/dtd/web-app_2_3.dtd>

    <web-app>

       <display-name>sample-ext</display-name>

       <listener>

          <listener-class>org.exoplatform.container.web.PortalContainerConfigOwner</listener-class>

       </listener>

       ...

    </web-app>

Then, add the servlet context name of this web application to a proper location in the list of Dependencies of the PortalContainerDefinition of all the portal containers that you want to access its resources.

After this step, your web archive will be on the portal's unified classpath, and unified servlet context resource path. The later in the Dependencies list your application is, the higher priority it has when resources are loaded by portal.

### Note

See the 'Configuring a portal' section for example of PortalContainerDefinition, that has sample-ext at the end of its list of Dependencies.

### Run Multiple Portals

It is possible to run several independent portal containers - each bound to a different URL context - within the same JVM instance. This kind of setup is very efficient from administration and resource consumption. The most elegant way to reuse configuration for different coexisting portals is by way of extension mechanism - by inheriting resources and configuration from existing web archives, and just adding extra resources to it, and overriding those that need to be changed by including modified copies.

In order for a portal application to correctly function when deployed in multiple portals, the application may have to dynamically query the information about the current portal container. The application should not make any assumptions about the name, and other information of the current portal, as there are now multiple different portals in play.

At any point during request processing, or lifecycle event processing, your application can retrieve this information through org.exoplatform.container. ExoContainerContext. Sometimes your application needs to make sure that the proper PortalContainer - the source of ExoContainerContext - is associated with the current call.

If you ship servlets or servlet filters as part of your portal application, and if you need to access the portal specific resources at any time during the processing of the servlet or filter request, you need to make sure the servlet/filter is associated with the current container.

The proper way to do that is to make your servlet extend org.exoplatform.container.web. AbstractHttpServlet class. This will not only properly initialize the current PortalContainer for you, but will also set the current thread's context classloader to one that looks for resources in the associated web applications in the order specified by Dependencies configuration (as explained in Extension mechanism section).

Similarly for filters, make sure your filter class extends org.exoplatform.container.web. AbstractFilter. Both AbstractHttpServlet, and AbstractFilter have the same method getContainer(), which returns the current PortalContainer. If your servlet handles the requests by implementing the service() method, you need to rename that method to match the following signature:

    /**

     * Use this method instead of Servlet.service()

     */

    protected void onService(ExoContainer container, HttpServletRequest req,

          HttpServletResponse res) throws ServletException, IOException;

### Note

The reason is that AbstractHttpServlet implements service() to perform its interception, and you don't want to overwrite (by overriding) this functionality.

You may also need to access portal information within your HttpSessionListener. Again, make sure to extend the provided abstract class - org.exoplatform.container.web. AbstractHttpSessionListener. Also, modify your method signatures as follows:

    /**

     * Use this method instead of HttpSessionListener.sessionCreated()

     */

    protected void onSessionCreated(ExoContainer container, HttpSessionEvent event);

    /**

     * Use this method instead of HttpSessionListener.sessionDestroyed()

     */

    protected void onSessionDestroyed(ExoContainer container, HttpSessionEvent event);

There is another method you have to implement in this case:

    /**

     * Method should return true if unified servlet context,

     * and unified classloader should be made available

     */

    protected boolean requirePortalEnvironment();

If this method returns true, the current thread's context classloader is set up according to the Dependencies configuration, and availability of the associated web applications. If it returns false, the standard application separation rules are used for resource loading (effectively turning off the extension mechanism). This method exists on AbstractHttpServlet and AbstractFilter as well, where there is a default implementation that automatically returns true, when it detects there is a current PortalContainer present. Otherwises, it returns false.

The followings explain how to properly perform the ServletContextListener based initialization, when you need to access the current PortalContainer.

GateIn has no direct control over the deployment of application archives (.war, .ear files) - it is the application server performing the deployment. For the extension mechanism to work properly, the applications, associated with the portal via the Dependencies configuration, have to be deployed before the portal, that depends on them, is initialized. On the other hand, these applications may require an already initialized PortalContainer to properly initialize themselves - we have a recursive dependency problem. To resolve this problem, a mechanism of initialization tasks, and task queues, was put in place. Web applications that depend on the current PortalContainer for their initialization have to avoid performing their initialization directly in some ServletContextListener executed during their deployment (before any PortalContainer was initialized). Instead, a web application should package its initialization logic into an init task of the appropriate type, and only use ServletContextListener to insert the init task instance into the proper init tasks queue.

An example of this is the Gadgets application which registers the Google gadgets with the current PortalContainer:

    public class GadgetRegister implements ServletContextListener

    {

       public void contextInitialized(ServletContextEvent event)

       {

          // Create a new post-init task

          final PortalContainerPostInitTask task = new PortalContainerPostInitTask() {

             public void execute(ServletContext context, PortalContainer portalContainer)

             {

                try

                {

                   SourceStorage sourceStorage =

                   (SourceStorage) portalContainer.getComponentInstanceOfType(SourceStorage.class);

                   ...

                }

                catch (RuntimeException e)

                {

                   throw e;

                }

                catch (Exception e)

                {

                   throw new RuntimeException("Initialization failed: ", e);

                }

             }

          };

          // Add post-init task for execution on all the portal containers

          // that depend on the given ServletContext according to

          // PortalContainerDefinitions (via Dependencies configuration)

          PortalContainer.addInitTask(event.getServletContext(), task);

       }

    }

The above example uses PortalContainerPostInitTask, which gets executed after the portal container has been initialized. In some cases, you may want to execute the initialization after portal container was instantiated, but before it was initialized - use PortalContainerPreInitTask in that case. Or, you may want to execute initialization after all the post-init tasks have been executed - use PortalContainerPostCreateTask in that case.

Also, you may need to pay attention to LoginModules. If you use custom LoginModules which require the current ExoContainer, make sure they extend org.exoplatform.services.security.jaas.AbstractLoginModule for the proper initialization. AbstractLoginModule also takes care of the basic configuration - it recognizes two initialization options - portalContainerName, and realmName whose values you can access via protected fields of the same name.

- Prev
- Up
- Home
- Next