Sponsored Links


Resources

Enterprise Java
Research Library

Get Java white papers, product information, case studies and webcasts

What's New in the eXo Platform 1.0 beta 4


December 2003

Discuss this Article


Introduction

Since our September article, many things have changed. The number of developers and users have dramatically increased. Big companies have already moved their development environment to the eXo platform and smaller companies are about to use it in production.

By using an open source model we target developers and provide them with many features that make their everyday life easier. Our strategy clearly defines a bottom-up approach.

With this new version, we go even further and prove our compliance with the portlet API specification defined by the Java Community Process, in order to convince and assure managers and decision makers that using the eXo platform is a safe choice.

Furthermore, we maintain our efforts to ease developers’ lives and this new version comes with many new features and services as well as an eclipse plug in.

Non techie part

Compliance

As soon as the specifications were final, we contacted Sun Microsystems for the Technology Compatibility Kit (TCK) software. This tool is a test suite composed of 372 tests.

The concept of the TCK is relatively simple. The licensee has to deploy several portlet application (WARs) in its portal / portlet-container and use the TCK client (HTTP client) to access them. The portlets interact with the portlet-container through the portlet API, and therefore, test the compliance of the implementation.

The test suite documentation was impressive and in two days our team had set up the entire set. After the first test showed 71% compliance, it took us one week to be 100% compliant. We did not encounter any major design problems needing big refactorings, and the fixes were only small details.

The compliance is fundamentally important as this certifies that portlets developed with the eXo platform can be deployed on any other compliant portal and vice versa. Indeed, as many features of the first versions - like hot deployment - really increases developers' productivity and reduces time to market, several large companies have decided to use the eXo platform in development stages. The certified compliance ensures that this is a good development choice. It is now our challenge to convince you that the eXo platform is fit for production.

Finally, sincere thanks to Sun Microsystems, and notably Adam Abramski, for their support and expedient responses.

Business model : dual licences, support and services

Like our code, our strategy is open : we communicate everything.

The eXo platform SARL is a commercial company distributing open source software (OSS) under the GNU/GPL licence. The company also provides commercial licences to Integration Service Vendors (ISVs) and end users. The "end user" licence adds many common warranties on the product we distribute, whereas any open source licence disclaims all responsibilities with the free software. The ISV licence allows integration companies to distribute their product bundled with the eXo platform without forcing them to use the GPL licence. Of course anyone is free to use the GPL licence and we do encourage it. But, as this sometimes is not possible we also propose other options. We enforce no restrictions on anyone, and truly believe this is what open source is about : Freedom.

We aim to delve deeper into the notion of "derivative work", as defined in the GPL licence. We have had many questions on that topic and have experienced that this is not well understood by our cutomers. Distributing the eXo platform with your own portlets that only communicate with the portal/portlet-container through the standard portlet API is possible. You do not have to use the GPL licence for them. This is not a derivative work of our software. But if those portlets use any non-standard extensions implemented by the eXo platform such as filters, message listeners, services etc., then this has to be viewed as "derivative work" and your portlets should also be distributed under the GPL licence.

Now, a few essentials about Intellectual Property (IP). Many open source committers are poorly informed on this very important point. In order to provide several licences for the same code source, the eXo platform SARL company requires a copy of the IP from every committer. This is a copy of the developer rights, and the concept of copy is of importance. When a developer commits code to the eXo platform project and provides a copy of its IP he does not part with it. He may still do anything with his code, and use it in any other project using any other licence. It is his code and IP, and with the copy he extends the company the same rights.

In exchange for these rights we reward credits for all tasks, issues and bugs. Once a task is completed and unit tested the developer's account is credited. Every three months we distribute 75% of the net revenues from licence sales to all committers (individual or company) according to their rewarded credits. The eXo platform project is composed of several modules that each have a credit budget for a three month period. Each module is managed by a leader that defines the amount of points rewarded for each task within his module.

This innovative approach to collaboration has already attracted many developers and companies. There are presently 6 very active developers, 4 of them working full time, including weekends! Please, join the consortium!

The eXo platform SARL provides services and support for its products. For more detailed information please browse the www.exoplatform.com site.

Roadmap

The eXo platform is based on several projects also in final development stages, such as Java Server Faces, Pico container or jBPM (Java Business Process Management).

The JSF team is expected to release a new version by year's end, and a final one within the first quarter of 2004. Pico Container is in beta3, with the next, final release expected soon. jBPM 1.0 is almost final and the current version (beta 5.2) is stable.

When all these products are out, we will release our first 1.0 version, probably during first quarter of 2004. We may release a last beta version in the begining of February. The next big step is then to support the Oasis WSRP standard.

Our intention is to challenge and compete with commercial solutions providing integrated Portal - Content Management Systems (CMS). There is still work to be done to tightly couple our workflow engine with our CMS repository, but our first version will be a viable open source alternative to very expensive, closed commercial solutions

Development environment

The goal of this section is to introduce the reader to the portlet API by showing a small tutorial on how to create a Hello World portlet and to test it with the eXo platform. Then, we introduce the eXo platform Eclipse plug-in, a tool that improves developers' productivity while implementing portlets.

Your first Hello World portlet

A basic portlet should extends the GenericPortlet class from the portlet API. It provides several methods like doView(), doHelp() that are called by the render() method of the GenericPortlet according to the current mode. The Hello World portlet code we will show is quite simple : it has dedicated behaviour for normal window state.

public class HelloWorldPortlet extends GenericPortlet {

  private static final String HELLO_TEMPLATE = "/WEB-INF/templates/html/HelloWorld.jsp";

  public void init(PortletConfig config) throws PortletException {
      super.init(config);
    }

  public void doView(RenderRequest request, RenderResponse response)
    throws PortletException, IOException {
    WindowState state = request.getWindowState();
    response.setContentType("text/html") ;
    if (state == state.NORMAL) {
      Writer writer = response.getWriter() ;
      writer.write("<center><img src='/HelloWorld/images/hello-world.png'/></center>");
      writer.write("<center>Hello Portal World in View Mode</center>");
    }
    PortletContext context = getPortletContext() ;
    PortletRequestDispatcher rd = context.getRequestDispatcher(HELLO_TEMPLATE) ;
    rd.include(request, response);
  }
  [...]
      

In the normal state we get a PrintWriter from the RenderResponse object and write directly into it. In any other window state (including the normal one), we use a PortletRequestDispatcher to include the content of a jsp page : HelloWorld.jsp. Obtaining the current state is very easy : request.getWindowState(). As the portlet API leverages the servlet API, the code is quite similar to what you write for a simple servlet. Even the init() method, that uses a PortletConfig object, uses almost the same signature as the corresponding servlet one.

The jsp page does not change much either. To obtain the portlet objects you need to either use some tags defined in the API or simply get them as request attributes :

<%@ page import="javax.portlet.RenderRequest"%>
<%
  RenderRequest renderRequest = (RenderRequest) request.getAttribute("javax.portlet.request");
 %>

<b>Hello</b> include in jsp in portlet<br>
portlet mode :
<%= renderRequest.getPortletMode().toString() %>
      

Each portlet application comes with a portlet.xml file that is located under the WEB-INF/ directory close to the web.xml. It provides information such as the mode supported per markup language, some init parameters, the portlet class name, etc.

<portlet>
    <description lang="EN">My First Hello World Portlet</description>
    <portlet-name>HelloWorld</portlet-name>
    <display-name lang="EN">Hello World</display-name>
    <portlet-class>HelloWorldPortlet</portlet-class>
    <init-param>
      <description>something to describe</description>
      <name>initName</name>
      <value>initValue</value>
    </init-param>
    <expiration-cache>-1</expiration-cache>
    <supports>
      <mime-type>text/html</mime-type>
      <portlet-mode>edit</portlet-mode>
      <portlet-mode>help</portlet-mode>
    </supports>
    <supported-locale>en</supported-locale>
    <portlet-info>
      <title>Hello World</title>
      <short-title>Hello</short-title>
      <keywords>hello</keywords>
    </portlet-info>
  </portlet>
</portlet-app>
      

This XML file is a minimal one but we do not need more information for our Hello World portlet. The web.xml file should also contain some basic information such as the portlet application name :

<display-name>HelloWorld</display-name>
  <description>
     This application is a portlet. It can not be used outside a portal.
   </description>
   [...]
  <taglib>
    <taglib-uri>portlet</taglib-uri>
    <taglib-location>/WEB-INF/tlds/portlet.tld</taglib-location>
  </taglib>
      

If you want to use the tag library you also need to inform on the taglib location in the web.xml file.

That's all, we have our first portlet. We now need to make a WAR and deploy it into our Tomcat webapps/ directory.

There are several ways to vizualise your portlet. The usual way is to access the basic portal page, to log in and to finally use the customizer portlet to add the Hello World portlet somewhere in your portal page. The basic URL for this main portal page access is : /exo/faces/public/portal.jsp?_ctx=community (refer to the last section where we define that URL more precisely). Of course, this is quite a long process while you develop, therefore we introduced a new development page where you can visualize a portlet located in a well known portlet application : /exo/faces/public/portlet.jsp?_ctx=anonymous&portletName=HelloWorld/HelloWorld. The portletName parameter is composed like this : portletApplicationName + "/" + portletName.

Figure 1. The Hello World portlet

The Hello World portlet

The Eclipse plugin

The ultimate goal of this plugin is to provide eXo platform application developers with a rich set of tools such as wizards, editors, and views that integrates with the Eclipse platform. This should leverage the existing capability of the Eclipse platform and its JDT tooling while providing specialized tools within Eclipse to help the eXo platform developers community.

The first beta release of the plugin targets a certain set of eXo platform developers, more specifically, the portlet developers. Building on our experience with developing the Pluto Plugin, we identify a simple development cycle that most of the portlet developers follow. It starts with first using a wizard to create a Java web application that contains one or more portlets. Next, you write the source code using the Eclipse Java editor. Then, you package and deploy the web application using the Deploy Portlets action provided by the eXo plugin. You then start the eXo portal and test your portlet. Finally, you go back to the source code for further editing and the whole cycle (except the project creation step) is repeated until the portlet functionality is completed.

This release of the plugin comes with three main tools to be used during the development cycle identified above:

  • A portlet project wizard

    This wizard creates a project with all the essential files and directory structures that are common among any portlet project. You also can specify the source folder name, the name of the folder that contains the web content (such as jsp files), and the context root to use when deploying the application. One feature that we particularly like is the ability to start with a sample project. Currently, the wizard comes with one sample application (you guessed it, it is a HelloWorld sample). However, expect to see much more interesting sample projects in future releases of the plugin. The following figures show the three pages that represent the wizard.

    Figure 2. The project settings page (part 1 of 3)

    The project settings page (part 1 of 3)

    Figure 3. The sample projects page (part 2 of 3)

    The sample projects page (part 2 of 3)

    Figure 4. The deployment settings page (part 3 of 3)

    The deployment settings page (part 3 of 3)
  • A web application settings property page

    For each java project, the plugin provides a property page that contains information related to deployment, such as the context root and the deployment directory. The information presented in this page is used later by the Deploy Portlet action. The following figure shows the property page.

    Figure 5. The web application settings property page

    The web application settings property page
  • Deploy Portlet action

    You can access this action either via the menu bar or by using the default key shortcut (Ctrl+Shift+D). This action takes care of packaging the portlet project and copying the result to the deployment directory. Figure 6 shows the action in the menu bar.

    Figure 6. The deploy portlet action

    The deploy portlet action

How to build the platform

Properties File: To build the eXo platform you need to create a build.properties file in ExoBuild module. You can create this file by following the next steps.

  • Copy the local.properties.sample in ExoBuild/build-props to local.properties and customize this file according to your environment. This file contains some information such as the repository of the eXo project, the developer info...If you are not an eXo platform developer, you just need to edit the base.dir property and jdk property. By default, jdk property is set to use jdk version 1.4 or later version.
  • ${platform}.properties file, We currently support jboss, tomcat and jetty platforms. If you don't save tomcat, jboss or jetty in ${base.dir}/exo-tomcat and ${base.dir}/exo-jboss, you need to recustomize this file. Usually you should change the server.dir property to the location of your jboss, tomcat or jetty. Other properties such as deploy.dir or lib.dir are computed base on the server.dir property
  • common.properties contains the directories structure and database info of the eXo platform. See comments of each property in this property file for more information. We currently support hsql, MySQL and DB2. An oracle support is on the way.
  • Concatenate 3 files local.properties, common.properties and ${platform}.properties to create build.properties file after you have updated the properties. You can use ant tasks : ant prepare.jboss , prepare.tomcat , prepare.jetty to create this build.properties file as well.

Ant tasks : The eXo platform contains many modules. Each module can depend on other modules. So the build must process in a specified order. The current order is ExoCommons, ExoServicesContainer, ExoServicesAPI , ExoServer, ExoServices, ExoPortal and ExoPortlets. Once you run ant build.exo.portal, you can start modifying code in each module and ant deploy localy. Note that what you modify may affect the other module so you may need to recompile and redeploy the dependant module as well.

  • ant prepare.jboss : this task will create a build.properties by concatenated 3 files local.properties , common.properties, jboss.properties
  • ant prepare.tomcat : this task will create a build.properties by concatenated 3 files local.properties , common.properties, tomcat.properties
  • ant build.exo.portal: This task will call many other sub tasks in build-script and other modules. Some important sub tasks are:
    • prepare task : prepare.tomcat and prepare.jboss, those 2 tasks will create the portlet deploy dir and services deploy dir. They will copy the the missing jar files and overwrite some configuration files of jboss and tomcat.
      <ant antfile="${exo_build.dir}/build-script/platform-prepare-task.xml"
      	target="prepare.tomcat" inheritall="false"/>
      <ant antfile="${exo_build.dir}/build-script/platform-prepare-task.xml"
      	target="prepare.jboss" inheritall="false"/>
                       

      Note that the pepare.jboss and prepare.tomcat will check for the jboss.version and tomcat.version in the build.properties file and execute only if the property is present.

    • modules deploy task : you will find many ant call
      <ant antfile="${exo_commons.dir}/build.xml" target="deploy" inheritall="false"/>
      <ant antfile="${exo_irc.dir}/build.xml" target="deploy" inheritall="false"/>
      <ant antfile="${exo_services_container.dir}/build.xml" target="deploy" inheritall="false"/>
      <ant antfile="${exo_services_api.dir}/build.xml" target="deploy" inheritall="false"/>
      <ant antfile="${exo_services.dir}/build.xml" target="deploy" inheritall="false"/>
      <ant antfile="${exo_portal.dir}/build.xml" target="deploy" inheritall="false"/>
      <ant antfile="${exo_portlets.dir}/build.xml" target="deploy" inheritall="false"/>
                       

      Each module has a build.xml and a deploy target. The deploy target usually compiles the code of the module, packages it and deploys the jar files to one of the following directories : ${exo-core-lib.deploy.dir}, ${exo-portal-lib.deploy.dir}, ${service.deploy.dir} and ${portlet.deploy.dir}.

      In the ExoPortlets and ExoServices modules, you will find many services and portlets. Each service and portlet has its own build.xml. There are also usually two other included files : common-service-build.xml and common-portlet-build.xml. The common build file defines the common tasks such clean, compile, package, classpath... In the build.xml, you only need to customize the service or portlet name and the deploy target.

  • ant developer.update : this will call a developer.update task in build-script/cvs-task.xml. You need a ssh client and bash shell to run this task. It may work with other environements as well, but we have never tested it. Look for the documents on sourceforge to see how to configure ssh. You need to run "ssh-agent bash" and "ssh-add Public_key.txt" before running this task. Please refer to the sourceforge cvs documentation for information on how to customize ssh and upload the private key to your account
  • ant test.all : This task will call the test.all target in each module and will generate a unit test report in ExoBuild/reports/junit. You can find the test report in html format in ExoBuild/reports/junit/html. To run this task , you need to update the build.properties and change the server.type property to server.type=standalone This will tell the eXo module to use some mock objects and mock service during the test.
  • ant clean.deploy : delete the portlet deploy dir , temp dir , work dir.
  • ant clean.modules : delete build dir , temp dir dist dir in all the modules.
  • ant clean.all : clean.deploy + clean.modules

Now that's hardcore...

Specifications extensions

In our previous article, we focused on the architecture and choices we had to make while we were building the core of the platform. We also showed that open source software’s drive innovation and never stop where standards do.

First, the eXo platform supports most of the non mandatory features and suggestion defined in the specifications such as :

  • Caching : Each portlet content can be cached in a per user map to reduce portal page creation time. The implementation of this feature uses Aspect Oriented Programmation (AOP) and the AspectJ language. At build time we weave several aspects, as described in the previous article, to the class that calls the portlets instances, the cache aspect is one of these. When cache is enabled we look in the advice to see if the content has already been generated. If so we directly return it, if not we move to the next aspect. The cache is discarded when the processAction method is called or when the expiration period has elapsed.

    The portlet API specifications lets you define cache in the portlet.xml file :

    
    <portlet>
      [...]
      <expiration-cache>0</expiration-cache>
      [...]
    </portlet>
    
                

    You can use several values :

    • -1 means that cache never expires
    • 0 means that cache is disabled
    • n represents any second integer before the cache is discarded.

    Cache can be completely disabled by defining it in the portlet-container.xml configuration file :

    
    <cache>
      <enable>false</enable>
    </cache>
    
                

    Note that we also cache PortletPreferences object once they have been extracted from the underlying storage system to avoid calls to the database on each portlet request.

  • Portlet filters : filters are on the list, in the suggestions chapter, for the next version of the portlet specifications. We have implemented this feature reproducing as well as possible the servlet filter's API. The interfaces used are very similar to the servlet ones.

    Figure 7. The Portlet Filters extension

    The Portlet Filters extension

    The main difference is that filters are defined per portlet. To add a filter the portlet developer must define it in the portlet.xml file.

    
    <portlet>
      <filter>
        <filter-name>LoggerFilter</filter-name>
        <filter-class>exo.services.portletcontainer.test.filter.LoggerFilter</filter-class>
        <init-param>
          <name>default-param</name>
          <value>default-param-value</value>
        </init-param>
      </filter>
      [...]
    </portlet>
    
                

    For example, it is good practise to use such filters to log access to portlets with non intrusive code. The class would look like :

    
    public class LoggerFilter implements PortletFilter{
    
      public void init(PortletFilterConfig portletFilterConfig) throws PortletException {
        if(!"default-param-value".equals(portletFilterConfig.getInitParameter("default-param")))
          throw new PortletException();
      }
    
      public void doFilter(PortletRequest portletRequest,
    	               PortletResponse portletResponse,
    		       PortletFilterChain filterChain)
    	throws IOException, PortletException {
        // do something before the portlet is reached
        filterChain.doFilter(portletRequest, portletResponse) ;
        // do something after the portlet is reached
      }
    
      public void destroy() {
      }
    }
    
                

    The filter chain is created from the list of filters defined in the portlet.xml file. The implementation is here also done using an AspectJ aspect. After any aspect has been proceeded, the filter aspect launches the portlet filter in a recurisve way.

  • Portlet inter-communication : this feature is also a suggestion for the next version of the specifications. It lets a portlet sends an event to another one or broadcast the event within the scope of the portlet application. This message mechanism may, for instance, be used when a user clicks on the node of a file tree included in a portlet. An event can then be sent to another portlet that would modify its state and render, for example, the content of the node.

    Here again, we extend the specifications' portlet.xml file and ask the developer to reference a MessageListener class.

    
    <portlet>
      <message-listener>
        <listener-name>SimpleMessageListener</listener-name>
        <listener-class>exo.services.portletcontainer.test.listeners.SimpleMessageListener</listener-class>
        <description>a simple example</description>
      </message-listener>
      <portlet-name>PortletThatReceivesMessage</portlet-name>
      [...]
    </portlet>
    
                

    By convention, the portlet that sends the Message object must be aware of the type of the objects the listener can receive.

    Figure 8. The Portlet Inter-Communications extension

    The Portlet Inter-Communications extension

    This mechanism can only occurs in a processAction method call, in other words before any render methods is called so that the state of portlets is consistent.

    
    public class SimpleMessageListener implements MessageListener{
      public void messageReceived(MessageEvent messageEvent) throws PortletException {
        DefaultPortletMessage message = (DefaultPortletMessage) messageEvent.getMessage();
        System.out.println("Message received in listener : " + message.getMessage());
      }
    }
    
                

    To be able to send a message you need to cast the PortletContext object to the ExoPortletContext interface which extends the specifications.

    
    public class PortletThatSendsMessage extends GenericPortlet{
    
      public void processAction(ActionRequest actionRequest, ActionResponse actionResponse)
          throws PortletException, IOException {
        ExoPortletContext context = (ExoPortletContext) actionRequest.getPortletSession().getPortletContext();
        context.send("PortletThatReceivesMessage",
    		 new DefaultPortletMessage("message sent"),
    	         actionRequest);
        actionResponse.setRenderParameter("status", "Everything is ok");
      }
    
      public void render(RenderRequest renderRequest, RenderResponse renderResponse)
           throws PortletException, IOException {
        ExoPortletContext context = (ExoPortletContext) renderRequest.getPortletSession().getPortletContext();
        context.send("PortletThatReceivesMessage",
    		 new DefaultPortletMessage("message sent"),
    	         renderRequest);
        PrintWriter w = renderResponse.getWriter();
        w.println("Everything is ok");
      }
    }
    
                

    Note that this code is extracted from our unit tests set. Therefore the sent in the processAction() method will be executed as expected while the call done within the render() method will throw a PortletException.

  • Declarative security : the specifications only define programmatic security. We have added the declarative capabilities within our portlet framework. You can then define J2EE roles for a portlet, for each mode, to protect access to its content. Here is a sample of our portlet framework controller XML file :

    
    <controllers>
      <name>User</name>
      <identifier>UserPortlet</identifier>
      <view-controller>
        <default-action>ListUser</default-action>
        <action name="ListUser" class="exo.portal.portlets.user.ListUser">
          <forward name="success" page="user/ListUser.jsp"/>
          <forward name="error" page="Error.jsp"/>
        </action>
    
        <action name="SaveUserInfo" class="exo.portal.portlets.user.SaveUserInfo">
          <forward name="success" page="user/UserInfo.jsp"/>
          <forward name="error" page="Error.jsp"/>
          <require-role>user</require-role>
        </action>
      </view-controller>
      [...]
    </controllers>
    
                

    The require-role tag lets you define which J2EE roles defined in the web.xml are needed to execute that action. If you are accustomed to MVC type 2 web frameworks such as Struts or Webwork this syntax should be easy to grasp. I If you want more information look up our previous article or read the portlet source code.

    This feature may be added as an extension of the portlet.xml in the near future.

We have built the portlet container as a real open source production choice and we have t herefore added several features to improve response time and memory management :

  • Pooling : the portlet specification is built on the top of the servlet specification. Therefore PortletRequest, PortletResponse, PortletSession objects and many wrappers must be created for each portlet call. When you have a large amount of portlets within a page, and many concurrent requests, the number of objects to instanciate can be very important. To avoid these problems we use pooled objects.

    When a request comes to the portlet container, we borrow portlet objects and wrapper from the pool, fill them with the incoming information and call the portlet instance. When the portlet has generated the content and the request goes back to the portal, we release the pooled object.

    You can configure the number of objects in the pool using the portlet-container.xml file :

    
    <object-pool>
      <instances-in-pool>500</instances-in-pool>
    </object-pool>
    
                

  • Support of shared sessions : some application servers such as IBM WebSphere or Tomcat (4.1.29), in its default mode, support shared session. This simply means that when a request is dispatched to another web or portlet application context (corresponding to a WAR) the session of the first context is propagated to the context where the request is dispatched to.

    This behaviour is not the one defined by the servlet specifications which impose the creation of a new session per context :

    HttpSession objects must be scoped at the application (or servlet context) level. The underlying mechanism, such as the cookie used to establish the session, can be the same for different contexts, but the object referenced, including the attributes in that object, must never be shared between contexts by the container.

    To illustrate this requirement with an example: if a servlet uses the RequestDispatcher to call a servlet in another Web application, any sessions created for and visible to the servlet being called must be different from those visible to the calling servlet.

    To reduce the number of session object we decided to also support this mode and even make it the default one. Of course we tested our implementation with the TCK test suite and we claim compliance for this mode too.

    This feature required substancial work and imagination as the portlet API imposes a unique session per portlet application. We therefore had to partition the main session with encoded attributes to simulate independent sessions' objects.

    You may configure the use of share session in the portlet-container.xml file. Note that you also need to configure the underlying servlet container to the same session mode.

    
    <shared-session>
      <enable>false</enable>
    </shared-session>
    
                

  • Portlet lazy loading : to manage memory resources we have decided to instantiate and init portlets only when they are called for the first time.

    Here, we leverage pico-container capabilities. When portlets are deployed in the container, we reference them in what we call the ServiceManager. This is a singleton wrapper around a pico container instance.

    Figure 9. The ServiceManager class diagram

    The ServiceManager class diagram

    When the portlet is called we obtain it by calling the ServiceManager getService(portletKey) which underneath calls the pico container which returns and instantiates the portlet if it is not cached already.

Technology bridges

In the previous article we presented our custom portlet framework as a mix between Struts and Webwork, but dedicated to portlets' behaviour, which is quite unique. We developed many portlets with it, and we really recommand its use when you start your portlets from scratch.

We had a number of question about how to use existing servlet applications within the context of portlets. Those demands were quite recurrent for Struts and also important for Cocoon.

Struts 1.2 main goal is to support portlets, but that will not make existing servlets work in any portal without any rewriting. The extension of the framework is not the same than the bridge we have developed. By adding a layer between the portal and any existing Struts application within the portlet, we allow any existing struts application to be embedded in a portlet with a minimal amount of change. Note that this portlet bridge is eXo platform dependent as its consist in two phases : (1) obtaining the servlet objects from the portlet objects (using custom casts) and (2) rewriting the URL that the Struts application generates in order for the portal to find the correct portlet application and portlet that embedded the Struts framework.

Let's take a simple Struts application deployed as a servlet and make a portlet out of it. As the produced mark up language is embedded in a portal page, you need to remove all the header and footer tags of the Struts jsp page. For the same reasons, replace all the forward() methods by include() methods. Finally, if you have any hardcoded URL in your jsp page (shame on you), just use the encodeURL() method if you don't already. As most of Struts application use the html:link tag this should not be a problem. The rest of the application will stay the same.

Now we need to use a custom portlet which we define in the portlet.xml :


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

<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd
                    http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd">
  <portlet>
    <description lang="EN">Struts application</description>
    <portlet-name>StrutsExample</portlet-name>
    <display-name lang="EN">StrutsExample</display-name>
    <portlet-class>exo.portal.portlet.struts.ExoStrutsPortlet</portlet-class>
    <init-param>
      <name>default-page</name>
      <value>/index.jsp</value>
    </init-param>
    [...]
  </portlet>
</portlet-app>

      

The portlet-class is the most important one. Indeed, this is the ExoStrutsPortlet object that is the true bridge implementation. Don't worry, we will not get into the code of this class. The next code is the index.jsp page of the struts basic example. We have not removed the header and footer tags (because it still works) but you should.


<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>
<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %>

<html:html locale="true">
<head>
<title><bean:message key="index.title"/></title>
<html:base/>
</head>
<body bgcolor="white">

<logic:notPresent name="database" scope="application">
  <font color="red">
    ERROR:  User database not loaded -- check servlet container logs
    for error messages.
  </font>
  <hr>
</logic:notPresent>

<logic:notPresent name="org.apache.struts.action.MESSAGE" scope="application">
  <font color="red">
    ERROR:  Application resources not loaded -- check servlet container
    logs for error messages.
  </font>
</logic:notPresent>

<h3><bean:message key="index.heading"/></h3>
<ul>
<li><html:link page="/editRegistration.do?action=Create">
	<bean:message key="index.registration"/></html:link></li>
<li><html:link page="/logon.jsp"><bean:message key="index.logon"/></html:link></li>
</ul>

<p>&nbsp;</p>
<html:link page="/tour.do">
<font size="-1"><bean:message key="index.tour"/></font>
</html:link>
<p>&nbsp;</p>

<html:img page="/struts-power.gif" alt="Powered by Struts"/>

</body>
</html:html>

      

As you can see nothing was changed from the original index.jsp. We can now deploy the portlet war into the portal.

Figure 10. The Struts portlets deployment WAR directories

The Struts portlets deployment WAR directories

Finally, we can launch the portlet using our development page (new in beta 4), watch the browser image for the URL:

Figure 11. The Struts portlets

The Struts portlets

The same work can be done for any existing framework. We will now focus on the Java Server Faces (JSR 127) bridge.

The support of JSF within a Portlet is a new feature in the eXo platform. We solved many intriguing tasks that occurred in the process of establishing a faces portlet able to adopt the exisiting JSF based projects

There are two well known implementations of JSF : Sun JSF-RI and the OSS project MyFaces. The eXo platform supports both of them. You may choose to compile the platform to work on the one you prefer. Most of our code are independent from the JSF implementation, but there are some differences between Sun Reference Implementation and MyFaces implementation. By the production release we seek to make it independent of which JSF implementation you want to use.

  • Portlet/Servlet : Similarities and Differences

    The Servlet and Portlet interfaces have a lot of similarities. Both set of interfaces define Request , Response , Session, Config, Context objects. In each case, you can use the request interface to retrieve the request parameters, to set an attribute and to use the respone interface to send back the response to the client. You can also use the config interface to read the application configuration and use the context interface to access the container...

    However Servlet and Portlet are not the same. The biggest modification has been made to introduce an MVC design directly into the portlet. The portlet defines 2 phases of execution, processAction(..) and render(..). Also the Request and Response interfaces are well defined for each execution phase : ActionRequest and ActionResponse for the process action phase, and RenderRequest and RenderResponse for the render phase. Therefore the current JSF implementation will not support the portlet out of box. However, JSF technology has a very flexible and well thought design.

  • The JSF Implementation

    As you know, JSF has 7 phases of execution controlled by the FacesServlet. The FacesContext contains all of the per-request state information related to the processing of a single JavaServer Faces request, and the rendering of the corresponding response. It is given to, and potentially modified by, each phase of the request processing lifecycle. The ExternalContext contains the context, request, and response objects of the request life cycle; those objects can be either of Portlet or Servlet types. We will list a pseudo code here to illustrate how FacesServlet work and the faces context is created

    class FacesServlet extends HttpServlet {
      [..]
    
      public void init(ServletConfig servletConfig) throws ServletException {
        //do Ñ•ome initialization here such create FacesContext and Lifecycle factory object
      }
    
      public void service(HttpServletRequest request, HttpServletResponse response) {
        //create face context instance base on the servlet context, request and response.
        //The faces context will create the ExternalContext object and pass the servletContext
        //request and response to the new ExternalContext. You can access those objects at any place
        //in 7 phases of execution by calling:
        // HttpServletRequest=(HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest()
        // HttpServletResponse=(HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getResponse()
        FaceContext facesContext = facesContextFactoy.getFacesContext(servletContext, request, response) ;
        //get lifecyle object
        Lifecycle lifecycle = lifecycleFactory.getLifeCycle() ;
        //the lifecycle execute 7 phases:
        // 1) Reconstitute Request tree
        // 2) Apply Request Values (decode)
        // 3) Handle Request Event
        // 4) Process Validations
        // 5) Update Model Values
        // 6) Invoke Application
        // 7) Render Request
        lifecyle.execute(facesContext) ;
        //release the resource such request , response and the current context that associate
        //with the current thread
        facesContext.release() ;
      }
    }
                 

    You can access the context, request and response objects at any place by obtaining the current faces context object. You just need to get the external context, and then extract the request or response objects out of it. However when you write a JSF application or component, you don't know that the application/component will be deployed into a servlet environment or a portlet environement so you should not call the getRequest() and getResponse() and cast the returned object.

    The ExternalContext interface comes with a set of method that can help you to abstract the current environment. For example, to obtain the request parameter, you can use : externalContext.getRequestParameterMap().get(key) instead of ((HttpServletRequest)externalContext.getRequest()).getParameter(key). PLease check ExternalContext API for more detail and the available set of methods.

  • Our modifications

    Now that you have an overview how JSF works in the servlet environment. You should see that in order to make a JSF support within the portlet technology, we only need to replace the FacesContext, ExternalContext and FacesContext factory implementations by a custom portlet implementation

    Figure 12. The request process in the JSF bridge

    The request process in the JSF bridge

    As we mention above, the portal controller delegates (1) the request to the portletcontainer where the faces portlet is located. The portlet replaces (2) the FacesContext instance by a new one and obtains the lifecycle object before executing (3) it. Wehen the new JSF lifecycle has finished its work (4), the portlet restores the previous state (5) and returns (6) an Output object to the portal WAR.

    The next JSP page shows hw simple it is to introduce JSF code inside a portlet :

    <%@ taglib uri="http://exoplatform.org/jsf/custom" prefix="x" %>
    <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>
    <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>
    
    <%@ page import="framework.bean.HelloBean"%>
    
    <f:use_faces>
      <div>Faces, Welcome to eXo Platform, this is a test</div>
      <div>
        <x:form id="facesForm" formName="facesForm" method="POST">
          <h:input_hidden id="actionName" value="HelloFacesAction" />
          <h:output_text id="label_name" value="Please enter your name:<br>" />
          <h:input_text id="input_name" valueRef="hello.name" />
          <h:command_button id="submit" type="submit" commandName="submit" label="Submit" actionRef="hello.submit"/>
        </x:form>
      </div>
    </f:use_faces>
               

    Do not focus on the eXo custom taglib,we have just added several usefull reusable tags. The HelloBean is a JSF Managed Bean defined in the faces-config.xml file :

    [...]
      <managed-bean>
        <description>Hello Bean</description>
        <managed-bean-name>hello</managed-bean-name>
        <managed-bean-class>framework.bean.HelloBean</managed-bean-class>
        <managed-bean-scope>session</managed-bean-scope>
        <managed-property>
          <property-name>name</property-name>
          <value>Enter your name here</value>
        </managed-property>
      </managed-bean>
      [...]
               

    The output created by this small portlet can be seen in the next screenshot or using the URL : /exo/faces/public/page.jsp?_ctx=anonymous&portletName=HelloFacesPortletFramework/HelloFacesPortletFramework with your eXo platform distribution as this portlet is bundled with it.

    Figure 13. A simple JSF Hello World portlet

    A simple JSF Hello World portlet

Let's finish with the configuration needed in the faces portlet WAR.

  • JSF Factory Objects The JSF factory objects are configured in 'WEB-INF/classes/faces.propterties', and must be included in every Portlet-WAR, to accomodate that your web application performs within the eXo platform.
    javax.faces.render.RenderKitFactory=com.sun.faces.renderkit.RenderKitFactoryImpl sPortletLifecycleFactoryImpl
    javax.faces.lifecycle.LifecycleFactory=com.sun.faces.lifecycle.LifecycleFactoryImpl
    javax.faces.tree.TreeFactory=exo.portal.portlet.faces.tree.ExoTreeFactory
    javax.faces.context.FacesContextFactory=exo.portal.portlet.faces.context.FacesPortletContextFactoryImpl
    javax.faces.context.MessageResourcesFactory=com.sun.faces.context.MessageResourcesFactoryImpl
    javax.faces.convert.ConverterFactory=com.sun.faces.convert.ConverterFactoryImpl
    javax.faces.application.ApplicationFactory=com.sun.faces.application.ApplicationFactoryImpl
               
  • The faces-config.xml should be configured as normal - there is nothing special to it. In web.xml however, you must add
    <listener>
        <listener-class>exo.portal.portlet.faces.webapp.DefaultServletContextListener</listener-class>
    </listener>
               
    When the servlet context is initialized, it configures the JSF Factory to use FacesPortletViewHandler. That aside normal JSF configuration is sufficient.
  • In portlet.xml you must configure the faces portlet class to use exo.portal.portlet.ExoFacesPortlet or your custom decsendant of it. In addition you should declare the faces url mapping.
    <init-param>
      <description>Faces Mapping URL</description>
      <name>facesMapping</name>
      <value>/customfacescontext</value>
    </init-param>
                
    If NOT specified it will use the default /faces url prefix for each dispatched request.

Finally, note that we are also making it available for cocoon as our previous version needed some manual rewrite of URL using PortletURLs.

The goals of those bridges is to use all the existing framework and application within a portlet of the eXo platform. If you are interested in using a servlet application based on a framework within the scope of a portlet, please contact us.

Discover Java Server Faces : The portal design

Let's describe how our Java Server Faces based portal works. This section gives an overview of the eXo portal achitecture, how we use JSF and the way the portal interacts with the portlet container. We focus on the request lifecycle which goes through the filter phase, the reconstitute request tree phase, the decode phase and finally, the render phase.

As we mention above, the eXo portal is composed of many portlet WARs and a master eXo portal WAR. The eXo portal module is responsible for checking the security, decoding the request, loading the user configuration, building the jsf tree, and distpatching the request to the portlet container. When all the portlets located in the requested page are rendered, the portal returns an aggregated page to the client.

The lifecycle of the exoportal can be seen as :

Figure 14. The eXo portal lifecycle

The eXo portal lifecycle

Note that the eXo portal does not use the entire JSF lifecycle, therefore some phases are not showned in the diagram.

Portal initialization

When the web server is started or the eXo web arvchive (WAR) is deployed, the PortalContextListener (You can find the configuration of this listener in web.xml) will catch the start event and run the checking code for eXo tables, the default groups and the default users. If the anonymous and admin users are missing, the listener will look up the organization service and cms service, to create the missing user and a home directory for that user in the cms. Also note that the default configuration XML file for the page layout (exo/WEB-INF/conf/user-pages.xml) is copied to the home directory in the cms repository of that user.

Portlet Initialization

When the webserver is started or a portlet war is dropped into the portlet deployed directory, the PortletApplicationListener (You can find the configuration of this listener in portlet.war/WEB-INF/web.xml or default-web.xml) will catch the start event, it will lookup for the portlet container service and register the application with the portlet container if a portlet.xml file is present.

Request processing

After the server is started and that all the initialization has been done. The portal is ready to receive the first request. There is a very simple rule to handle the request : any dynamic request must go though the portal and be treated by the filter , the jsf servlet controller and the portlet container. For a static resource request, such as an image file, the request can be handled by the default servlet of portal or the default servlet of the portlet; only depending on the context path of the request.

Before getting deeper in the request processing mechanism we would like to define the notion of “User Context”. The eXo portal defines 2 types of users for a session : a user context and a remote user(request.getRemoteUser()). The user context is the page configuration and a user profile of an user. The remote user is the user who visits the page. In most cases, when a remote user logs in, the remote user and user context will be the same and the remote user will use the private link. In this case, the remote user has all the admin rights for the user context, the remote user can view all the pages and customize the pages.xml by using the customizer portlet. But, a remote user can still be logged in and visit another user context using public links. In this case , the remote user can only view the public pages of that user context. Finally note that with the eXo portal, you can turn any user context into the default home page by setting some pages of that user as public and map the home page to that user url in web.xml.

  • Filter phase. When a request is sent to the portal, it will be first handle by either a PublicRequestFilter or a PrivateRequestFilter (you can find the configuration of those 2 filters in exo/WEB-INF/web.xml) depending on the url type. We currently define 2 types of url, public and private. The public url has the form /exo/faces/public/portal.jsp?_ctx=user... and the private url has the form /exo/faces/private/portal.jsp?_ctx=user... Both public and private paths you saw in 2 url are virtual paths and they are defined in the web.xml. Both paths are mapped to the same portal.jsp file in exo/portal.jsp. The role of filter is to check the User Context. If the user context, _ctx=user, does not match with the current user context or no user context exists in the session, the filter will destroy the jsf tree in the session, reload the user context according the request, and store the user context in the session.
  • Reconstitute JSF tree phase. After the filter phase, the request is forwarded to the FacesServlet. The FacesServlet will get the Lifecycle instance and will execute the faces lifecycle. One important phase in the faces lifecycle is the process reconstitute part. It will check for a tree id in the session - id associated with the request tree - and will reconstruct the component tree if the two trees are not the same. With exo, you always make the request to /portal.jsp tree so the jsf tree is always cached in session. The JSF tree is destroyed and reconstructed only when the request user context does not match with the one in the session (done in the filter phase). Note that the exo jsf tree is constructed based on the pages.xml configuration file of the user, basically you will have a ui component tree that reflects the xml file.
  • JSF Decode phase The next phase of the faces cycle is the decode phase or apply request phase. In this phase, the JSF implementation iterates over the components in the component tree and calls each component's decode() method. That method extracts information from the request and stores it in the component. With the eXo implementation, the UIpage component will check for the portal action parameter, if the action is “change page”, it will go on and check for the page id. If the page id matches the current UIPage component id, it raises an action event. The PageActionListener will catch the event and will set the selected page flag of the UIPage object to true. Finally, it will ask the parent ui component (UIPortal) to set the selected property of the other UIPage components to false.
    
      public void decode(FacesContext facesContext, UIComponent uiComponent) throws IOException {
        UIPage uiPage = (UIPage) uiComponent ;
        HttpServletRequest request = (HttpServletRequest) facesContext.getExternalContext().getRequest();
        String portalAction = request.getParameter(Constants.PORTAL_ACTION);
        //check for the change page action
        if (portalAction != null && "changePage".equals(portalAction)) {
          String pageId = request.getParameter(Constants.PAGE_ID) ;
          // check the request page id with the current uiPage instance
          if (pageId.equals(uiPage.getId())) {
            //pass the event to the PageActionListener
            ActionEvent event = new ActionEvent(uiComponent, portalAction);
            facesContext.addFacesEvent(event);
          }
        }
      }
    
            
    The UITab decode() method will do the same thing as UIPage decode() method : it checks for the portal change tab action and sets the selected tab property to true and the property of the other tabs to false.
    
      public void decode(FacesContext facesContext, UIComponent uiComponent) throws IOException {
        UITab uiTab = (UITab) uiComponent ;
        HttpServletRequest request = (HttpServletRequest) facesContext.getExternalContext().getRequest();
        String portalAction = request.getParameter(Constants.PORTAL_ACTION);
        // Check if the portal action is change tab action
        if (portalAction != null && "changeTab".equals(portalAction)) {
          String tabId = request.getParameter(Constants.TAB_ID) ;
          // Check if the change tab action is addressed to the current UITab instance
          if (tabId.equals(uiTab.getId())) {
            //Create Action event and pass it to the TabActionListener
            ActionEvent event = new ActionEvent(uiTab, portalAction);
            facesContext.addFacesEvent(event);
          }
        }
      }
    
            
    The UIPorlet decode() will do 3 main tasks :
    • Check for the “change mode” event : if the event is detected and the request component id matches the current UIPorlet component id, it will raise an event and will delegate it to the PortletActionListnenter class. The listener will reset the mode in the UIPortlet Component.
    • Check for the “change window” state event: if the event is detected and that the request component id matches the current UIPorlet component id, it will raise an event and will delegate it to the PortletActionListnenter class. The listener will reset the window state in the UIPortlet Component.
    • Check for the portlet action type: According the portlet spec, we have 2 types of action : one is the action type and the other one is the render type. If the type is action , the processAction(..) method of the portlet will be called and then the render(....) method is called. If the type is render, only the render(..) method is called. It is mandatory that the processAction(..) has to be called before any render(..) method is called. The reason for this requirement is because a portlet can process an action and send a message to another portlet. Indeed, it would not make any sense if the render(...) method of the other portlet has already been called. Once again we can see how JSF technology fits very well with portlet technology, by defining many process phase. This way, it will make sure that each processAction(..) of each portlet will be called first and each render(..) method of each portlet will be called in the render phase
    
      public void decode(FacesContext facesContext, UIComponent uiComponent) throws IOException {
        UIPortlet uiPortlet = (UIPortlet) uiComponent ;
        HttpServletRequest request = (HttpServletRequest) facesContext.getExternalContext().getRequest();
        String type = request.getParameter(Constants.TYPE_PARAMETER);
        String portletMode = request.getParameter(Constants.PORTLET_MODE_PARAMETER);
        String windowState = request.getParameter(Constants.WINDOW_STATE_PARAMETER);
        String componentId = request.getParameter(Constants.COMPONENT_PARAMETER) ;
    
        // check for the portlet mode and if a mode change is detected , raise a change mode event
        if (portletMode != null && componentId.equals(uiPortlet.getWindowId())) {
          ChangePortletMode event = new ChangePortletMode(uiPortlet, "portletMode", portletMode);
          facesContext.addFacesEvent(event);
        }
    
        // check for the window state and if a change is detected , raise a change window state event
        if (windowState != null && componentId.equals(uiPortlet.getWindowId())) {
          ChangeWindowState event = new ChangeWindowState(uiPortlet, "windowState", windowState);
          facesContext.addFacesEvent(event);
        }
    
        //check for the action type and component id
        if (type != null  && componentId.equals(uiPortlet.getWindowId())) {
          //if type = action, raise a PortletAction event and pass it to the PortletActionListener
          //the listener will create the input/output object and pass the request to the portlet container
          if (type.equals("action")) {
            HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
            PortletAction event = new PortletAction(uiPortlet, "portletAction", facesContext, request, response );
            facesContext.addFacesEvent(event);
          }  else {
            //else type = render, Simply copy the parameter map and store it in UIPortlet component
            //in the render phase, use this parameter map for the request
            Map renderParams = request.getParameterMap() ;
            Map temp = new HashMap(10) ;
            Iterator keys = renderParams.keySet().iterator() ;
            while (keys.hasNext()) {
              String key = (String) keys.next() ;
              temp.put(key, renderParams.get(key)) ;
            }
            renderParams = temp ;
            uiPortlet.setRenderParameters(temp);
          }
        }
      }
    
            
  • JSF Render phase. Finally the Render phase creates the html page by calling the methods encodeBegin(..) encodeChildren(..) and encodeEnd(..) of the root component. The parent UIComponent will control the render phase of its children. With the eXo portal jsf tree, the root component is UIPortal component. As you can see in the code below, the UI portal renderer renders a table, then it renders the header portlet, the the current selected page and finally the footer portlet.
    
      public void encodeBegin(FacesContext facesContext, UIComponent uiComponent) throws IOException {
        ResponseWriter writer = facesContext.getResponseWriter();
        writer.write("<table class='portal' cellspacing='0' cellpadding='0' border='0' width='100%' height='100%'>");
      }
    
      public void encodeChildren(FacesContext facesContext, UIComponent uiComponent) throws IOException {
        ResponseWriter writer = facesContext.getResponseWriter();
        Renderer pageRenderer = renderKit_.getRenderer(UIPage.RENDERER_TYPE);
        UIPortalPages uiPortalPages = (UIPortalPages) uiComponent ;
        Iterator iterator = uiPortalPages.getChildren();
        while (iterator.hasNext()) {
          Object component = iterator.next() ;
          //if instance is the portlet , it is either header or footer portlet
           if (component instanceof UIPortlet) {
            UIPortlet uiPortlet = (UIPortlet ) component ;
            Renderer portletRenderer = renderKit_.getRenderer(uiPortlet.getRendererType());
            writer.write("<tr><td valign='top' style='padding: 0px'>");
            portletRenderer.encodeBegin(facesContext, uiPortlet);
            portletRenderer.encodeChildren(facesContext, uiPortlet);
            portletRenderer.encodeEnd(facesContext, uiPortlet);
            writer.write("</td></tr>");
          } else if (component instanceof UIPage) {
            //If the componenent is UIPage instance and the page is selected
            //Then render the page.
            UIPage uiPage = (UIPage ) component ;
            if (uiPage.isSelectedPage()) {
              writer.write("<tr><td width='100%' height='100%' valign='top'>");
              pageRenderer.encodeBegin(facesContext, uiPage);
              pageRenderer.encodeChildren(facesContext, uiPage);
              pageRenderer.encodeEnd(facesContext, uiPage);
              writer.write("</td></tr>");
            }
          }
        }
      }
    
            
    The UI page renderer will render all its children that are UIMainColumn instances.
    
      public void encodeBegin(FacesContext facesContext, UIComponent uiComponent) throws IOException {
        UIPage uiPage = (UIPage) uiComponent ;
        ResponseWriter writer = facesContext.getResponseWriter();
        writer.write("<table width='100%' height='100%' class='") ;
        writer.write(uiPage.getStyle()) ;
        writer.write("' cellspacing='0' cellpadding='0' border='0'>\n");
      }
    
      public void encodeChildren(FacesContext facesContext, UIComponent uiComponent) throws IOException {
        ResponseWriter writer = facesContext.getResponseWriter();
        Renderer mainColumnRenderer = renderKit_.getRenderer(UIMainColumn.RENDERER_TYPE);
        UIPage uiPage = (UIPage) uiComponent ;
        Iterator iterator = uiPage.getChildren();
        writer.write("<tr>");
        while (iterator.hasNext()) {
          UIMainColumn uiMainColumn = (UIMainColumn )iterator.next() ;
          writer.write("<td class='");
          writer.write(uiMainColumn.getStyle()) ;
          writer.write("' width='");
          writer.write(uiMainColumn.getWidth());
          writer.write("' height='100%' valign='top'>");
          mainColumnRenderer.encodeBegin(facesContext, uiMainColumn);
          mainColumnRenderer.encodeChildren(facesContext, uiMainColumn);
          mainColumnRenderer.encodeEnd(facesContext, uiMainColumn);
          writer.write("</td>");
        }
        writer.write("</tr>");
      }
    
      public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException {
        ResponseWriter writer = facesContext.getResponseWriter();
        writer.write("</table>");
      }
    
            
    In UI Portlet Renderer, since the UIPortlet has no children, only encodeBegin(..) is required to be called. In the code below, you can see that we create the RenderInput object, it contains all the information of the request, and delegates it to the portlet container. The portlet container will then invoke the method render(..) and return an OutputObject. Then the portal renders the portlet header and the portlet body using the content returned by the portlet container in the Output object. Note that in the portal page you have many portlets but each request only targets one portlet. Therefore, the parameter map sent to the container is cached as a parameter map in the associated UIPortlet object. Only the portlet you send request to use HttpServletRequest parameter map or a parameter map produced by the processAction() method. All of those steps are processed in the decode phase.
    
      public void encodeBegin(FacesContext facesContext, UIComponent uiComponent) throws IOException {
        portletContainer_ = (PortletContainerService)
    			ServicesManager.getInstance().getService(PortletContainerService.class);
        HttpServletRequest request = (HttpServletRequest)
    			facesContext.getExternalContext().getRequest() ;
        HttpServletResponse response = (HttpServletResponse)
    			facesContext.getExternalContext().getResponse();
        UserProfile up = (UserProfile) request.getSession().getAttribute(Constants.USER_BEAN) ;
        StringBuffer baseUrlBuf = new StringBuffer() ;
        baseUrlBuf.append(request.getRequestURL().toString()).
                   append('?').append(Constants.PORTAL_CONTEXT).append('=').append(up.getUserName()) ;
        String baseUrl = baseUrlBuf.toString();
        UIPortlet uiPortlet = (UIPortlet) uiComponent ;
    
        Map renderParams = uiPortlet.getRenderParameters() ;
        log_.debug("map from uiportlet = " + renderParams) ;
        if (renderParams == null) {
          renderParams = new HashMap() ;
          uiPortlet.setRenderParameters(renderParams);
        }
        RenderInput input = new RenderInput(baseUrl, uiPortlet.getWindowId() ,
                                            up.getUserName(), up.getUserInfoMap(),//user map
                                            uiPortlet.getPortletMode(),
                                            uiPortlet.getWindowState(), "text/html",
                                            renderParams);
        RenderOutput output = null;
        String portletContent = "There is an error" ;
        try {
          output = portletContainer_.render(request, response, input);
          portletContent = output.getContent() ;
        } catch (Throwable ex) {
          ex.printStackTrace();
        }
    
        String portletTitle = uiPortlet.getTitle() ;
        if (portletTitle == null) {
          portletTitle = output.getTitle() ;
        }
        String portletHeight = uiPortlet.getHeight() ;
        if (uiPortlet.getWindowState() == WindowState.MINIMIZED) {
          portletHeight = null;
        }
    
        ResponseWriter writer = facesContext.getResponseWriter();
        writer.write("<table class='");
        writer.write(uiPortlet.getStyle()) ;
        writer.write("' cellspacing='0' cellpadding='0' border='0' width='100%'");
        if(portletHeight != null) {
          writer.write(" height='");
          writer.write(portletHeight);
          writer.write("'>\n");
        } else {
          writer.write(">\n");
        }
    
        renderPortletHeaderBar(writer, uiPortlet, portletTitle, baseUrl, up) ;
        if (uiPortlet.getWindowState() != WindowState.MINIMIZED) {
          renderPortletBody(writer, uiPortlet, portletContent) ;
        }
        renderPortletFooterBar(writer, uiPortlet, portletTitle, baseUrl, up) ;
        writer.write("</table>\n");
      }
    
            

Multi application server support : the universal deployer

In our previous article, we presented a JMX based deployer for the JBoss application server (beta2). That made us quite couple with this specific server. In beta3, we released an express version based on Tomcat. Here too we modified Tomcat code to allow portlet hot deployment. Unfortunately this approach was only possible with open source application server.

After days of brainstorming and a week of work refactoring some important parts of the portlet container we succeeded in building a universal deployer. We actually use the same code to deploy portlets on Tomcat, Jetty and JBoss. We are also about to make it work on IBM's Websphere Application Server (WAS). We will then be able to distribute the enterprise version of the portal as an EAR. Other application server will then be targeted.

The previous deployers were used to acquire the ClassLoader and ServletContext objects of the portlet application deployed as a WAR archive. Those objects were then registered to the portlet container that directly instantiated the portlets using the correct class loader. The new universal deployer is based on the RequestDispatcher object. When a portlet application is now deployed, only the portlet.xml and web.xml files are registered into the container (actually we register the object representation of those XML files using JAXB). This phase simply uses a ServletListener object that register and unregister the files when the context of the application is deployed and undeployed.

Figure 15. The Portlet Container architecture

The Portlet Container architecture

Therefore, when a user sends a request to the portal web application (1), the portal decodes the incoming parameters to extract the portlet application name and portlet name (2) and redirects the request using the RequestDispatcher include() (4) method. What is necessary to understand here is that the request dispatcher is accessed using the portlet application context obtained using the portal ServletContext.


ServletContext portletContext = portalContext.getContext("/" + windowInfos.getPortletApplicationName());
RequestDispatcher dispatcher = portletContext.getRequestDispatcher(SERVLET_MAPPING);
try {
  dispatcher.include(request, response);
} catch (ServletException e) {
  throw new PortletContainerException(e);
} catch (IOException e) {
  throw new PortletContainerException(e);
} finally{
  ((PortletPreferencesImp)windowInfos.getPreferences()).discard();
}

    

In the portlet application, a servlet used to wrapped portlets is then accessed. It extracts the information on which portlet to invoke with which incoming data and then delegates the work the PortletApplicationHandler class. This object obtains the portlet instance from the PortletApplicationProxy that instantiates it and calls the init() method if this is the first request to call the portlet. Then the handler calls either the processAction() or render() methods of the portlet.

Note that we have splited the ServletWrapper and PortletApplicationHandler in two in order to be able to unit test the portlet-container without launching the application server. This design which implied some more work was really a good choice as we highly used unit tests to develop the container. We are almost sure that without unit tests our first TCK tests score would have been much lower. The following code content is not that important, as such; it is to show that we have made custom code to avoid request dispatching and consequently the use of a servlet engine.


if (Environment.getInstance().getPlatform() == Environment.STAND_ALONE) {
  try {
    URLClassLoader oldCL = (URLClassLoader) Thread.currentThread().getContextClassLoader();
    URL[] urls = {new URL(PORTLET_APP_PATH + "WEB-INF/classes/"),
                  new URL("file:./lib/portlet-api.jar"),
                  new URL(PORTLET_APP_PATH + "WEB-INF/lib/")};
    Thread.currentThread().setContextClassLoader(new URLClassLoader(urls));

    try {
      return standAloneHandler.process(portalContext, request, response, input, output, windowInfos, isAction);
    } finally {
      Thread.currentThread().setContextClassLoader(oldCL);
    }
  } catch (MalformedURLException e) {
    e.printStackTrace();
  }
}

    

We insist again : eXtreme Programming (XP) and Test Driven Development (TDD) are more than good practices, they are way of life.

IoC everywhere

The Inversion of Control design pattern really makes the developer's life much simpler and forces you to use interfaces instead of classes. This produces a much cleaner and maintainable code. As we explained in our previous article, the first aim of the pattern is not to let the object create itself the instances of the object it references.

The IoC type 3, as used in pico-container and now supported in the Spring framework, gives to the object, the other objects to reference within the constructor arguments.


public class PortletToTestIoC extends GenericPortlet{

  private LogService logService;
  private Log log;

  public PortletToTestIoC(LogService logService) {
    this.logService = logService;
    log = logService.getLog("exo.portal.container");
  }

  public void processAction(ActionRequest actionRequest, ActionResponse actionResponse)
       throws PortletException, IOException {
    log.debug("Portlet is an IoC type 3 component");
    actionResponse.setRenderParameter("status", "Everything is ok");
  }

  public void render(RenderRequest renderRequest, RenderResponse renderResponse)
       throws PortletException, IOException {
    log.debug("Portlet is an IoC type 3 component");
    PrintWriter w = renderResponse.getWriter();
    w.println("Everything is ok");
  }
}

      

It is highly advisable that the constructor uses interface types instead of classes implementation. Indeed you can register into any pico-container the class implementation and then, when the object is instantiated pico can resolve the implementation of the interface that the constructor needs. Therefore, just by changing the implementation registered into pico you can completely modify the behaviour of your object without any change in it.

One of the main advantage of the IoC containers is to provide a simple way to unit tests your component. Indeed, by just registering a mock component implementation into pico in the setUp() method of a JUnit test, you can test the object easily outside the scope of any framework, server or anything like makes unit testing complex.


public void setUp() throws Exception {
  try {
    if (initService_) {
      ServicesUtil.addService("LogService", null, "exo.services.log.impl.LogServiceImpl",
			      getClass().getClassLoader());
  [...]

      

The way we use pico container is quite interesting. We first define a set of services API composed of WorkflowServices, DatabaseService, CMSService, HibernateService, CacheService, PortletContainerService, OrganizationService, XMLProcessingService, EcommerceService, MonitorService, CommunicationService...We try to extract abstract behaviours using interfaces and value objects to completely decouple the API from the implementation in order to be able to change the concrete behaviour of a service without any change in any other services or java objects that use it.

The implementation of each service is packaged as an independent JAR archive with an exo-service.xml file bundled with it. This XML file defines the classes to be registered in our ServiceContainer object :


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

<services>
  <name>DatabaseService</name>
  <service>
    <description>Database service</description>
    <class-name>exo.services.database.impl.DatabaseServiceImpl</class-name>
  </service>
  <service>
    <description>Hibernate service</description>
    <class-name>exo.services.database.impl.HibernateServiceImpl</class-name>
  </service>
</services>


      

Here is the schema of this XML file :

Figure 16. The eXo services scheme

The eXo services scheme

Each given class name represented is simply an implementation of one of the service API interfaces. When the ServiceManager singleton object is called for the first time it searches for all the exo-services XML file located in the classpath and register the service implementation class into a pico-container instance. With this automated discovery mechanism you only have to replace the implementation JAR to change the concrete implementation, the ServiceManager and pico-container takes care of the rest.


public class ServicesManager {

  private static ServicesManager ourInstance;
  private DefaultPicoContainer container;
  private Map servicesContext;

  public static ServicesManager getInstance() {
    if (ourInstance == null) {
      synchronized(ServicesManager.class) {
        ourInstance = new ServicesManager();
        if (Environment.getInstance().getPlatform() != Environment.STAND_ALONE){
          ourInstance.installServices() ;
        }
      }
    }
    return ourInstance;
  }

  private ServicesManager() {
    servicesContext = new HashMap();
    container = new DefaultPicoContainer();
  }

[...]

  private void installServices() {
    try {
      ClassLoader cl = Thread.currentThread().getContextClassLoader() ;
      JAXBContext jc = JAXBContext.newInstance("exo.services.model");
      Unmarshaller u = jc.createUnmarshaller();
      Enumeration e = cl.getResources("exo/services/exo-service.xml") ;
      while(e.hasMoreElements()) {
        URL url  = (URL) e.nextElement() ;
        InputStream serviceDescriptor = url.openStream();
        Services services = (Services) u.unmarshal(serviceDescriptor);
        ServiceContext serviceContext = new ServiceContext( cl, services);
        addService(serviceContext);
      }
    } catch (Exception ex) {
      ex.printStackTrace() ;
    }
  }
}

      

For example our PortletContainerService is based on the CMSService which is itself based on the DatabaseService; they are also all based on the LogService. All these services have well defined API interfaces which are used by the other dependent classes. Therefore if you have a log4j implementation of the LogService and that you would like to use a commons-logging one, you just have to change the LogService implementation, bundle that with an XML file that defines the new class to register in the ServiceManager in a JAR archive, and finally deploy it in the application server instead of the previous JAR. No other modifications are necessary in any other services that depends on the log one. And this is the case for all other services!

The ServiceManager object is implemented as a singleton that wraps pico-container. This lets us define a single IoC components repository, allowing for non IoC components like AspectJ aspect to also lookup the services. Each time we would like to make a component use the services in a type 3 fashion we just need to add it to the ServiceManager. This is what we have done for Portlets, PortletFilters, MessageListeners and ActionHandlers classes, they all are IoC type 3 components and can lookup the services from the unique repository within their constructor. Here is the example of the already shown PortletFilter that is - this time - given the LogService :


public class LoggerFilter implements PortletFilter{

  private LogService logService;
  private Log log;

  public LoggerFilter(LogService logService) {
    this.logService = logService;
    log = logService.getLog("exo.portal.container");
  }

  public void doFilter(PortletRequest portletRequest,
   		       PortletResponse portletResponse,
		       PortletFilterChain filterChain)
	throws IOException, PortletException {
    log.debug("------------->LOG FILTER  PRE");
    filterChain.doFilter(portletRequest, portletResponse) ;
    log.debug("------------->LOG FILTER  POST");
  }

  public void destroy() {
    log.debug("------------->LOG FILTER DESTROY");
  }
}

      

AspectJ aspects can not be registered in the ServiceManager, so to lookup a service from there some more work has to be done :


aspect PortletCacheAspect extends PortletBaseAspect {

  private PortletContainerConf conf;

  public PortletCacheAspect() {
    conf = (PortletContainerConf)ServicesManager.getInstance().getService(PortletContainerConf.class);
  }
[...]
}

      

Conclusion

We had quite a lot of feedback after our previous article, most of it about the complexity of the concepts we used - mainly AOP and IoC. We think these ideas are now better understood, but we have included more examples this time, especially in the IoC part. You can actually only grasp the power and ease of use of these paradigms having played a little with real life applications.

More than a simple marketing presentation, we hope to promote the best practises that IoC, TDD, XP and even AOP represent, by showing an open source application that tries to apply all these concepts. Total Quality is our ambitious aim!

We have split the article in several sections that may interest different readers : managers, portlet developers and platform architech/developers. Of course we hope that all parts can be read by everybody! Do not hesitate to contact us about the article, to comment on the TSS thread, and please feel free to ask any questions on our forum on the www.exoplatform.org community site.

Let's finish with an overview of the eXo platform functionalities

Figure 17. The eXo platform overview

The eXo platform overview

Resources

JSR 168 : the portlet API . First, if we had only one introduction article to suggest, we would advise you to read the chapte 4 (Concepts) of the portlet API specifications. This is a very clear presentation of what is a Portal, a Portlet Container and the way they interact.

JSR 127 : Java Server Faces

Inversion of Control resources

Related projects :


PRINTER FRIENDLY VERSION


News | Blogs | Discussions | Tech talks | Patterns | Reviews | White Papers | Articles | Media kit | About
All Content Copyright ©2006 TheServerSide Privacy Policy