JBoss.orgCommunity Documentation
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 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().
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()
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()
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.
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.
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) .
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.
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
/** * 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(); }
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:
... public class GadgetRegister implements ServletContextListener { ... public void contextInitialized(ServletContextEvent event) { try { ExoContainer pcontainer = ExoContainerContext.getContainerByName("portal") ; SourceStorage sourceStorage = (SourceStorage)pcontainer.getComponentInstanceOfType(SourceStorage.class); ... } ... }
The new code relies on a org.exoplatform.container.RootContainer.PortalContainerPostInitTask, as you can see below
... 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); ... } ... }
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.
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.
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.
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.