CDI Support in GateIn PC

Version 3

    Status

     

    Not started. Will be developed by Ken Finnigan from Red Hat.

     

    Description

     

    Currently CDI does not work in any non JSF portlet, ie. its not possible to inject CDI beans into a class that extends GenericPortlet. We'd like to add the ability for non JSF portlets to take advantage of JSF as well.

     

    With CDI working for a portlet class, any @RequestScoped beans would not function as a portlet developer would expect. This is because any beans declared with @RequestScoped are created new at the start of ActionRequest and RenderRequest, so if a portlet triggers an ActionRequest any data set on a bean in that request will be lost before the portlet is rendered again. Need to be able to support a bean being in scope of an entire portlet lifecycle request.

     

    Solution

     

    Enabling CDI for a non JSF portlet will involve the instance of the portlet class loaded by the Portlet Container having CDI injection directly performed on the portlet class before the container is started.

     

    To solve the @RequestScoped problem we will create two new CDI scopes for portlet development.

     

    @PortletLifecycleScoped

     

    This scope context will be active through Action -> Event -> Render so that each request in that lifecycle can access the same instance of a bean. For a Resource request, each request will not share bean instances either with the normal lifecycle or other resource requests.

     

    @PortletRedisplayScoped

     

    This scope context is similar to @PortletLifecycleScoped except that this scope will be terminated when:

    • A new ActionRequest occurs
    • A new EventRequest occurs after a full lifecycle has completed
    • A ResourceRequest navigates to a new view
    • Limitations in available resources require it to be garbage collected

     

    Any ResourceRequest will start with an empty scope context, ie. all beans will be created as required, and at the end of the request any beans that were instantiated will be added into the @PortletRedisplayScoped context. Note that this will overwrite any existing beans of that bean type that are already present within the scope context.

     

    Specification

     

    CDI for non JSF portlets

     

    To support the ability to perform injection into the portlet class used by the Portlet Container, while de-coupling the use of CDI from the Portlet Container, we need to:

     

    1. Add CREATED to LifeCycleStatus enumeration.

     

    2. Modify org.gatein.pc.portlet.container.object.PortletContainerObject by adding the following methods to it:

     

    void create() throws Exception;
    void destroy();
    Portlet getPortletInstance();
    

     

    3. Modify org.gatein.pc.portlet.container.managed.ManagedPortletContainer by adding the following method:

     

    Portlet getPortletInstance();
    

     

    4. Modify org.gatein.pc.portlet.impl.container.PortletContainerLifeCycle by:

     

    Modifying invokeStart() such that this:

     

    portletContainer.start();
    

     

    becomes:

     

    portletContainer.create();
    
    getListener().onEvent(new ManagedObjectLifeCycleEvent(this, LifeCycleStatus.CREATED));
    
    portletContainer.start();
    
    

     

    Modifying invokeStop() such that this:

     

    portletContainer.start();
    

     

    becomes:

     

    portletContainer.stop();
    
    getListener().onEvent(new ManagedObjectLifeCycleEvent(this, LifeCycleStatus.STOPPED));
    
    portletContainer.destroy();
    
    


    5. Modify org.gatein.pc.portlet.impl.deployment.PortletApplicationDeployer by adding a new protected method

     

    protected PortletApplicationDeployment createPortletApplicationDeployment(ServletContext webApp, PortletApplication10MetaData metaData) {
        PortletApplicationDeployment deployment = new PortletApplicationDeployment(broadcaster, webApp, metaData);
        return deployment;
    }
    

     

    Then replace the line from PortletApplicationDeployer.add() where it creates the PortletApplicationDeployment with a call to the protected method.

     

    6. Create the following class in the CDI module (along with the implementations from below):

     

    public class CDIInjectionListener implements ManagedObjectRegistryEventListener {
        public void onEvent(ManagedObjectRegistryEvent event) {
            if (event instanceof ManagedObjectLifeCycleEvent) {
                ManagedObjectLifeCycleEvent lifeCycleEvent = (ManagedObjectLifeCycleEvent) event;
                ManagedObject managedObject = lifeCycleEvent.getManagedObject();
    
                if (managedObject instanceof ManagedPortletContainer) {
                    ManagedPortletContainer portletContainer = (ManagedPortletContainer) managedObject;
                    Portlet portlet = portletContainer.getPortletInstance();
    
                    LifeCycleStatus status = lifeCycleEvent.getStatus();
                    if (LifeCycleStatus.CREATED == status) {
    
                        // Perform injection with CDI
                    } else if (LifeCycleStatus.STOPPED == status) {
                        // Perform cleanup with CDI 
                    }
    
                }
            }
        }
    }
    

     

    7. Override the createPortletApplicationDeployment method in org.exoplatform.portal.pc.ExoPortletApplicationDeployer with the following:

     

    protected PortletApplicationDeployment createPortletApplicationDeployment(ServletContext webApp, PortletApplication10MetaData metaData) {
        this.addListener(new CDIInjectionListener());
        return super.createPortletApplicationDeployment(webApp, metaData);
    }
    

     

    @PortletLifecycleScoped and @PortletRedisplayScoped

     

    @PortletLifecycleScoped and @PortletRedisplayScoped annotations will be created within the gatein-api project, so they may be used by both the code that is implementing the scope contexts and for developers to annotate their beans with it during portlet development.

     

    At this stage it is anticipated that the scope contexts will be stored within the user session between requests.

     

    The implementation of the scope contexts, and associated objects, doesn't seem to have a clear place to reside. I see two possibilities:

    1. Create a new repository under the GateIn organization on github called gatein-cdi
    2. Create a new cdi component module under gatein-portal/component

     

    I would only lean towards option 1 as it is not expected to have constant change like gatein-portal does, but am not wedded to either location in particular.

     

    As part of integrating these scope contexts into GateIn, we will also extend the AS7 packaging to detect situations in which a portlet application is being deployed that is CDI enabled, so that we can add the module containing these scope contexts, and the CDI extension that activates them, onto the classpath of the application. It would also add a service file for the extension into the application so that the scope contexts would be activated on CDI startup for the application.