Portal containers

Version 1

    Work in progress

    Should move to the reference guide, published here "as-is" as it may give hints to some advanced developers.

     

     

    {toc:style=disc|indent=20px} h1. Introduction h2. Overview Since GateIn beta 2, we added a set of features in order to customize a GateIn instance without modifying the GateIn binary, this usecase will be called _portal extension_ in this documentation. Those features are also required to be able to launch several portal instances at the same time, that means to have several "portal.war". h2. Motivations Up to now, to create an application over a portal, we need to modify files into the "portal.war". It is handy to be able to extend the portal without having (or limitating the need) to modify the shipped-in portal. h1. Prerequisites 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. h2. Remove all the hard coded portal container name (i.e. "portal") Now if we need to get the portal container name (even in a standalone mode: in case of standalone mode the default value will be returned), we can: * If the component is instantiated by Pico container, you can add in the constructor of your component, the component _ExoContainerContext_, then call the method _getPortalContainerName()_ * If the component is not instantiated by Pico container, you can call at runtime the static method _PortalContainer.getCurrentPortalContainerName()_ * In js files, you can use the variable _currentContext_ if your script must be loaded before the variable _eXo.env.server.context_, otherwise use _eXo.env.server.context_ instead. * In jsp files, you can use _request.getContextPath()_. h2. Remove all the hard coded rest context name (i.e. "rest") Now if we need to get the rest context name (even in a standalone mode: in case of standalone mode the default value will be returned), we can: * If the component is instantiated by Pico container, you can add in the constructor of your component, the component _ExoContainerContext_, then call the method _getRestContextName()_ * If the component is not instantiated by Pico container, you can call at runtime the static method _PortalContainer.getCurrentRestContextName()_ h2. Remove all the hard coded realm name (i.e. "exo-domain") Now if we need to get the realm name (even in a standalone mode: in case of standalone mode the default value will be returned), we can: * If the component is instantiated by Pico container, you can add in the constructor of your component, the component _ExoContainerContext_, then call the method _getRealmName()_ * If the component is not instantiated by Pico container, you can call at runtime the static method _PortalContainer.getCurrentRealmName()_ h2. Make your Http Filters compatible Now all your Http Filters that need to get the current _ExoContainer_ must extends _org.exoplatform.container.web.AbstractFilter_. You just need to call the method _getContainer()_ to get the current _ExoContainer_. h2. Make your HttpServlets compatible 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. {info:title=Useful Information}In the class _org.exoplatform.container.web.AbstractHttpServlet_ you have a method called _requirePortalEnvironment()_ that is used to indicate that we would like the abstract class to setup or not the full portal environment (_PortalContainer_, _ClassLoader_ and _ServletContext_) before executing the servlet. This value should return true when the servlet is executed within the web application of a portal container. By default, it checks if the name of the current _ServletContext_ is a portal container name, it is sufficient in most cases but you can still overload this method if you already know that the servlet will always been executed within the web application of portal container (i.e. the method always return true) or will never be executed within the web application of a portal container (i.e. the method always return false) . {info} h2. Make your HttpSessionListeners compatible Now all your HttpSessionListeners that need to get the current _ExoContainer_ must extends _org.exoplatform.container.web.AbstractHttpSessionListener_. This abstract class will give you the current _ExoContainer_ directly in the signature of the methods to implement which are _ onSessionCreated(ExoContainer container, HttpSessionEvent event)\_ and _onSessionDestroyed(ExoContainer container, HttpSessionEvent event)_ You will also need to implement the method called _requirePortalEnvironment()_ that is used to indicate that we would like the abstract class to setup or not the full portal environment (_PortalContainer_ and _ClassLoader_) before processing the event. This value should return true when the event is processed within the web application of a portal container. h2. Use init tasks if you need a PortalContainer to initialize an Http Filter or an HttpServlet 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: * The _org.exoplatform.container.RootContainer.PortalContainerPreInitTask_ which are executed before the portal container has been initialized * The _org.exoplatform.container.RootContainer.PortalContainerPostInitTask_ which are executed after the portal container has been initialized * The _org.exoplatform.container.RootContainer.PortalContainerPostCreateTask_ which are executed after the portal container has been fully created (i.e. after all the post init tasks). A init task is defined as below {code:title=PortalContainerPreInitTask|borderStyle=solid} /** * This interface is used to define a task that needs to be launched at a given state during the * initialization of a portal container */ public static interface PortalContainerInitTask { /** * This method allows the implementation to define what the state "already exists" * means for a portal container * * @param portalContainer the value of the current portal container * @return <code>true</code> if the portal container exists according to the task * requirements, <code>false</code> otherwise */ public boolean alreadyExists(PortalContainer portalContainer); /** * This method is called if the related portal container has already been registered * * @param context the servlet context of the web application * @param portalContainer the value of the current portal container */ public void onAlreadyExists(ServletContext context, PortalContainer portalContainer); /** * Executes the task * * @param context the servlet context of the web application * @param container The portal container on which we would like to execute the task */ public void execute(ServletContext context, PortalContainer portalContainer); /** * @return the type of the task */ public String getType(); } {code} To add a task you can either call: * _PortalContainer.addInitTask(ServletContext context, PortalContainerInitTask task)_ in order to execute the task on all the portal containers that depend on the given _ServletContext_ according to the _PortalContainerDefinitions_. * _PortalContainer.addInitTask(ServletContext context, PortalContainerInitTask task, String portalContainerName)_ in order to execute the task on a given portal container. * _RootContainer.addInitTask(ServletContext context, PortalContainerInitTask task)_ in order to execute the task on the portal container which name is the name of the given _ServletContext_. 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:+ {code:title=Old GadgetRegister.java|borderStyle=solid}... public class GadgetRegister implements ServletContextListener { ... public void contextInitialized(ServletContextEvent event) { try { ExoContainer pcontainer = ExoContainerContext.getContainerByName("portal") ; SourceStorage sourceStorage = (SourceStorage)pcontainer.getComponentInstanceOfType(SourceStorage.class); ... } ... } {code} The new code relies on a _org.exoplatform.container.RootContainer.PortalContainerPostInitTask_, as you can see below {code:title=New GadgetRegister.java|borderStyle=solid}... 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) { contextInitialized(context, portalContainer); } }; // Add the init task to all the related portal containers PortalContainer.addInitTask(event.getServletContext(), task); } private void contextInitialized(ServletContext context, PortalContainer pcontainer) { try { SourceStorage sourceStorage = (SourceStorage)pcontainer.getComponentInstanceOfType(SourceStorage.class); ... } ... } {code} h2. Make your LoginModules compatible Now all your LoginModules that need to get the current _ExoContainer_ must extends _org.exoplatform.services.security.jaas.AbstractLoginModule_. You just need to call the method _getContainer()_ to get the current _ExoContainer_. {info:title=Useful Information}The class _org.exoplatform.services.security.jaas.AbstractLoginModule_ supports 2 login module options which are _portalContainerName_ and _realmName_, to allow you to indicate the realm name and the portal container name, if you want to change the default value. {info} h2. Avoid _static_ modifier on component dependency A local variable that stores a component dependency must not be static. In other words, when you create a component A that depends on component B, we don't store B in a static variable of A otherwise we cannot have several different instances of A in the same JVM which is not compatible with a multi-portal instance. h2. Avoid component initialization based on component dependency in the constructor We will have more and more extensible components (i.e. that can be extended thanks to an external plugin) which means that those components can only be initialized in the _start_ method, thus it is not a good practice to initialize a component in its constructor if this initialization uses other components because those components may not be initialized. For example, now the ResourceBundleService is extensible, so if you create a component that depends on the ResourceBundleService and you need the ResourceBundleService to initialize your component, your component will need to be "Startable" and you will have to initialize your component in a _start_ method. h1. FAQ h2. What is the main purpose of a _portal extension_? An _extension_ is just a set of files that we use to customize or add new features to a given portal. An extension must be the least intrusive as possible, as we could potentially have several extensions for the same portal. In other words we are supposed to only add what is missing in the portal and avoid to change or duplicate files that are in the portal.war. h2. What is the main purpose of the _starter_? The _starter_ is a web application that has been added to create and start all the portals (i.e. portal containers) at the same time when all the other web applications have already been started. In fact all other web applications can potentially defined several things at startup such as skins, javascripts, google gadgets and configuration files, thus the loading order is important as we can redefine skins or configuration files or a javascript from a web application 1 could depend on another javascript from a web application 2 so if the web application 2 is loaded after the web application 1, we will get errors in the merged javascript file. If a _PortalContainerDefinition_ has been defined, the loading order will be the order that has been used to define the list of dependency. And if you defined a _PortalContainerDefinition_ you need to deploy the starter otherwise the loading order will be the default one (i.e. the loading order of the Application Server) So if you need to customize your portal by adding a new extension and/or a new portal, you need to defined the related _PortalContainerDefinitions_ so you need to deploy also the starter. Otherwise, you don't need to define any _PortalContainerDefinition_ and to deploy the _starter_. h2. How a portal and a portal container are related? Each portal instance has its own portal container which allows the portal to have its own set of components/services. It will ensure the isolation between the different portal instances. h2. How to define and register a _PortalContainerDefinition_? 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: * The name of the portal container * The name of the context name of the rest web application * The name of the realm * The list of all the dependencies of the portal container ordered by priority 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 wiki article [http://wiki.exoplatform.com/xwiki/bin/view/Kernel/Container+Configuration]). +See below an example of configuration file that define and register a PortalContainerDefinition:+ {code:xml}<?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> {code} 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. h2. How the platform interprets the dependency order defined into the PortalContainerDefinition? The dependency order defined into the _PortalContainerDefinition_ is really crucial since it will be interpreted the same way by several components of the platform. All those components, will consider the 1st element in the list is less important than the second element and so on. +So it is currently used to:+ * know loading order of all the dependencies * If we have several _PortalContainerConfigOwner_ (see next section for more details about a PortalContainerConfigOwner) ** The ServletContext of all the _PortalContainerConfigOwner_ will be unified, if we use the unified ServletContext (PortalContainer.getPortalContext()) to get a resource, it will try to get the resource in the ServletContext of the most important _PortalContainerConfigOwner_ (i.e. last in the dependency list) and if it cans find it, it will try with the second most important _PortalContainerConfigOwner_ and so on. ** The ClassLoader of all the _PortalContainerConfigOwner_ will be unified, if we use the unified ClassLoader (PortalContainer.getPortalClassLoader()) to get a resource, it will try to get the resource in the ClassLoader of the most important _PortalContainerConfigOwner_ (i.e. last in the dependency list) and if it cans find it, it will try with the second most important _PortalContainerConfigOwner_ and so on. h2. How to change the ServletContext name, the realm name and/or the rest context name of my portal without using a PortalContainerDefinition? 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 wiki article [http://wiki.exoplatform.com/xwiki/bin/view/Kernel/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+ {code:xml}<?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> {code} {+}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 h3. On JBoss (tested on JBoss 5.1.0.GA) You need to change the name of the application policy in your file _conf/login-config.xml_ (the new name is "my-exo-domain"). h3. On Tomcat (tested on Tomcat 6.0.20) +You need to:+ * Update the file _tomcat/conf/Catalina/localhost/portal.xml_ by changing the "path" (the new value is "/myPortal"), the "docBase" (the new value is "myPortal") and the "appName" in the "Realm" definition (the new value is "my-exo-domain"). * Rename the file _tomcat/conf/Catalina/localhost/portal.xml_ to _myPortal.xml_. * Update the file _tomcat/conf/Catalina/localhost/rest.xml_ by changing the "path" (the new value is "/myRest"), the "docBase" (the new value is "myRest") and the "appName" in the "Realm" definition (the new value is "my-exo-domain"). * Rename the file _tomcat/conf/Catalina/localhost/rest.xml_ to _myRest.xml_. * Change the realm name in the file _tomcat/conf/jaas.conf_ (the new name is "my-exo-domain"). h2. How to add new configuration file to a given portal from a war file? To indicate the platform that a given web application has configuration file to provide, you need to: * add the ServletContextListener _org.exoplatform.container.web.PortalContainerConfigOwner_ in its web.xml. * add the servlet context name of this web application as a new dependency in the _PortalContainerDefinition_ of all the portal containers for which you want to share the configuration file embedded into the war file, located at _WEB-INF/conf/configuration.xml_. 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_. {info:title=Useful Information #1}The position of the servlet context name of this web application in the dependency list is important since the last configuration file loaded has always right towards other configuration files. Each configuration file loaded, could potentially redefine a configuration file that has already been loaded. Moreover, as we now use a unified Servlet Context to load the configuration files, 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. {info} {info:title=Useful Information #2}A portal is implicitly considered as a _PortalContainerConfigOwner_ without having to define the ServletContextListener _org.exoplatform.container.web.PortalContainerConfigOwner_ in its web.xml. {info}{+}See an example of a web.xml below:+ {code:xml}<?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> {code} h2. How to create/define a portal extension? 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":+ {code:xml}<?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> {code} h2. How to deploy a portal extension? Refer to _How to deploy the sample extension?_ h2. How to create/define a new portal? You have no need anymore, to duplicate the entire "portal.war" file to create a new portal, you just to duplicate the following files from the original "portal.war": * login/jsp/login.jsp * login/skin: You can customize the css files and pictures * bookmark.jsp * favicon.ico: You can replace it by your own logo * index.jsp * portal-unavailable.jsp * portal-warning.jsp * WEB-INF/web.xml: You just need to change the "display-name" and set a different value for the "realm-name" in the "login-config". Indeed, we must have one realm name per portal. * WEB-INF/jboss-web.xml: If you use JBoss AS, you need to duplicate also this file and set the new "security-domain" with the new realm name. 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": * WEB-INF/web.xml: You just need to change the "display-name" and set a different value for the "realm-name" in the "login-config". Indeed, we must have one realm name per portal. * WEB-INF/jboss-web.xml: If you use JBoss AS, you need to duplicate also this file and set the new "security-domain" with the new realm name. Finally you need to register and define the corresponding _PortalContainerDefinition_. The _PortalContainerDefinition_ of your portal will be composed of: * The name of new portal * The name of the context name of the new rest web application * The name of the new realm * The list of all the dependencies of the original portal, with the new name of the rest web application instead of the old name (i.e. "rest") and with a new dependency which is in fact the name of your portal. As we leave the dependency of the original portal in the list of dependencies, it will load the configuration files of original "portal.war" file. +See an example below:+ {code:xml}<?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> {code} {info:title=Useful Information #1}A portal is implicitly a _PortalContainerConfigOwner_ which means that it shares the configuration file embedded into the war file, located at _WEB-INF/conf/configuration.xml_ {info} {info:title=Useful Information #2}The position of the servlet context name of this web application in the dependency list is important since the last configuration file loaded has always right towards other configuration files. Each configuration file loaded, could potentially redefine a configuration file that has already been loaded. Moreover, as we now use a unified Servlet Context to load the configuration files, 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. {info} h2. How to deploy a new portal? Refer to _How to deploy the sample portal?_ h2. How to import properly a configuration file using the prefix "war:"? 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: * Avoid the best you can to redefine a configuration file from the "portal.war" by using the exact same path (like the previous example) * Add your configuration files in a dedicated folder which name will be the name of the product, in oder to ensure that no other products will use the same path The example below, is an the example of a file _WEB-INF/conf/configuration.xml_ of the product "sample-ext". {code:xml}<?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> {code} h2. How to avoid duplicating configuration files just to rename a simple value? 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:+ {code:xml}<?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> {code} h2. How to add or change a Repository and/or a Workspace? 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: * add missing Repository Definitions and/or Workspace Definitions. * change the properties of a Repository Definition if it has already been defined * replace the Workspace Definition if it has already been defined. +See an example of jcr-configuration.xml below:+ {code:xml}<?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> {code} {+}See an example of repository-configuration.xml below:+ {code:xml}<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> {code} {warning:title=Warning}If you have to change the default repository or the default workspace, please be careful since it could cause side effects as you could be incompatible with some portal configuration files that refer explicitly to _portal-system_ and/or to _repository_. To solve this problem you can either redefine the configuration file in a portal extension to change the workspace name and/or the repository name or you could also add your own repository instead of your own workspace. {warning} h2. How to add new ResourceBundles to my portal? Now you can add new Resource Bundles, thanks to an external plugin. +See an example below:+ {code:xml}<?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> {code} h2. How to overwrite existing ResourceBundles in my portal? 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: * Add the corresponding resource bundle with the same name, with +the same extension+ (xml or properties) at the same location into the _classpath_ of your Web application (i.e. directly into the _WEB-INF/classes_ directory or into a jar file in the _WEB-INF/lib_ directory) * Ensure that your web application is defined after the web application of the portal in the dependency list of the related _PortalContainerDefinition_. 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_. {code:title=WEB-INF/classes/local/portal/webui_en.properties|borderStyle=solid}############################################################################# #org.exoplatform.portal.webui.component.UIHomePagePortlet      # ############################################################################# UIHomePagePortlet.Label.Username=Usr: UIHomePagePortlet.Label.Password=Pwd: UIHomePagePortlet.Label.SampleKey=This is a new key that has been added to the Resource Bundle "locale.portal.webui" of "sample-ext" {code} h2. How to replace a groovy template of my portal? Now each portal container has its own _ServletContext_ which is automatically set for you at runtime (FYI: it could be retrieved thanks to _portalContainer.getPortalContext()_). This _ServletContext_ is an unified _ServletContext_ that is also aware of the dependency order defined into the _PortalContainerDefinition_ so to replace a groovy template of the portal, you just need to: * Add the corresponding groovy template with the same name at the same location into your Web application * Ensure that your web application is defined after the web application of the portal in the dependency list of the related _PortalContainerDefinition_. h2. How to add new Portal Configurations, Navigations, Pages or Portlet Preferences to my portal? Now you can add new Portal Configurations, Navigations, Pages or Portlet Preferences thanks to an external plugin. +See an example below:+ {code:xml}<?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> {code} h2. How to add new Http Filters to my portal without modifying the portal binary? 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:+ {code:xml}<?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> {code} {+}See an example of Filter below:+ {code:title=SampleFilter.java|borderStyle=solid}... import org.exoplatform.web.filter.Filter; import java.io.IOException; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; public class SampleFilter implements Filter { public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.out.println("SampleFilter start"); try { chain.doFilter(request, response); } finally { System.out.println("SampleFilter end"); } } } {code} h2. How to add new _HttpSessionListeners_ and/or _ServletContextListeners_ to my portal without modifying the portal binary? 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: * _org.exoplatform.web.GenericHttpListener.sessionCreated_: When a new session is created in the portal. * _org.exoplatform.web.GenericHttpListener.sessionDestroyed_: When a session is destroyed in the portal. * _org.exoplatform.web.GenericHttpListener.contextInitialized_: When the servlet context of the portal is initialized. * _org.exoplatform.web.GenericHttpListener.contextDestroyed_: When the servlet context of the portal is destroyed. 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:+ {code:xml}<?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> {code} {+}See an example of Session Listener below:+ {code:title=SampleHttpSessionCreatedListener.java|borderStyle=solid}.. import org.exoplatform.container.PortalContainer; import org.exoplatform.services.listener.Event; import org.exoplatform.services.listener.Listener; import javax.servlet.http.HttpSessionEvent; public class SampleHttpSessionCreatedListener extends Listener<PortalContainer, HttpSessionEvent> { @Override public void onEvent(Event<PortalContainer, HttpSessionEvent> event) throws Exception { System.out.println("Creating a new session"); } } {code} {+}See an example of Context Listener below:+ {code:title=SampleContextInitializedListener.java|borderStyle=solid}.. import org.exoplatform.container.PortalContainer; import org.exoplatform.services.listener.Event; import org.exoplatform.services.listener.Listener; import javax.servlet.ServletContextEvent; public class SampleContextInitializedListener extends Listener<PortalContainer, ServletContextEvent> { @Override public void onEvent(Event<PortalContainer, ServletContextEvent> event) throws Exception { System.out.println("Initializing the context"); } } {code} h2. How to add new _HttpServlet_ to my portal without modifying the portal binary? Actually, it is not possible but you can create a rest component instead. For more details about rest, please refer to the following wiki article [http://wiki.exoplatform.com/xwiki/bin/view/WS]. h2. How to override or add a Context Parameter to my portal without modifying the portal binary? Actually, you have nothing to do, you just need to ensure that you get the context parameter value from the unified servlet context of the portal, that should be set for you but you can still get it from _portalContainer.getPortalContext()_. h2. Where can I found an example of how to extend my portal? We added an example of portal extension (i.e. ability to customize a portal without changing anything in the portal.ear) that you can find in the svn of gatein at gatein/sample/extension. h2. How to deploy the sample extension? h3. On JBoss (tested on JBoss 5.1.0.GA) 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:+ * Add the file _sample-ext.ear_ from sample/extension/ear/target/ to the deploy directory of JBoss, this file contains: ** The file _sample-ext.war_ which is the web application that contains (potentially) configuration files, groovy templates, resource bundles, skins and javascript files, that will extend the portal. ** The file _exo.portal.sample.extension.config-X.Y.Z.jar_ which is the file in which we defined the _PortalContainerDefinition_ of the original portal in which we added _sample-ext_ at the end of dependency list. ** The file _exo.portal.sample.extension.jar-X.Y.Z.jar_ which is the file in which we have internal classes that are actualy a set of sample classes _(SampleFilter, SampleContextInitializedListener, SampleContextDestroyedListener, SampleHttpSessionCreatedListener and SampleHttpSessionDestroyedListener)_ * Add the file _starter.ear_ from starter/ear/target/ to the deploy directory of JBoss, this file contains: ** The file _starter.war_ which is the web application that will create and start all the portal containers, that is why it must be launched after all the other web applications. {warning:title=Warning #1}This can only work if a Unified ClassLoader has been configured on your JBoss (default behavior) and the load order is first the _exoplatform.ear_ then the _sample-ext.ear_ and finally the _starter.ear_. {warning} {warning:title=Warning #2}The file _starter.ear_ must always been started last. {warning} h3. On Tomcat (tested on Tomcat 6.0.20) 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_. * Add the file _sample-ext.war_ from sample/extension/war/target/ to the _tomcat/webapps_ directory. _(See the purpose of this file in the JBoss section)_. * Add the folder _starter_ from starter/war/target/ to the _tomcat/webapps_ directory. _(See the purpose of this file in the JBoss section)_. * Rename the directory (unzipped folder) _starter_ to _starter.war_ (for more details see the warning below) * Add the jar file _exo.portal.sample.extension.config-X.Y.Z.jar_ from sample/extension/config/target/ to the _tomcat/lib_ directory. _(See the purpose of this file in the JBoss section)_. * Add the jar file _exo.portal.sample.extension.jar-X.Y.Z.jar_ from sample/extension/jar/target/ to the _tomcat/lib_ directory. _(See the purpose of this file in the JBoss section)_. {warning:title=Warning}This can only work if the _starter.war_ is the last war file to be loaded, so don't hesitate to rename it if your war files are loaded following to the alphabetic order. {warning} h2. Where can I found an example of how to create a new portal? We added an example of new portal (i.e. ability to create a new portal from another portal without changing anything in the portal.ear) that you can find in the svn of gatein at gatein/sample/portal. h2. How to deploy the sample portal? h3. On JBoss (tested on JBoss 5.1.0.GA) 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:+ * Add the file _sample-portal.ear_ from _sample/portal/ear/target/_ to the deploy directory of JBoss, this file contains: ** The file _sample-portal.war_ which is the entry point of the new portal ** The file _rest-sample-portal.war_ which is the entry point for _rest_ outside the portal (in the portal you can access to _rest_ thanks to path prefix _/sample-portal/rest_) ** The file _exo.portal.sample.portal.config-X.Y.Z.jar_ which is the file in which we defined the _PortalContainerDefinition_ of this portal ** The file _exo.portal.sample.portal.jar-X.Y.Z.jar_ which is the file in which we have internal classes that are actualy a set of sample classes _(SampleFilter, SampleContextInitializedListener, SampleContextDestroyedListener, SampleHttpSessionCreatedListener and SampleHttpSessionDestroyedListener)_ * Add the file _starter.ear_ from starter/ear/target/ to the deploy directory of JBoss, this file contains: ** The file _starter.war_ which is the web application that will create and start all the portal containers, that is why it must be launched after all the other web applications. * Define the related application policy in your file _conf/login-config.xml_, as below: {code:xml} <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> {code} {warning:title=Warning #1}This can only work if a Unified ClassLoader has been configured on your JBoss (default behavior) and the load order is first the _exoplatform.ear_ then the _sample-portal.ear_ and finally the _starter.ear_. {warning} {warning:title=Warning #2}The file _starter.ear_ must always been started last. {warning} h3. On Tomcat (tested on Tomcat 6.0.20) 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_. * Add the file _sample-portal.war_ from sample/portal/war/target/ to the _tomcat/webapps_ directory. _(See the purpose of this file in the JBoss section)_. * Add the file _rest-sample-portal.war_ from sample/portal/rest-war/target/ to the _tomcat/webapps_ directory. _(See the purpose of this file in the JBoss section)_. * Add the folder _starter_ from starter/war/target/ to the _tomcat/webapps_ directory. _(See the purpose of this file in the JBoss section)_. * Rename the directory (unzipped folder) _starter_ to _starter.war_ _(for more details see the warning below)_. * Add the jar file _exo.portal.sample.portal.config-X.Y.Z.jar_ from sample/portal/config/target/ to the _tomcat/lib_ directory. _(See the purpose of this file in the JBoss section)_. * Add the jar file _exo.portal.sample.portal.jar-X.Y.Z.jar_ from sample/portal/jar/target/ to the _tomcat/lib_ directory. _(See the purpose of this file in the JBoss section)_. * Define the related realm in your file _tomcat/conf/jaas.conf_, as below: {code:title=tomcat/conf/jaas.conf|borderStyle=solid}... exo-domain-sample-portal { org.exoplatform.web.security.PortalLoginModule required portalContainerName="sample-portal" realmName="exo-domain-sample-portal"; org.exoplatform.services.security.jaas.SharedStateLoginModule required portalContainerName="sample-portal" realmName="exo-domain-sample-portal"; org.exoplatform.services.security.j2ee.TomcatLoginModule required portalContainerName="sample-portal" realmName="exo-domain-sample-portal"; }; {code} * Define the context of _sample-portal_ by creating a file called _sample-portal.xml_ into _tomcat/conf/Catalina/localhost/_ with the following content: {code:title=tomcat/conf/Catalina/localhost/sample-portal.xml|borderStyle=solid}<Context path='/sample-portal' docBase='sample-portal' debug='0' reloadable='true' crossContext='true' privileged='true'> <Logger className='org.apache.catalina.logger.SystemOutLogger' prefix='localhost_portal_log.' suffix='.txt' timestamp='true'/> <Manager className='org.apache.catalina.session.PersistentManager' saveOnRestart='false'/> <Realm className='org.apache.catalina.realm.JAASRealm' appName='exo-domain-sample-portal' userClassNames='org.exoplatform.services.security.jaas.UserPrincipal' roleClassNames='org.exoplatform.services.security.jaas.RolePrincipal' debug='0' cache='false'/> <Valve className='org.apache.catalina.authenticator.FormAuthenticator' characterEncoding='UTF-8'/></Context> {code} * Define the context of _rest-sample-portal_ by creating a file called _rest-sample-portal.xml_ into _tomcat/conf/Catalina/localhost/_ with the following content: {code:title=tomcat/conf/Catalina/localhost/rest-sample-portal.xml|borderStyle=solid}<Context path="/rest-sample-portal" docBase="rest-sample-portal" reloadable="true" crossContext="false"> <Logger className='org.apache.catalina.logger.SystemOutLogger' prefix='localhost_portal_log.' suffix='.txt' timestamp='true'/> <Manager className='org.apache.catalina.session.PersistentManager' saveOnRestart='false'/> <Realm className='org.apache.catalina.realm.JAASRealm' appName='exo-domain-sample-portal' userClassNames="org.exoplatform.services.security.jaas.UserPrincipal" roleClassNames="org.exoplatform.services.security.jaas.RolePrincipal" debug='0' cache='false'/> </Context> {code} {warning:title=Warning}This can only work if the _starter.war_ is the last war file to be loaded, so don't hesitate to rename it if your war files are loaded following to the alphabetic order. {warning} h2. 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? To fox this issue you need to check if: # The file _starter-gatein.ear_ has been deployed # The file _starter-gatein.ear_ is the last ear file to be launched h1. Recommendations h2. Don't ship your configuration files with your jar files? Remove all the configuration files from the jar files (_conf/configuration.xml_ and _conf/portal/configuration.xml_) and move them to the war file of your extension, otherwise your configuration files will be loaded for all the portal containers which could cause incompatibility issues with other portals. Each extension should manage independently, its css files, js files, google gadgets and configuration files. If you add configuration files into the jar files of your extension, you brake this law. h2. Use a dedicated workspace/repository for your extension? In order to avoid conflicts with other extensions and to manage each extension independently, it is highly recommended to use a dedicated workspace or repository per extension.