JBoss.orgCommunity Documentation

Chapter 77. How to extend my GateIn instance?

77.1. Introduction
77.1.1. Overview
77.1.2. Motivations
77.2. Prerequisites
77.2.1. Removing all the hard coded portal container name (i.e. "portal")
77.2.2. Removing all the hard coded rest context name (i.e. "rest")
77.2.3. Removing all the hard coded realm name (i.e. "exo-domain")
77.2.4. Making your Http Filters compatible
77.2.5. Making your HttpServlets compatible
77.2.6. Making your HttpSessionListeners compatible
77.2.7. Use init tasks if you need a PortalContainer to initialize an Http Filter or an HttpServlet
77.2.8. Making your LoginModules compatible
77.2.9. Avoiding static modifier on component dependency
77.2.10. Avoid component initialization based on component dependency in the constructor
77.3. FAQ
77.3.1. What has changed since the previous versions?
77.3.2. What is the main purpose of a portal extension?
77.3.3. What is the main purpose of the starter?
77.3.4. How a portal and a portal container are related?
77.3.5. How to define and register a PortalContainerDefinition?
77.3.6. How the platform interprets the dependency order defined into the PortalContainerDefinition?
77.3.7. How to change the ServletContext name, the realm name and/or the rest context name of my portal without using a PortalContainerDefinition?
77.3.8. How to add new configuration file to a given portal from a war file?
77.3.9. How to create/define a portal extension?
77.3.10. How to deploy a portal extension?
77.3.11. How to create/define a new portal?
77.3.12. How to deploy a new portal?
77.3.13. How to import properly a configuration file using the prefix "war:"?
77.3.14. How to avoid duplicating configuration files just to rename a simple value?
77.3.15. How to add or change a Repository and/or a Workspace?
77.3.16. How to add new ResourceBundles to my portal?
77.3.17. How to overwrite existing ResourceBundles in my portal?
77.3.18. How to replace a groovy template of my portal?
77.3.19. How to add new Portal Configurations, Navigations, Pages or Portlet Preferences to my portal?
77.3.20. How to add new Http Filters to my portal without modifying the portal binary?
77.3.21. How to add new HttpSessionListeners and/or ServletContextListeners to my portal without modifying the portal binary?
77.3.22. How to add new HttpServlet to my portal without modifying the portal binary?
77.3.23. How to override or add a Context Parameter to my portal without modifying the portal binary?
77.3.24. Where can I found an example of how to extend my portal?
77.3.25. How to deploy the sample extension?
77.3.26. Where can I find an example of how to create a new portal?
77.3.27. How to deploy the sample portal?
77.3.28. I get "java.lang.IllegalStateException: No pre init tasks can be added to the portal container 'portal', because it has already been initialized." what can I do to fix it?
77.4. Recommendations
77.4.1. Don't ship your configuration files with your jar files?
77.4.2. Using a dedicated workspace/repository for your extension?

To be able to migrate an application to GateIn, the first thing we need to do is to ensure that our application supports properly several portal container instances. The following section aims to help you to be compatible with GateIn.

Now all your HttpServlets that need to get the current ExoContainer must extends org.exoplatform.container.web.AbstractHttpServlet. This abstract class will ensure that the environment has been properly set, so you will be able to call the usual methods such as ExoContainerContext.getCurrentContainer() (if it must also be compatible with the standalone mode) or PortalContainer.getInstance() (if it will only work on a portal environment mode).

If you had to implement the method service(HttpServletRequest req, HttpServletResponse res), now you will need to implement onService(ExoContainer container, HttpServletRequest req, HttpServletResponse res), this method will directly give you the current ExoContainer in its signature.

If your Http Filter or your HttpServlet requires a PortalContainer to initialize, you need to convert your code in order to launch the code responsible for the initialization in the method onAlreadyExists of an org.exoplatform.container.RootContainer.PortalContainerInitTask.

We need to rely on init tasks, in order to be sure that the portal container is at the right state when the task is executed, in other words the task could be delayed if you try to execute it too early. Each task is linked to a web application, so when we add a new task, we first retrieve all the portal containers that depend on this web application according to the PortalContainerDefinitions, and for each container we add the task in a sorted queue which order is in fact the order of the web applications dependencies defined in the PortalContainerDefinition. If no PortalContainerDefinition can be found we execute synchronously the task which is in fact the old behavior (i.e. without the starter).

The supported init tasks are:

A init task is defined as below:

To add a task, you can either call:

We will take for example the class GadgetRegister that is used to register new google gadgets on a given portal container.

The old code was:

The new code relies on a org.exoplatform.container.RootContainer.PortalContainerPostInitTask, as you can see below

A PortalContainerDefinition allows you to indicate the platform how it must initialize and manage your portal. In a PortalContainerDefinition, you can define a set of properties, such as:

You can define and register a PortalContainerDefinition thanks to an external plugin that has to be treated at the RootContainer level. In other words, your configuration file must be a file conf/configuration.xml packaged into a jar file or $AS_HOME/exo-conf/configuration.xml (for more details, please have a look to the article Container Configuration).

See below an example of configuration file that define and register a PortalContainerDefinition:

<?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>
                <value>
                  <string>sample-ext</string>
                </value>
              </collection>
            </field>
          </object>
        </object-param>
      </init-params>
    </component-plugin>
  </external-component-plugins>
</configuration>

In the previous example, we define a portal container called "portal", which rest context name is "rest", which realm name is "exo-domain" and which dependencies are the web applications "eXoResources", "portal"... The platform will load first "eXoResources", then "portal" and so on.

To do that you need first to change the default values used by a PortalContainer that has not been defined thanks to a PortalContainerDefinition. Those default values can be modified thanks to a set of init parameters of the component PortalContainerConfig.

The component PortalContainerConfig must be registered at the RootContainer level. In other words, your configuration file must be a file conf/configuration.xml packaged into a jar file or $AS_HOME/exo-conf/configuration.xml (for more details please have a look to the article Container Configuration).

In the example below we will rename:

  • The portal name "portal" to "myPortal".

  • The rest servlet context name "rest" to "myRest".

  • The realm name "exo-domain" to "my-exo-domain".

See below an 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">
  <component>
    <!-- The full qualified name of the PortalContainerConfig -->
    <type>org.exoplatform.container.definition.PortalContainerConfig</type>
    <init-params>
      <!-- The name of the default portal container -->
      <value-param>
        <name>default.portal.container</name>
        <value>myPortal</value>
      </value-param>
      <!-- The name of the default rest ServletContext -->
      <value-param>
        <name>default.rest.context</name>
        <value>myRest</value>
      </value-param>
      <!-- The name of the default realm -->
      <value-param>
        <name>default.realm.name</name>
        <value>my-exo-domain</value>
      </value-param>
    </init-params>
  </component>
</configuration>

Once your configuration is ready, you need to:

  • Update the file WEB-INF/web.xml of the file "portal.war" by changing the "display-name" (the new value is "myPortal") and the "realm-name" in the "login-config" (the new value is "my-exo-domain").

  • If you use JBoss AS: Update the file WEB-INF/jboss-web.xml of the file "portal.war" by changing the "security-domain" (the new value is "java:/jaas/my-exo-domain").

  • Rename the "portal.war" to "myPortal.war" (or "02portal.war" to "02myPortal.war")

  • Update the file WEB-INF/web.xml of the file "rest.war" by changing the "display-name" (the new value is "myRest") and the "realm-name" in the "login-config" (the new value is "my-exo-domain").

  • If you use JBoss AS: Update the file WEB-INF/jboss-web.xml of the file "rest.war" by changing the "security-domain" (the new value is "java:/jaas/my-exo-domain").

  • Rename the "rest.war" to "myRest.war"

  • If "portal.war" and "rest.war" were embedded into an ear file: Update the file META-INF/application.xml of the file "exoplatform.ear" by remaming "02portal.war" to "02myPortal.war", "portal" to "myPortal", "rest.war" to "myRest.war" and "rest" to "myRest".

The end of the process depends on your application server

To indicate the platform that a given web application has configuration file to provide, you need to:

The simple fact to add this Servlet Context Listener, will add the Servlet Context of this web application to the Unified Servlet Context of all the PortalContainers that depend on this web application according to their PortalContainerDefinition.

See an example of a web.xml below:

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

  <context-param>
    <param-name>org.exoplatform.frameworks.jcr.command.web.fckeditor.digitalAssetsWorkspace</param-name>
    <param-value>collaboration</param-value>
    <description>Binary assets workspace name</description>
  </context-param>

  <context-param>
    <param-name>org.exoplatform.frameworks.jcr.command.web.fckeditor.digitalAssetsPath</param-name>
    <param-value>/Digital Assets/</param-value>
    <description>Binary assets path</description>
  </context-param>

  <context-param>
    <param-name>CurrentFolder</param-name>
    <param-value>/Digital Assets/</param-value>
    <description>Binary assets workspace name</description>
  </context-param>

  <!-- ================================================================== -->
  <!--   RESOURCE FILTER TO CACHE MERGED JAVASCRIPT AND CSS               -->
  <!-- ================================================================== -->
  <filter>
    <filter-name>ResourceRequestFilter</filter-name>
    <filter-class>org.exoplatform.portal.application.ResourceRequestFilter</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>ResourceRequestFilter</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>


  <!-- ================================================================== -->
  <!--           LISTENER                                                 -->
  <!-- ================================================================== -->
  <listener>
    <listener-class>org.exoplatform.container.web.PortalContainerConfigOwner</listener-class>
  </listener>
  <!-- ================================================================== -->
  <!--           SERVLET                                                  -->
  <!-- ================================================================== -->
  <servlet>
    <servlet-name>GateInServlet</servlet-name>
    <servlet-class>org.gatein.wci.api.GateInServlet</servlet-class>
    <load-on-startup>0</load-on-startup>
  </servlet>
  <!--  =================================================================  -->
  <servlet-mapping>
    <servlet-name>GateInServlet</servlet-name>
    <url-pattern>/gateinservlet</url-pattern>
  </servlet-mapping>
</web-app>

A portal extension is in fact a web application declared as a PortalContainerConfigOwner (see previous section for more details about a PortalContainerConfigOwner) that has been added to the dependency list of the PortalContainerDefinition of a given portal.

See below an example of configuration file that add the portal extension "portal-ext" to the dependency list of the portal "portal":

<?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 towards
                the other web applications and particularly towards "portal" -->
                <value>
                  <string>sample-ext</string>
                </value>
              </collection>
            </field>
          </object>
        </object-param>
      </init-params>
    </component-plugin>
  </external-component-plugins>
</configuration>

To duplicate the entire "portal.war" file to create a new portal, you just need to duplicate the following files from the original "portal.war":

You need also to duplicate the "rest.war" file to create a dedicated rest web application for your portal as we must have one rest web application per portal, in fact you just need to duplicate the following files from the original "rest.war":

Finally, you need to register and define the corresponding PortalContainerDefinition. The PortalContainerDefinition of your portal will be composed of:

See an example below:

<?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>sample-portal</name>
          <object type="org.exoplatform.container.definition.PortalContainerDefinition">
            <!-- The name of the portal container -->
            <field name="name"><string>sample-portal</string></field>
            <!-- The name of the context name of the rest web application -->
            <field name="restContextName"><string>rest-sample-portal</string></field>
            <!-- The name of the realm -->
            <field name="realmName"><string>exo-domain-sample-portal</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-sample-portal</string>
                </value>
                <value>
                  <string>web</string>
                </value>
                <value>
                  <string>wsrp-producer</string>
                </value>
                <value>
                  <string>sample-portal</string>
                </value>
              </collection>
            </field>
          </object>
        </object-param>
      </init-params>
    </component-plugin>
  </external-component-plugins>
</configuration>

Now, the ConfigurationManager uses by default the unified servlet context of the portal in order to get any resources in particular the configuration files. The unified servlet context is aware of the priorities that has been set in the PortalContainerDefinition of the portal. In other words, if you want for instance to import the file war:/conf/database/database-configuration.xml and this file exists in 2 different web applications, the file from the last (according to the dependency order) web application will be loaded.

So, in order to avoid issues when we would like to package several products at the same time (i.e. WCM, DMS, CS, KS), we need to:

The example below, is an the example of a file WEB-INF/conf/configuration.xml of the product "sample-ext".

<?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">
  <import>war:/conf/sample-ext/common/common-configuration.xml</import>
  <import>war:/conf/sample-ext/jcr/jcr-configuration.xml</import>
  <import>war:/conf/sample-ext/portal/portal-configuration.xml</import>
  <import>war:/conf/sample-ext/web/web-inf-extension-configuration.xml</import>
</configuration>

In your configuration file, you can use a special variable called container.name.suffix in order to add a suffix to values that could change between portal containers. The value of this variable will be an empty sting if no PortalContainerDefinition has been defined otherwise the value will be \-$portal.container.name. See an example below:

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

Now you can add new JCR repositories or workspaces thanks to an external plugin, the configuration of your JCR Repositories will be merged knowing that the merge algorithm will:

See an example of jcr-configuration.xml below:

<?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">
  <external-component-plugins>
    <!-- The full qualified name of the RepositoryServiceConfiguration -->
    <target-component>org.exoplatform.services.jcr.config.RepositoryServiceConfiguration</target-component>
    <component-plugin>
      <!-- The name of the plugin -->
      <name>Sample RepositoryServiceConfiguration Plugin</name>
      <!-- The name of the method to call on the RepositoryServiceConfiguration in order to add the RepositoryServiceConfigurations -->
      <set-method>addConfig</set-method>
      <!-- The full qualified name of the RepositoryServiceConfigurationPlugin -->
      <type>org.exoplatform.services.jcr.impl.config.RepositoryServiceConfigurationPlugin</type>
      <init-params>
        <value-param>
          <name>conf-path</name>
          <description>JCR configuration file</description>
          <value>war:/conf/sample-ext/jcr/repository-configuration.xml</value>
        </value-param>
      </init-params>
    </component-plugin>
  </external-component-plugins>
</configuration>

See an example of repository-configuration.xml below:

<repository-service default-repository="repository">
  <repositories>
    <repository name="repository" system-workspace="system" default-workspace="portal-system">
      <security-domain>exo-domain</security-domain>
      <access-control>optional</access-control>
      <authentication-policy>org.exoplatform.services.jcr.impl.core.access.JAASAuthenticator</authentication-policy>
      <workspaces>
        <workspace name="sample-ws">
          <container class="org.exoplatform.services.jcr.impl.storage.jdbc.JDBCWorkspaceDataContainer">
            <properties>
              <property name="source-name" value="jdbcexo${container.name.suffix}" />
              <property name="dialect" value="hsqldb" />
              <property name="multi-db" value="false" />
              <property name="update-storage" value="true" />
              <property name="max-buffer-size" value="204800" />
              <property name="swap-directory" value="../temp/swap/sample-ws${container.name.suffix}" />
            </properties>
            <value-storages>
              <value-storage id="sample-ws" class="org.exoplatform.services.jcr.impl.storage.value.fs.TreeFileValueStorage">
                <properties>
                  <property name="path" value="../temp/values/sample-ws${container.name.suffix}" />
                </properties>
                <filters>
                  <filter property-type="Binary" />
                </filters>
              </value-storage>
            </value-storages>
          </container>
          <initializer class="org.exoplatform.services.jcr.impl.core.ScratchWorkspaceInitializer">
            <properties>
              <property name="root-nodetype" value="nt:unstructured" />
              <property name="root-permissions"
                value="any read;*:/platform/administrators read;*:/platform/administrators add_node;*:/platform/administrators set_property;*:/platform/administrators remove" />
            </properties>
          </initializer>
          <cache enabled="true">
            <properties>
              <property name="max-size" value="20000" />
              <property name="live-time" value="30000" />
            </properties>
          </cache>
          <query-handler class="org.exoplatform.services.jcr.impl.core.query.lucene.SearchIndex">
            <properties>
              <property name="index-dir" value="../temp/jcrlucenedb/sample-ws${container.name.suffix}" />
            </properties>
          </query-handler>
          <lock-manager>
            <time-out>15m</time-out><!-- 15min -->
            <persister class="org.exoplatform.services.jcr.impl.core.lock.FileSystemLockPersister">
              <properties>
                <property name="path" value="../temp/lock/sample-ws${container.name.suffix}" />
              </properties>
            </persister>
          </lock-manager>
        </workspace>
      </workspaces>
    </repository>
  </repositories>
</repository-service>

Now you can add new Resource Bundles, thanks to an external plugin.

See an example below:

<?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">
  <external-component-plugins>
    <!-- The full qualified name of the ResourceBundleService -->
    <target-component>org.exoplatform.services.resources.ResourceBundleService</target-component>
    <component-plugin>
      <!-- The name of the plugin -->
      <name>Sample ResourceBundle Plugin</name>
      <!-- The name of the method to call on the ResourceBundleService in order to register the ResourceBundles -->
      <set-method>addResourceBundle</set-method>
      <!-- The full qualified name of the BaseResourceBundlePlugin -->
      <type>org.exoplatform.services.resources.impl.BaseResourceBundlePlugin</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.portal.sample</value>
        </values-param>
        <values-param>
          <name>portal.resource.names</name>
          <description>The properties files of the portal , those file will be merged
            into one ResoruceBundle properties </description>
          <value>locale.portal.sample</value>
        </values-param>
      </init-params>
    </component-plugin>
  </external-component-plugins>
</configuration>

Now each portal container has its own ClassLoader which is automatically set for you at runtime (FYI: it could be retrieved thanks to portalContainer.getPortalClassLoader()). This ClassLoader is an unified ClassLoader that is also aware of the dependency order defined into the PortalContainerDefinition, so to add new keys or update key values, you just need to:

In the example below, we want to change the values of the keys UIHomePagePortlet.Label.Username and UIHomePagePortlet.Label.Password, and add the new key UIHomePagePortlet.Label.SampleKey into the Resource Bundle locale.portal.webui.

Now you can add new Portal Configurations, Navigations, Pages or Portlet Preferences thanks to an external plugin.

See an example below:

<?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">
  <external-component-plugins>
    <!-- The full qualified name of the UserPortalConfigService -->
    <target-component>org.exoplatform.portal.config.UserPortalConfigService</target-component>
    <component-plugin>
      <!-- The name of the plugin -->
      <name>new.portal.config.user.listener</name>
      <!-- The name of the method to call on the UserPortalConfigService in order to register the NewPortalConfigs -->
      <set-method>initListener</set-method>
      <!-- The full qualified name of the NewPortalConfigListener -->
      <type>org.exoplatform.portal.config.NewPortalConfigListener</type>
      <description>this listener init the portal configuration</description>
      <init-params>
        <object-param>
          <name>portal.configuration</name>
          <description>description</description>
          <object type="org.exoplatform.portal.config.NewPortalConfig">
            <field name="predefinedOwner">
              <collection type="java.util.HashSet">
                <value>
                  <string>classic</string>
                </value>
              </collection>
            </field>
            <field name="ownerType">
              <string>portal</string>
            </field>
            <field name="templateLocation">
              <string>war:/conf/sample-ext/portal</string>
            </field>
          </object>
        </object-param>
        <object-param>
          <name>group.configuration</name>
          <description>description</description>
          <object type="org.exoplatform.portal.config.NewPortalConfig">
            <field name="predefinedOwner">
              <collection type="java.util.HashSet">
                <value>
                  <string>platform/users</string>
                </value>
              </collection>
            </field>
            <field name="ownerType">
              <string>group</string>
            </field>
            <field name="templateLocation">
              <string>war:/conf/sample-ext/portal</string>
            </field>
          </object>
        </object-param>
        <object-param>
          <name>user.configuration</name>
          <description>description</description>
          <object type="org.exoplatform.portal.config.NewPortalConfig">
            <field name="predefinedOwner">
              <collection type="java.util.HashSet">
                <value>
                  <string>root</string>
                </value>
              </collection>
            </field>
            <field name="ownerType">
              <string>user</string>
            </field>
            <field name="templateLocation">
              <string>war:/conf/sample-ext/portal</string>
            </field>
          </object>
        </object-param>
      </init-params>
    </component-plugin>
  </external-component-plugins>
</configuration>

We added a GenericFilter that allows you to define new Http Filters thanks to an external plugin. Your filter will need to implement the interface org.exoplatform.web.filter.Filter.

See an example of configuration below:

<?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 ExtensibleFilter -->
    <target-component>org.exoplatform.web.filter.ExtensibleFilter</target-component>
    <component-plugin>
      <!-- The name of the plugin -->
      <name>Sample Filter Definition Plugin</name>
      <!-- The name of the method to call on the ExtensibleFilter in order to register the FilterDefinitions -->
      <set-method>addFilterDefinitions</set-method>
      <!-- The full qualified name of the FilterDefinitionPlugin -->
      <type>org.exoplatform.web.filter.FilterDefinitionPlugin</type>
      <init-params>
        <object-param>
          <name>Sample Filter Definition</name>
          <object type="org.exoplatform.web.filter.FilterDefinition">
            <!-- The filter instance -->
            <field name="filter"><object type="org.exoplatform.sample.ext.web.SampleFilter"/></field>
            <!-- The mapping to use -->
            <!-- WARNING: the mapping is expressed with regular expressions -->
            <field name="patterns">
              <collection type="java.util.ArrayList" item-type="java.lang.String">
                <value>
                  <string>/.*</string>
                </value>
              </collection>
            </field>
          </object>
        </object-param>
      </init-params>
    </component-plugin>
  </external-component-plugins>
</configuration>

See an example of Filter below:

We added a GenericHttpListener that allows you to define new HttpSessionListeners and/or ServletContextListeners thanks to an external plugin. Actually, the GenericHttpListener will broadcast events thanks to the ListenerService that you can easily capture. The events that it broadcasts are:

If you want to listen to org.exoplatform.web.GenericHttpListener.sessionCreated, you will need to create a Listener that extends _Listener<PortalContainer, HttpSessionEvent>_If you want to listen to \_org.exoplatform.web.GenericHttpListener.sessionDestroyed_, you will need to create a Listener that extends _Listener<PortalContainer, HttpSessionEvent>_If you want to listen to \_org.exoplatform.web.GenericHttpListener.contextInitialized_, you will need to create a Listener that extends _Listener<PortalContainer, ServletContextEvent>_If you want to listen to \_org.exoplatform.web.GenericHttpListener.contextDestroyed_, you will need to create a Listener that extends Listener<PortalContainer, ServletContextEvent>

See an example of configuration below:

<?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 ListenerService -->
    <target-component>org.exoplatform.services.listener.ListenerService</target-component>
    <component-plugin>
      <!-- The name of the listener that is also the name of the target event -->
      <name>org.exoplatform.web.GenericHttpListener.sessionCreated</name>
      <!-- The name of the method to call on the ListenerService in order to register the Listener -->
      <set-method>addListener</set-method>
      <!-- The full qualified name of the Listener -->
      <type>org.exoplatform.sample.ext.web.SampleHttpSessionCreatedListener</type>
    </component-plugin>
    <component-plugin>
      <!-- The name of the listener that is also the name of the target event -->
      <name>org.exoplatform.web.GenericHttpListener.sessionDestroyed</name>
      <!-- The name of the method to call on the ListenerService in order to register the Listener -->
      <set-method>addListener</set-method>
      <!-- The full qualified name of the Listener -->
      <type>org.exoplatform.sample.ext.web.SampleHttpSessionDestroyedListener</type>
    </component-plugin>
    <component-plugin>
      <!-- The name of the listener that is also the name of the target event -->
      <name>org.exoplatform.web.GenericHttpListener.contextInitialized</name>
      <!-- The name of the method to call on the ListenerService in order to register the Listener -->
      <set-method>addListener</set-method>
      <!-- The full qualified name of the Listener -->
      <type>org.exoplatform.sample.ext.web.SampleContextInitializedListener</type>
    </component-plugin>
    <component-plugin>
      <!-- The name of the listener that is also the name of the target event -->
      <name>org.exoplatform.web.GenericHttpListener.contextDestroyed</name>
      <!-- The name of the method to call on the ListenerService in order to register the Listener -->
      <set-method>addListener</set-method>
      <!-- The full qualified name of the Listener -->
      <type>org.exoplatform.sample.ext.web.SampleContextDestroyedListener</type>
    </component-plugin>
  </external-component-plugins>
</configuration>

See an example of Session Listener below:

See an example of Context Listener below:

We assume that you have a clean JBoss version of GateIn, in other words, we assume that you have already the file exoplatform.ear in the deploy directory of JBoss and you have the related application policy in your conf/login-config.xml.

You need to:

We assume that you have a clean JBoss version of GateIn, in other words, we assume that you have already the file exoplatform.ear in the deploy directory of JBoss and you have the related application policy in your conf/login-config.xml.

You need to:

  <application-policy name="exo-domain-sample-portal">
    <authentication>
      <login-module code="org.exoplatform.web.security.PortalLoginModule" flag="required">
        <module-option name="portalContainerName">sample-portal</module-option>
        <module-option name="realmName">exo-domain-sample-portal</module-option>
      </login-module>
      <login-module code="org.exoplatform.services.security.jaas.SharedStateLoginModule" flag="required">
        <module-option name="portalContainerName">sample-portal</module-option>
        <module-option name="realmName">exo-domain-sample-portal</module-option>
      </login-module>
      <login-module code="org.exoplatform.services.security.j2ee.JbossLoginModule" flag="required">
        <module-option name="portalContainerName">sample-portal</module-option>
        <module-option name="realmName">exo-domain-sample-portal</module-option>
      </login-module>
    </authentication>
  </application-policy>

We assume that you have a clean Tomcat version of GateIn, in other words, we assume that you have already all the jar files of GateIn and their dependencies into tomcat/lib, you have all the war files of GateIn into tomcat/webapps and you have the realm name "exo-domain" defined into the file tomcat/conf/jaas.conf.