6.1.8. JSF2 portlet with CDI

In previous section, you have learnt to write JSF2 portlet. In this section, your JSF2 portlet will utilize CDI (Contexts and Dependency Injection).

This section will not explain JBoss Portlet Brigde again, so get back to the previous section if necessary.

The code sample can be found here.

Note

Currently you could not use JSF together with CDI in Tomcat. This tutorial is for JBoss only.

Also note that the deployment of this portlet does not follow Portlet deployment section completely, so do not miss the deployment part at the end of this tutorial.

So why CDI?

If you want to get a quick understanding about CDI, and current Dependency Injection frameworks, this introduction may help. As CDI is a part of Java EE specification, Oracle's Documentation is always recommended.

Note this tutorial sticks with Weld, CDI implementation of JBoss.

In this tutorial, you learn the basic CDI via an example, in which you use @Inject annotation, with some Scopes and Qualifiers.

In the example, your JSF2 portlet is a form in which users input email subject/body and press buttons to send emails. There are two kinds of recipients - "customers" and "partners" - so you have two buttons. See the screenshot below:

The To mail lists are different in the two cases. So "customers" addresses are provided by a Bean, and "partners" are provided by a modified one of that Bean. Both are at ApplicationScoped.

The From field will be the email of the logged-in user. It is provided by another Bean at SessionScoped.

The base idea of CDI is: your application (the portlet in this example) does not create the Mail list providers, but let CDI create and manage the lifecycle of them, so the application always gets the same object for the same context. As important as that, it is CDI which knows the chain of the dependencies, not the application.

The three Beans are declared by annotations, but need to be packaged together with a beans.xml file.

Now let's start your project. Again, see the full code sample at GitHub.

  1. Create a Maven project with the following structure:

  2. In pom.xml, add the dependencies of JSF, JPB and CDI, and also some eXo dependencies to work with eXo Mail and Social services.

    
    <!-- CDI (Contexts and Dependency Injection) -->
    <dependency>
        <groupId>javax.inject</groupId>
        <artifactId>javax.inject</artifactId>
        <version>1</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.enterprise</groupId>
        <artifactId>cdi-api</artifactId>
        <version>1.0-SP4</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.gatein</groupId>
        <artifactId>cdi-portlet-integration</artifactId>
        <version>1.0.3.Final</version>
        <scope>runtime</scope>
    </dependency>
    <!-- eXo -->
    <dependency>
        <groupId>org.exoplatform.core</groupId>
        <artifactId>exo.core.component.security.core</artifactId>
        <version>2.5.13-GA</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.exoplatform.social</groupId>
        <artifactId>social-component-core</artifactId>
        <version>4.2.0</version>
        <scope>provided</scope>
    </dependency>
  3. Edit the WEB-INF/beans.xml file:

    
    <?xml version="1.0"?>
    <beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://jboss.org/schema/cdi/beans_1_0.xsd">
        <!-- This file is required to enable CDI for this web-app. There is nothing 
            here because the beans will be declared using annotations. In case your beans 
            are packaged in jar, this file should be placed under META-INF/ folder. -->
    </beans>
  4. Edit the WEB-INF/portlet.xml file. You need to configure the portlet filter to the org.gatein.cdi.PortletCDIFilter class.

    
    <?xml version="1.0" encoding="UTF-8"?>
    <portlet-app version="2.0"
        xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd
      http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd">
        <portlet>
            <portlet-name>jsf2portlet-cdi-example</portlet-name>
            <portlet-class>javax.portlet.faces.GenericFacesPortlet</portlet-class>
            <init-param>
                <name>javax.portlet.faces.defaultViewId.view</name>
                <value>/pages/main.xhtml</value>
            </init-param>
            <init-param>
                <name>javax.portlet.faces.preserveActionParams</name>
                <value>true</value>
            </init-param>
            <expiration-cache>0</expiration-cache>
            <supports>
                <mime-type>text/html</mime-type>
                <portlet-mode>VIEW</portlet-mode>
            </supports>
            <portlet-info>
                <title>JSF2 Portlet CDI</title>
            </portlet-info>
        </portlet>
        <filter>
            <filter-name>PortletCDIFilter</filter-name>
            <filter-class>org.gatein.cdi.PortletCDIFilter</filter-class>
            <lifecycle>ACTION_PHASE</lifecycle>
            <lifecycle>EVENT_PHASE</lifecycle>
            <lifecycle>RENDER_PHASE</lifecycle>
            <lifecycle>RESOURCE_PHASE</lifecycle>
        </filter>
        <filter-mapping>
            <filter-name>PortletCDIFilter</filter-name>
            <portlet-name>jsf2portlet-cdi-example</portlet-name>
        </filter-mapping>
    </portlet-app>
  5. Edit the WEB-INF/web.xml file. It is the same as the basic JSF2 portlet, so not repeated here.

  6. Edit the pages/main.xhtml file.

    
    <f:view id="ajaxEcho" xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html">
        <h:head />
        <h:body>
            <h2>JSF 2 portlet</h2>
            <h:form id="form1">
                Subject: <h:inputText id="subject" autocomplete="off" value="#{mailSender.subject}"></h:inputText>
                <br/>
                Message: <h:inputTextarea id="body" value="#{mailSender.body}"></h:inputTextarea>
                <br/>
                <h:commandButton id="sendCustomer" value="Send Customers" actionListener="#{mailSender.sendCustomers}"></h:commandButton>
                <br/>
                <h:commandButton id="sendPartners" value="Send Partners" actionListener="#{mailSender.sendPartners}"></h:commandButton>
            </h:form>
        </h:body>
    </f:view>
  7. Create the MailList.java interface:

    package org.exoplatform.samples.jsf2portlet.cdi;
    
    
    public interface MailList {
      
      public String getMailList();
    }

    There will be two implementations of this interface. In companion with CDI, you annotate the two with Qualifiers. For that, you will create two qualifiers, Customer and Partner.

  8. Edit the two qualifiers. In Customer.java:

    package org.exoplatform.samples.jsf2portlet.cdi;
    
    
    import static java.lang.annotation.ElementType.FIELD;
    import static java.lang.annotation.ElementType.METHOD;
    import static java.lang.annotation.ElementType.PARAMETER;
    import static java.lang.annotation.ElementType.TYPE;
    import java.lang.annotation.Retention;
    import static java.lang.annotation.RetentionPolicy.RUNTIME;
    import java.lang.annotation.Target;
    import javax.inject.Qualifier;
    @Qualifier
    @Retention(RUNTIME)
    @Target({TYPE, METHOD, FIELD, PARAMETER})
    public @interface Customer {}

    And do the same with Partner.java.

  9. Implement the MailList interface. Use the qualifier Customer in CustomerMailList.java:

    package org.exoplatform.samples.jsf2portlet.cdi;
    
    
    import javax.faces.bean.ApplicationScoped;
    import javax.faces.bean.ManagedBean;
    @ManagedBean
    @ApplicationScoped
    @Customer
    public class CustomerMailList implements MailList{
      
      public String getMailList() {
        return "user1@example.com, user2@example.com";
      }
    }

    Do it similarly in PartnerMailList.java, use the qualifier Partner.

  10. Edit UserBean.java. This bean provides the current user email, so its scope should be SessionScoped.

    package org.exoplatform.samples.jsf2portlet.cdi;
    
    
    import javax.faces.bean.ManagedBean;
    import javax.faces.bean.SessionScoped;
    import org.exoplatform.container.ExoContainerContext;
    import org.exoplatform.services.security.ConversationState;
    import org.exoplatform.social.core.manager.IdentityManager;
    import org.exoplatform.social.core.identity.model.*;
    import org.exoplatform.social.core.identity.provider.OrganizationIdentityProvider;
    @ManagedBean
    @SessionScoped
    public class UserBean {
      
      private String userEmail;
      
      public UserBean() {
        IdentityManager identityManager = (IdentityManager) ExoContainerContext.getCurrentContainer().getComponentInstanceOfType(IdentityManager.class);
        String currentUserId = ConversationState.getCurrent().getIdentity().getUserId();
        Identity currentIdentity = identityManager.getOrCreateIdentity(OrganizationIdentityProvider.NAME, currentUserId, false);
        Profile profile = currentIdentity.getProfile();
        userEmail = profile.getEmail();
      }
      
      public String getUserEmail() {
        return userEmail;
      }
    }

    Now you have all dependencies that your JSF portlet will use. So let's finish the portlet.

  11. Edit the MailSender.java file:

    package org.exoplatform.samples.jsf2portlet.cdi;
    
    
    import javax.inject.*;
    import javax.faces.bean.*;
    import org.exoplatform.services.mail.MailService;
    import org.exoplatform.services.mail.Message;
    import org.exoplatform.commons.utils.CommonsUtils;
    @ManagedBean
    public class MailSender {
      
      private String subject, body;
      
      @Inject @Customer MailList customerMailList;
      @Inject @Partner MailList partnerMailList;
      @Inject UserBean userBean;
      
      public String getSubject() {
        return subject;
      }
      public void setSubject(String subject) {
        this.subject = subject;
      }
      public String getBody() {
        return body;
      }
      public void setBody(String body) {
        this.body = body;
      }
      
      public void sendCustomers() {
        Message message = new Message();
        message.setSubject(subject);
        message.setBody(body);
        message.setFrom(userBean.getUserEmail());
        message.setTo(customerMailList.getMailList());
        
        try {
          ((MailService) CommonsUtils.getService(MailService.class)).sendMessage(message);
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
      
      public void sendPartners() {
        Message message = new Message();
        message.setSubject(subject);
        message.setBody(body);
        message.setFrom(userBean.getUserEmail());
        message.setTo(partnerMailList.getMailList());
        
        try {
          ((MailService) CommonsUtils.getService(MailService.class)).sendMessage(message);
        } catch (Exception e) {
          e.printStackTrace();
        }
      } 
    }

Deployment in eXo Platform JBoss

Your webapp needs to be scanned by Weld so you will not deploy it in standalone/deployments as other portlet applications. Instead, deploy it into platform.ear and add a module in application.xml.

  1. Deploy target/jsf2portlet-cdi-example.war into $PLATFORM_JBOSS_HOME/standalone/deployments/platform.ear.

  2. Edit $PLATFORM_JBOSS_HOME/standalone/deployments/platform.ear/META-INF/application.xml to add a module like the following. The module must be added before the starter module, so on top if you like that:

    
    <module>
        <web>
            <web-uri>jsf2portlet-cdi-example.war</web-uri>
            <context-root>jsf2portlet-cdi-example</context-root>
        </web>
    </module>

Then follow the Portlet deployment section to register and add the portlet to a page for testing.

Note

Starting from eXo Platform 5.0, we upgraded to JBoss EAP 7.0 which uses Contexts and Dependency Injection (CDI) 1.2.

CDI 1.2 comes with the new notion of implicit bean archive allowing to scan war archives for annotations to process by Weld (the JBoss implementation of CDI). This new feature has some conflicts with our development and thus it has been disabled by default for eXo Platform EAR including its addons.

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