6.7. Running multiple portals

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

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

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

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

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

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

/**

   * Use this method instead of Servlet.service()
   */
  protected void onService(ExoContainer container, HttpServletRequest req,
HttpServletResponse res) throws ServletException, IOException;

Note

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

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

/**

   * Use this method instead of HttpSessionListener.sessionCreated()
   */
  protected void onSessionCreated(ExoContainer container, HttpSessionEvent event);
  /**
   * Use this method instead of HttpSessionListener.sessionDestroyed()
   */
protected void onSessionDestroyed(ExoContainer container, HttpSessionEvent event);

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

/**

   * Method should return true if unified servlet context,
   * and unified classloader should be made available
   */
protected boolean requirePortalEnvironment();

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

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

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

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

public class GadgetRegister implements ServletContextListener

  {
     public void contextInitialized(ServletContextEvent event)
     {
        // Create a new post-init task
        final PortalContainerPostInitTask task = new PortalContainerPostInitTask() {
           public void execute(ServletContext context, PortalContainer portalContainer)
           {
              try
              {
                 SourceStorage sourceStorage =
                 (SourceStorage) portalContainer.getComponentInstanceOfType(SourceStorage.class);
                 ...
              }
              catch (RuntimeException e)
              {
                 throw e;
              }
              catch (Exception e)
              {
                 throw new RuntimeException("Initialization failed: ", e);
              }
           }
        };
        // Add post-init task for execution on all the portal containers
        // that depend on the given ServletContext according to
        // PortalContainerDefinitions (via Dependencies configuration)
        PortalContainer.addInitTask(event.getServletContext(), task);
     }}

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

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

See also

Copyright ©. All rights reserved. eXo Platform SAS
blog comments powered byDisqus