Introducing the eXo Platform

Java Development News:

Introducing the eXo Platform

By Benjamin Mestrallet and Tuan Nguyen

01 Sep 2003 | TheServerSide.com

Introduction

This article overviews the eXo platform and of all its modules. It focuses on the architecture, underlining the design decisions we made while building the platform. Many aspects of the Java 2 Enterprise Edition (such as J2EE declarative security, servlet/jsp, EJB) have been used and the way we leverage them will be detailed. Note that some knowledge of UML [1] is required to understand some of the diagrams. A basic understanding of the J2EE platform would be helpful as well, although we will introduce most of the notions.

The binaries and the source code of the eXo platform 1.0 beta2 will be downloadable at the eXo site. The eXo platform only requires a Java virtual machine on your computer. Once you have installed it, you must point the JAVA_HOME environment variable to your java distribution directory. Go to exo-jboss/bin and launch the run script. Open your browser at http://localhost:8080/exo/faces/public/layout/blue-lagoon/portal.jsp.

If you want to build the platform from the source code that is in the SourceForge Current Versioning System (CVS) repository you will need to check out several modules :

  • the ExoBuild module

  • the ExoPortal module

  • the ExoServices module

  • the ExoPortletContainer module

  • the ExoDeployer

  • the ExoCMS module

  • the ExoPortlets module

  • the ExoCommon module

In ExoBuild you need to update build.properties and run ant build.exo.portal.

The eXo project started two years ago with the idea of providing enhanced communications tools (asynchronous and synchronous) to make online Math courses possible. After we had developed a Learning Management System (LMS), we decided to provide a custom web site as the basis for a Learning Content Management System (LCMS). The portal development started nine months ago. When we first planned to implement a portal we looked at the jetspeed cvs and found a portlet API draft that had been written and given to the community by Stefan Hepper from IBM. We read it, and as it was well designed, and decided to implement it. A bit later we realised that this draft was much like the last Websphere portlet API. When we finally discovered that Stefan Hepper was one of the spec leads of the JSR 168 team, and it became clear to us that implementing the Websphere API was the way to go...

Overview

For the time being, there are two distributions of the platform available (both under the GPL licence):

exo-express

This distribution does not include the Enterprise Java Bean (EJB) container. The organization model is built using Hibernate. The version is intended to be used by personal and community sites. Since we first advertised the platform on TheServerSide.com (/news/thread.tss?thread_id=21102), we did not have any problems with the online demo. The server we are using is an Athlon 750 with 300MB Ram running on Linux. We successfully handled 10K requests per day, the Java Virtual Machine (JVM) consummation never surpassing 80MB of Ram. However, stress tests will be done before the version 1.0 release.

exo-enterprise

This version of the platform has been designed to fulfill enterprise needs. Based on the exo-express version described above, it offers an EJB container and a workflow system. The organization model uses Hibernate but an EJB version can also be implemented. Note that this distribution consumes much more memory (between 128 and 256 MB for a normal load). The exo-enterprise is an enterprise level portal that can be used in any Intranet. Moreover, workflow functionalities allow you to add Business Process Management (BPM) and Enterprise Application Integration (EAI). The set of Business Processes (BP) will gradually grow to manage Human Resources and Customer Relationship (CRM) management and, finally, adapt to custom business logic. A partnership with a bank company has been established to introduce the eXo portal as the Intranet enterprise portal solution and to provide Human Resources business processes.

In the exo-express/ and the exo-enterprise/ directories we can find:

Figure 1. eXo express and enterprise main directory

eXo express and enterprise main directory


The lib/ and the exo-lib/ directories contain some specific library archives necessary to run the platform. The conf directory allows you to configure some deployment and security information as well as the login tool. If you need to debug something you should go to this directory and set up log4j.xml as you wish. The log directory will contain the log files as defined in the configuration file above.

There are four deployment directories, each having its own purpose. The container-deploy/ directory contains archives and files related to the platform - you will probably never have to use it. The deploy directory contains the web application archives (WAR) that are not portlets, as JBoss does it. The portlet-deploy directory is the place to drop your portlet WARs. Finally, the services-deploy directory allows you to drop all the eXo services archive files (.es files). Note that this directory structure has only been built for convenience; in fact, you can drop your archives in any x-deploy directory. However, we do not encourage you to do so.

All the archive files are hot deployable, which means that you can add/remove/replace them when the server is started.


Figure 2. eXo overview

eXo overview

The eXo deployers

For the time being, the eXo platform must be deployed in a Java application server that provides at least the hot deployment feature. However, we are currently working on a more general solution that would allow the adaptation of our portal to other servers. The default portal is built using JBossMX (the JBoss implementation of the Java JMX API) and its service architecture that provides an easy environment for deployers development. The ExoDeployers module is the application server dependant module.

Two deployers have to be created :

  • A portlet application deployer based on the web application deployer. The default web server is Tomcat but a Jetty deployer has been created. The deployer registers the portlet application in the portlet container.

    Our portlet deployer for JBoss is based on the JBoss-Tomcat deployer. We extend the EmbeddedCatalinaService41 to leverage the WAR deployer and Tomcat. The deployer is also registered in JBoss as the WebServer MBean. Our custom deployer unmarshalls the portlet.xml file, creates the PortletApplicationProxy object and registers it in the portlet container. The ServletContext and the class loader of the web application are put into the portlet application object. We will study the portlet container design in greater detail below:

    
    public class TomcatWebAndPortletDeployer extends EmbeddedCatalinaService41
        implements TomcatWebAndPortletDeployerMBean {
      ...
    
      protected void performDeploy(WebApplication webApplication, String warURL,
              WebDescriptorParser webDescriptorParser)
    throws Exception {
        super.performDeploy(webApplication, warURL,
    webDescriptorParser); //perform usual web application deployment
    
        URL url = new URL(warURL + "/WEB-INF/portlet.xml" );
        InputStream in = null;
        try {
          in = url.openStream();
    
          //unmarshall portlet.xml
          JAXBContext jc = JAXBContext.newInstance("exo.portal.container.portlet");
          Unmarshaller u = jc.createUnmarshaller();
          PortletApp portletApp_ = (PortletApp) u.unmarshal(in);
    
          //get the portlet container facade
          ExoPortletContainerFacade facade = ExoPortletContainerFacade.getInstance();
    
          //creates the PortletApplicationProxy to add to the eXo portlet container
          ServletContext sC = ((org.apache.catalina.core.StandardContext)
    webApplication.getAppData()).
              getServletContext();
          URLClassLoader cl = (URLClassLoader) ((org.apache.catalina.
    core.StandardContext) webApplication.getAppData()).
              getLoader().getClassLoader();
          PortletApplicationProxy portletApp =
       new PortletApplicationProxy(sC, cl, portletApp_);
    
          //register the portlet application to the container
          facade.addPortletApplication(portletApp);
        } catch (java.io.IOException e) {
          //not a portlet just a normal web application
        }
      }
    
      public void performUndeploy(String warURL) throws Exception {
        super.performUndeploy(warURL);//perform usual web application undeployment
        WebApplication appInfo = getDeployedApp(warURL);
        DeploymentInfo di = appInfo.getDeploymentInfo();
        InputStream in = di.localCl.getResourceAsStream("WEB-INF/portlet.xml");
        if (in != null) {
          ExoPortletContainerFacade facade = ExoPortletContainerFacade.getInstance();
          facade.removePortletApplication(appInfo.getName());
        }
      }
    }
    
                  

  • A service deployer that deploys Exo Services archives and registers them in the services container. It scans the services-deploy/ directory to detect new .es archives.

    This deployer code is quite different from the previous one as it implements SubDeployer and extends SubDeployerSupport. We tell the deployer which file extension to scan (.es). When the deployment occurs, we unmarshall the exo-service.xml file creating a Services object: The latter is put into the SeviceContext along with the class loader. Finally, this context is registered in the service container.

    
    
    public class ExoServicesDeployer extends SubDeployerSupport
        implements SubDeployer, ExoServicesDeployerMBean{
    
      //tells which extension to look for
      public boolean accepts(DeploymentInfo deploymentInfo)
       throws DeploymentException {
        String URLString = deploymentInfo.url.toString();
        return URLString.endsWith("es") || URLString.endsWith("es/");
      }
    
      [...]
    
      public synchronized void start(DeploymentInfo di) throws DeploymentException {
        super.start(di);
        InputStream in = di.localCl.getResourceAsStream("META-INF/exo-service.xml");
        JAXBContext jc = null;
        try {
          //unmarshall the exo service XML file
          jc = JAXBContext.newInstance("exo.portal.services.model");
          Unmarshaller u = jc.createUnmarshaller();
          Services services = (Services) u.unmarshal(in);
    
          //creates and register the Service Context object to the ServiceManager
          ServiceContext sC = new ServiceContext(di.localCl, services);
          ServicesManager.getInstance().addService(sC);
        } catch (JAXBException e) {
          e.printStackTrace();
        }
      }
    
      public void stop(DeploymentInfo di) throws DeploymentException {
        super.stop(di);
        InputStream in = di.localCl.getResourceAsStream("META-INF/exo-service.xml");
        JAXBContext jc = null;
        try {
          jc = JAXBContext.newInstance("exo.portal.services.model");
          Unmarshaller u = jc.createUnmarshaller();
          Services services = (Services) u.unmarshal(in);
          ServiceContext sC = new ServiceContext(di.localCl, services);
          ServicesManager.getInstance().removeService(sC);
        } catch (JAXBException e) {
          e.printStackTrace();
        }
      }
    
      [...]
    
    }
    
                  

The eXo kernel

The kernel of the platform is based on 5 modules, independant from the application server:

The services container

The services container is based on Pico so as to provide an Inversion of Control(IoC) services manager. Therefore, the services are not responsible for the instantiation of the components they depend on; object creation is delegated to a singleton object: the ServiceManager. This architecture provides a loosely coupled design where dependant services can be transparently exchanged at runtime.

Let's look at an example: imagine an organization service that manages users, groups and memberships between them. It needs a connection to the CMS services in order to persist the data. As we use Pico Container, the dependency must be declared in the OrganizationService constructor.


...
public OrganizationService {

  private CMSService service;

  public OrganizationService(CMSService service) {
    this.service = service;
  }

  //methods that use the CMSService interface
  ...
}

                

Both services are then registered in the manager service that will resolve the dependencies during the instantiation process. The implementation of the CMSService can be replaced at runtime by another one while still ensuring that the whole tree of dependencies will be resolved again. Our CMS implementation - using AspectJ - could be hot replaced by a JBossAOP implementation at runtime, without changing a single line code in the OrganizationService class. The newly deployed service only needs to implement the CMSService interface.


Figure 3. The services class diagram

The services class diagram

The services are registered in the manager by the service deployer that is application server dependant. To add a service in the container you or the deployer need to provide a ServiceContext object. This class references services under the same class loader. Each time a ServiceContext is added to the service manager, the main class loader is updated with the one given by the context, using the usual parent/children class loader paradigm. The Services themselves are described by Services classes that have been generated by JAXB. The model package on the previous class diagram is the generated one. The scheme used to create the Java classes goes as follows:


Figure 4. The eXo services scheme

The eXo services scheme

The deployer uses the associated XML files with JAXB in order to unmarshall the file and to populate the Java objects. The exo services have to be packaged in an Exo Services (.es) archive and deployed under the service-deploy/ directory. The exo-services.xml file has to be located under the META-INF/ directory of the archive.


<services>
  <!-- ================================================================== -->
  <!--                        EXO   SERVICES                              -->
  <!-- ================================================================== -->
  <name>OrganizationExoService</name>
  <service>
    <description>Organization service based on Hibernate</description>
    <class-name>exo.portal.services.organization.hibernate.Service</class-name>
  </service>
</services>

              

Thus, when the deployer - which is scanning the service-deploy/ directory - finds a new .es jar, it deploys it : the exo-services.xml file is then unmarshalled and the resulting object is put into the ServiceContext with the associated class loader. Then the manager rebuilds the object tree with the correct dependencies.

CMS AOP

The ExoCMS module is a content management module, that will implement JSR 170 as soon as it is available. This module is based on the composite design pattern which is described in the famous Gang of Four(GoF) book.


Figure 5. The composite design pattern

The composite design pattern

The ExoCMS module can be seen as a low level content management framework. In short, it provides a hierarchy organization of binary objects that can be stored in any database or a file system with a small and easy amount of code to implement for each storage system. In other words, to store XML documents, a native XML database can be used (eXist or Software AG's Tamino) while objects can be stored either in a relational database (using blogs) or in an object database. Each component may be either a node or a leaf. In the case of the eXo CMS, the directories can be seen as nodes while the files in those directories are the leaves. We have recreated an index table that is placed in memory and stored in a relational database with all the object metadata. The object leaves are loaded into memory from the custom persistence storage system and then cached with the file system table. Therefore the node does not directly reference its children or its parent, as in usual Java trees, but indirectly references them using a string URI with the same syntax conventions as the Unix file system.


Figure 6. The composite design pattern in the eXo case

The composite design pattern in the eXo case

Why the composite pattern? We used this pattern because we wanted to have a unique repository for all types of content and to provide the developer with a small set of powerful interfaces and extensible capabilities. Combining this pattern with aspect oriented programming (Aspectj), the eXo CMS offers all the features of a content management system such as caching, versioning, locking, indexing and searching. For indexing, we use the Lucene engine and for caching the Apache JCS project. Thus the client can access, add, remove and modify any content object in the repository via getNodeByUri() , addChild(), removeChild(), appendChild() and getParentNode().

The following AspectJ code shows two methods as well as the constructor of one of the ObjectNode aspects: the JDBCPersistentAspect. This aspect saves the object's metadata in a relational database, delegates the saving of the object itself to the ObjectNode.setNodeContent() method and caches the result in memory. It uses the JCS cache library from the Jakarta project as well as a JDBCHelper object. The constructor instantiates both the cache and the jdbc object helpers. The methods are the ObjectNode.appendChild(ObjectNode) and ObjectNode ObjectNode.save(). The ObjectNode class has the getUri() method that is the key used to cache the object. A syntax similar to the Unix filesystem is used: newChildUri = parentUri + "/" + newChild.getLocalName() to create a child uri ;



              aspect JDBCPersistentAspect {

                private JCS jcsCache_  ;
                private JDBCHelper jdbcHelper_ ;

                public JDBCPersistentInterceptor() throws Exception {
                  JCS.setConfigFilename("/exo/content/aop/common/cache.ccf") ;
                  jcsCache_ =  JCS.getInstance( "ExoCms" );
                  jdbcHelper_ =  new JDBCHelper() ;
                }

                ObjectNode around(ObjectNode parentNode, ObjectNode newChild) :
    target(parentNode) && call(ObjectNode
     ObjectNode.appendChild(ObjectNode))
                                     && args(newChild)
                {
                  try {
                    String parentUri = parentNode.getUri() ;
                    if (parentUri == null || parentUri.charAt(0) != '/') {
                      throw new Exception ("parent node need to save first") ;
                    }

                    if ("/".equals(parentUri)) {
                      parentUri = "" ;
                    }

                    String uri = parentUri + "/" + newChild.getLocalName() ;
                    newChild.setUri(uri) ;
                    newChild.setParentUri(parentNode.getUri()) ;
                    newChild = jdbcHelper_.saveNode(newChild);
                    jcsCache_.put( uri , newChild ) ;
                    return newChild ;
                  } catch (Exception ex) {
                    ex.printStackTrace() ;
                    throw new SoftException (ex) ;
                  }
                }

                ObjectNode around(ObjectNode node) :
                  target(node) && call(ObjectNode ObjectNode.save())
                {
                  try {
                    if (node.getUri() == null ) {
        throw new Exception ("Node is new , use append
      child instead") ;
                    }
                    node = jdbcHelper_.saveNode(node);
                    jcsCache_.put(node.getUri() , node) ;
                    return node ;
                  } catch (Exception ex) {
                    throw new SoftException (ex) ;
                  }
                }

                [...]

              }

              

Why another CMS? Before our decision to write the ExoCMS module, we spent a lot of time doing researche on the Jakarta-slide project. We even wrote a jetspeed portlet that could access the slide kernel, allowing you to modify/update the content of the slide repository. Despite the fact that Slide is undoubtedly a good project, it has a number of drawbacks such as its slow implementation speed and poor documentation. Moreover, the last stable release, 1.0.16, dates from Nov 2001.

This module is wrapped in a service that is accessed by the Portlet Container as well as by the Portal. Most of the persistence issues have been resolved using this module but it could be easily replaced by a custom persistence implementation.

Portlet Container

This eXo platform component can be used by any Portal provider. Its goal is to manage the portlet lifecycle as well as to interact with JSR 168 compatible portlets. The portlet container interacts with the CMS module in order to implement the persistent part required in the portlet API specifications. The module is based on the Pico Container so as to manage the lifecycle and the lazy instantiation. The eXo portlet container competes with the JSR 168 reference implementation, Pluto, provided by IBM.

The design of the container is simple. The ExoPortletContainerFacade, an implementation of the Facade design pattern, is the main entry point to the container. The deployers register PortletApplicationProxy objects using the addPortletApplication(PortletApplicationProxy) method. The equivalent remove method must be called when the portlet application is undeployed. The facade references an aggregation of PortletApplicationProxy objects. When the application is registered, the facade will call the protected methods of the application proxy to load, register and instantiate the portlets. The Pico Container is used underneath to provide instantiation facilities as well as the incoming laziness support. The next sequence diagram focuses on the init phase.


Figure 7. The eXo Portlet Container init sequence diagram

The eXo Portlet Container init sequence diagram

The PortletApplicationProxy object is created by the deployer itself and populated with the war class loader, the ServletContext, as well as with the JAXB generated portlet data objects (portlet.xml unmarshalling). Those classes contain the portlet class name, the portlet preferences and a lot of other information as defined in the portlet API specification. The portlets Class object is first loaded using the war class loader. Then, it is registered in the Pico container. Finally all the objects in the container (portlets and validators) are instantiated. This part will be improved as soon as lazy instantiation becomes possible with Pico.


Figure 8. The eXo Portlet Container class diagram


The portal also has to deal with the ExoPortletContainerFacade. In the service provider (spi) package, you can find the Input and the Output objects, used when the portal accesses the facade. Those classes are the argument objects used when the portal calls the processAction() and render() methods. The portlet API specifications define two phases in the portlet object runtime: action and render. The ActionInput and the RenderInput are the processAction and the render method arguments, while the ActionOutput and the RenderOutput are the outputs, returned to the portal by the methods. When the Input objects are created, they are populated with data, such as the windowId to which the portlet access is linked, the userId and the user attributes Map. (Note that the windowId is the association of a Portlet and a PortletPreferences object, as defined in the spec). The output objects, returned to the portal, contain all the information needed to render and save the state of the portlet.


Figure 9. The Input and Output objects class diagram


The portletAPIImp package contains the implementation of all the objects defined in the portlet API specification. Most of the objects are placed in a pool (we use the Jakarta common pool utility for that) as they have a request time lifecycle. This is the case for the PortletSessionImp, the ActionRequestImp, the ActionResponseImp, the RenderRequestImp, the RenderResponseImp and of all the other ServletRequest and ServletResponse custom wrappers we use. The package also contains the PersistenceManager interface and its CMS implementation. This interface allows the portlet container to delegate the storage of PortletPreferences objects (remember that each portlet has a PortletPreference object that is defined per user window id) and to get back to the stored info when the user portlet is used again. Note that the object caching has to be managed by the persistence engine as is the case in our CMS implementation. Finally, the package contains the tag subpackage where all the required tags are implemented.


public interface PersistenceManager {

  public void storePreferences(String userId, String windowId,
   PortletPreferencesImp prefs)
      throws java.io.IOException;

  public PortletWindowInternal getWindow(String userId, String windowId);

}

                

Many required parts of the portlet API specification have been implemented using AOP and AspectJ. However, as portlets are hot deployed, the weaving of aspects has to be done at runtime. Therefore we use a custom class loader in order to load portlet classes, which allows us to use AspectJ and to add the byte code modified portlet classes to the Pico container.


Figure 10. The Portlet Aspects

The Portlet Aspects

  • The monitor aspect is used to monitor the number of times the portlet is reached, so as to manage the memory and to decide which portlet to remove.

  • The exception handling aspect manages the UnavailableExceptions defined in the portlet API. These exceptions can contain a time period during which the portlet can not be called any more. If this time period is not due, the aspect will not call the next one.

  • The security aspect uses the incoming PortletRequest to compare the roles of the Principal, that is calling, to the roles required (and defined in the portlet.xml) in order to access the Portlet object. The main authentication is provided by the portal. The aspect only leverages the J2EE declarative security methods that can be accessed using the ServletRequest.

  • The cache aspect is rather self explanatory. It caches the rendering and the title of the portlet so that the portlet object is not reached each time. This content is unique per user and so is the cache.

  • The last aspect manages portlet filters. This feature is not part of the portlet API but it may be so in the next versions. As we thought it was a great functionality, we provided it. It works exactly in the same way as servlet filters.

Portal

Our portal is based on Java Server Faces(JSR-127), which is a new web framework paradigm. It provides a real MVC reference architecture allowing developers to deal with components and events on the web layer almost in the same way as they would with rich Swing clients. The portal interacts with the container, described above, in order to get the portlet contents and to retrieve users' preferences from the CMS AOP.

JavaServer Faces allows you to build a tree of Java objects that describes the components of a page. This tree is walked through several times during the request lifecycle before the last phase - the rendering phase - is accessed. We will not get into the details of these phases but we will describe some interesting features they offer. The tree components are associated with renderers that create the output mark-up language. These components can also create events to be delegated to Action listener objects. For example, the UIPage component creates an event when the user clicks on a toolbar link. This ActionEvent object is created when the decode() method of the component renderer is called. The UIPage that has been selected by the user is placed in the event.


  public void decode(FacesContext facesContext, UIComponent uiComponent)
      throws IOException {
    UIPage uiPage = (UIPage) uiComponent ;
    HttpServletRequest request = (HttpServletRequest)
  facesContext.getServletRequest();
    String portalAction = request.getParameter("portalAction");
    if (portalAction != null && "changePage".equals(portalAction)) {
      String pageId = request.getParameter("pageId") ;
      if (pageId.equals(uiPage.getId())) {
        ActionEvent event = new ActionEvent(uiComponent, portalAction);
        facesContext.addFacesEvent(event);
      }
    }
  }

                

In the ActionListener processAction() method, the event is processed. In the above example, the listener will go through all the children of UIPortalPages that are the "brothers" of the UIPage given with the event. Only the page selected by the user will be marked as such. When the rendering phase occurs, only the selected page is rendered.


  public void processAction(ActionEvent action)
  throws AbortProcessingException {
    UIPage uiPage = (UIPage)action.getComponent();
    UIPortalPages uiPortalPages = (UIPortalPages) uiPage.getParent() ;
    Iterator i =  uiPortalPages.getChildren() ;
    while (i.hasNext()) {
      UIPage child = (UIPage) i.next() ;
      child.setSelectedPage(false) ;
    }
    uiPage.setSelectedPage(true) ;
  }

                


Figure 11. Components in a page

Components in a page

Our portal page includes several components that are used to build the JSF tree :

  • UIPortalPages : This is the root component. It references an aggregation of UIPortal objects.

  • UIPage : a page is what can be seen on the portal screen. The user can choose the page using the toolbar. Each page is composed of several main columns.

  • UIMainColumn :The user can - using the customized portlet - define the width of the column as well as the style and the Renderer to use.

  • UITab : To allow a high level of document sorting, each MainColumn can contain several Tabs, as this is the case in the News page of the online demo. Each tab can be split into several columns.

  • UIColumn : the user can also define the width of those second level columns. These columns contain the portlets.

  • UIPortlet : finally, the UIPortlet keeps the state and the mode info. This object can be seen as a proxy to the real javax.portlet.Portlet object. It interacts with the portlet container using the ExoPortletContainerFacade singleton. It builds the Input objects and gets the Output objects in return so as to update its state.


Figure 12. The JSF eXo tree components class diagram


The security model is based on J2EE declarative security. In the web.xml file, you define an exo-domain and the URL it should map to:


  <login-config>
    <auth-method>FORM</auth-method>
    <realm-name>exo-domain</realm-name>
    <form-login-config>
      <form-login-page>/login.jsp</form-login-page>
      <form-error-page>/login.jsp</form-error-page>
    </form-login-config>
  </login-config>

  <security-constraint>
    <web-resource-collection>
      <web-resource-name>user authentication</web-resource-name>
      <url-pattern>/faces/private/*</url-pattern>
      <http-method>POST</http-method>
      <http-method>GET</http-method>
    </web-resource-collection>
    <auth-constraint>
      <role-name>user</role-name>
    </auth-constraint>
    <user-data-constraint>
      <transport-guarantee>NONE</transport-guarantee>
    </user-data-constraint>
  </security-constraint>

                

The /faces/private/* URL requires the user to be authenticated and authorised to be viewed (he needs the "user" role) . If a request to the URL is made by a user whose security context (association of Principal and credentials objects) has not been created yet, then the login.jsp page is accessed. A form with the login and the password will have to be filled by the user. This information is then compared with the backend system (databases, LDAP directory...). A logged-in user can tell which part of the JSF tree he wants to make public. If anyone uses public instead of private in the URL associated with a user site, he will be allowed to access this public information (like an enhanced weblog).

Let's look at an example. When you access the site for the first time, the URL is: http://server:port/exo/faces/public/layout/blue-lagoon/portal.jsp. This simply means that you access the anonymous user public pages. If you have the correct password for the anonymous user, you will be able to set the main site layout, style and portlets. While you are logged in as the anonymous user the URL is: http://server:port/exo/faces/private/layout/blue-lagoon/portal.jsp. If you register a new user, let's say toto, and you log in with this user, the URL is http://server:port/exo/faces/private/layout/blue-lagoon/portal.jsp again. This time, using the customizer's portlet, you can choose to let other users view some pages from your site with the public/private keywords in the GUI tool. Once you have saved this new configuration, other users only need to point to the URL http://server:port/exo/faces/public/layout/blue-lagoon/portal.jsp?user=toto to view your public pages. Finally, the admin user can choose any of those user public pages to be the main site. In other words, with one eXo platform, you can build many custom dynamic websites. This entire mechanism is based on servlet filters that we add using the "url-mapping" tag in the web.xml file of the exo.war archive.

The eXo portlet framework

The eXo portlet framework helps portlet developers to build portlet applications. It is a framework à la Struts but with a portlet specific design. It offers to portlet developers the possibility to manage all action mappings of all portlet modes in a single XML file. The latter also includes exception handling and dynamic action classes. The framework is also a full IoC framework, which allows Action classes to use services that have not been created by the classes themselves. If you are familiar with struts and the Model View Controller (MVC) type 2, the learning curve will be really small.

The best practices established for Servlet/JSP programming also apply to portlet development. The MVC model is still accurate, taking the portlet as the controller, some JavaBeans as the model and the jsp as the view. Struts is the best example of such an architecture in the web world.


Figure 13. The MVC paradigm

The MVC paradigm

In the portlet scope, the portlet acts as the controller. Whether inside the portlet or using a helper, the controller creates the model object - JavaBeans - via access to the Enterprise Information System (an EJB layer, any database or ERP...). The portlet then dispatches the request to the correct view. The jsp page takes the previously created object and uses it to render the output mark-up language. The next section introduces our own MVC portlet framework that highly reduces portlet development time. It is inspired from Struts, so any Struts developer should be familiar with it.

You map a Controller class to a portlet in the portlet.xml file using the exo.portal.portlet.ExoPortletFramework portlet.


          <portlet>
            <description lang="EN">Hello Framework</description>
            <portlet-name>HelloPortletFramework</portlet-name>
            <display-name lang="EN">Framework</display-name>
            <portlet-class>exo.portal.portlet.ExoPortletFramework</portlet-class>

            <init-param>
              <description>Portlet Controller</description>
              <name>controller-url</name>
              <value>controllers.xml</value>
            </init-param>

            <init-param>
              <description>jsp files location</description>
              <name>template-location</name>
              <value>/WEB-INF/templates/html/</value>
            </init-param>

            [...]

        

In the controllers.xml file you bind incoming action parameters to ActionHandler classes. If the business logic from the class does not throw any exception, the request is redirected to the jsp defined in the controllers.xml file; otherwise, another error.jsp file is used. The DynamicAction class is provided to redirect the request to the jsp page, without doing anything in the ActionHandler class.


<controllers>
  <name>Hello Portlet Framework</name>
  <identifier>Hello Portlet Framework</identifier>
  <view-controller>
    <default-action>ShowHelloWorld</default-action>
    <action name="HelloAction" class="framework.HelloAction">
      <forward name="success" page="HelloAction.jsp"/>
      <forward name="error" page="HelloAction.jsp"/>
    </action>
    <action name="ShowHelloWorld" class="exo.portal.portlet.actions.DynamicAction">
      <forward name="success" page="HelloWorld.jsp"/>
      <forward name="error" page="HelloWorld.jsp"/>
    </action>
  </view-controller>
  <help-controller>
    <default-action>HelloHelpMode</default-action>
    <action name="HelloHelpMode" class="framework.HelloHelpMode">
      <forward name="success" page="HelloHelp.jsp"/>
      <forward name="error" page="HelloHelp.jsp"/>
    </action>
  </help-controller>
  <edit-controller>
    <default-action>HelloEditMode</default-action>
    <action name="HelloEditMode" class="framework.HelloEditMode">
      <forward name="success" page="HelloEdit.jsp"/>
      <forward name="error" page="HelloEdit.jsp"/>
    </action>
  </edit-controller>
</controllers>

        

As there are several modes that can be supported by a portlet (VIEW, HELP and EDIT), there are also several blocks in the controller that define specific actions within each mode. Each mode controller defines a default action that is used when no action parameter is provided (nothing has been added to the portlet request). This happens, for example, when the portlet is first executed. The action parameter dispatches the request to an action class. The latter must extend the ActionHandler class.

Here is an exmple of the code you can find in an ActionHandler class. As the ActionHandler is a component of the IoC service container, it can use services without instantiating them itself; it only has to get them from the constructor as required by the Pico Container.


package framework ;

import javax.portlet.ActionRequest ;
import javax.portlet.PortletContext ;
import exo.portal.portlet.actions.ActionHandler ;

public class HelloAction extends  ActionHandler {

  private DumbService dumbService;

  public HelloAction(DumbService service){
    this.dumbService = service;
  }

  public void init() throws Exception {
  }

  public void execute(PortletContext context, ActionRequest request)
     throws Exception {
    String name = request.getParameter("name");
    request.setAttribute("name", name) ;
    service.dumbMethod();
  }
}

        

When a portlet with ActionHandlers or a service is redeployed, the whole new dependencies object is resolved. The portlet can then use - at runtime and without any changes in its code - this newly deployed service.


Figure 14. The JSF eXo portlet framework class diagram

The JSF eXo portlet framework class diagram

The eXo portlets

Finally, we provide a bunch of portlets that can be classified in several categories :

Core portlets

Without those portlets, the portal would be useless. It includes all the administration portlets such as registration, login/logout, monitors, the layout customization portlet and the edit portlet (called FilePortlet) that allows identified users to create the content of the portlets using a “What You See Is What You Get” editor (WYSIWYG). The created document is then saved in the CMS directory. In the enterprise distribution, the workflow is accessible through portlets. All those portlets have been built using the eXo portlet framework.

Technology wrappers portlets

One of the big advantages of the portlet API is that it allows you to use several technologies within the portlets even if the portal itself is built upon some other technology (such as JSF for eXo). Thus, we provide technology specific portlets like Velocity, Cocoon and JSF. There is also an IFrame portlet that allows you to include another web application in the portal, making it possible to use PHP, ASP or CGI applications in portlets.

Basic JSR 168 portlets

These portlets, such as the Sun samples or the calendar, can be added or removed simply from the eXo portal. As the API is a standard, the portlets market may grow considerably. It may be possible to buy portlets to connect to Enterprise Resource Planning (ERP), Supply Chain Management (SCM) or CRM software, which would work in the same way as if the portlets were deployed on the Weblogic portal or the eXo portal. We will probably see many portlet applications in the coming years, both commercial and free ones.


Figure 15. A summary

A summary

Conclusion

We hope that you've grasped the big picture of the eXo platform, which was built upon a service micro-kernel to be as "agile" as possible. We encourage you to download the sources, play with them and to join us; there is still much work to do in many areas.

We've tried to introduce most of the aspects of the project's architecture, but some modules - like the enhanced communications tools and the workflow integration - have been left for further articles and tutorials.


Resources


About the Authors

Benjamin Mestrallet is a French computer science engineer and has a DEA in Management Science from the University of Paris IX Dauphine. He is currently doing research on "Object and Aspect Modelisation of operational Business Processes". He founded the eXo project in late 2002 and released the first source code in December 2002.

Tuan Nguyen is a Java developer who graduated with a bachelor's degree in 2000 from Concordia University, Montreal, Canada. From 2000 to 2002 , he worked for Azerity Inc in Silicon Valley. In early 2003, he went back to Canada and worked as an independant consultant. He joined Exo Team in May 2003.

The eXo kernel (CMS, portlet container, portal and some core portlets) is distributed under the GNU/GPL licence. The Portlet framework as well as the Services container and some non-core portlets are distributed under the GNU/LGPL licence. Therefore, compagnies who would like to bundle the eXo platform with their application must either distribute their code under the GNU/GPL licence (copyleft) or contact the eXo develoment team to create a custom licence. The eXo development team also provides customized - business specific - portlets and business processes development, as well as consulting and support on the platform integration.