Since the last upgrade of Liferay to version 7 and OSGi architecture, we have noticed that some developers are getting confused. The Liferay team threw away the old and brought in the new. In the past, developers could create a simple Java web project with the required portlet properties files and include any required dependencies. The deployed project will work effortlessly. Liferay, as portlet container, uses a modular approach to building web functionalities through the implementation of JSR 168 – 286. Where is the issue, one might ask. Re-using Liferay services when developing a web application as a portlet has changed. Liferay services and API have been split into OSGi modules. Therefore, developers have to use different mechanism to access them. The aim of this post to help developers with a quick start to writing application on Liferay 7 CE and DXP.

Pre-requisite

In order to follow this simple tutorial, the following tools will be required:

Liferay 7 GA 4 CE
Maven
Your IDE of choice (I’m using NetBeans 8.2)

The tutorial will be using Java Server Faces (JSF) 2.2 and PrimeFaces.

Creating a new Liferay JSF portlet

We are using Liferay Maven artefacts to create a new JSF Primefaces portlet


mvn archetype:generate -Dfilter=liferay

and choose the artefact for Primefaces. If you are wondering which artefacts are compatible with Liferay CE 7.0; Those prefixed with com.liferay.project.templates.[TYPE] or com.liferay.faces.archetype:[TYPE]are compatible with Liferay Portal CE 7.0.

2. Project dependencies

Maven would have created a project with its corresponding pom.xml.
Open the project pom.xml



<?xml version="1.0"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.kiktronik</groupId>
    <artifactId>LiferayTuts</artifactId>
    <packaging>war</packaging>
    <name>LiferayTuts</name>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <faces.api.version>2.2</faces.api.version>
        <liferay.faces.bridge.ext.version>5.0.0</liferay.faces.bridge.ext.version>
        <liferay.faces.bridge.version>4.0.0</liferay.faces.bridge.version>
        <mojarra.version>2.2.13</mojarra.version>
    </properties>
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.3</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-war-plugin</artifactId>
                <version>2.3</version>
                <configuration>
                    <filteringDeploymentDescriptors>true</filteringDeploymentDescriptors>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>javax.faces</groupId>
            <artifactId>javax.faces-api</artifactId>
            <version>${faces.api.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.glassfish</groupId>
            <artifactId>javax.faces</artifactId>
            <version>${mojarra.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.liferay.faces</groupId>
            <artifactId>com.liferay.faces.bridge.ext</artifactId>
            <version>${liferay.faces.bridge.ext.version}</version>
        </dependency>
        <dependency>
            <groupId>com.liferay.faces</groupId>
            <artifactId>com.liferay.faces.bridge.impl</artifactId>
            <version>${liferay.faces.bridge.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.14</version>
        </dependency>
        <dependency>
            <groupId>org.primefaces</groupId>
            <artifactId>primefaces</artifactId>
            <version>6.1</version>
        </dependency>
        <dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.core</artifactId>
            <version>6.0.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.liferay.portal</groupId>
            <artifactId>com.liferay.portal.kernel</artifactId>
            <version>2.3.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>


Our project is name LiferayTuts

2.1 Liferay service dependencies

This tutorial requires UserLocalService services. This API has been moved to portal-kernel. We declare the dependency as follow


<dependency>
            <groupId>com.liferay.portal</groupId>
            <artifactId>com.liferay.portal.kernel</artifactId>
            <version>2.3.0</version>
            <scope>provided</scope>
        </dependency>

3. Maven would have created the required Liferay properties and files to run this project. As it is, the project could be deployed in Liferay.

Open your view.xhtml file:


<?xml version="1.0"?>

<f:view
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://xmlns.jcp.org/jsf/core"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:p="http://primefaces.org/ui"
    >
    <h:head>
        <h:outputStylesheet library="css" name="main.css" />

    </h:head>
    <h:body>
        <p:panel id="panelId">
            <h:outputText styleClass="bold-hello-world" value="#{i18n['LiferayTuts-hello-world']}" />
        </p:panel>
        <ul>
            <li><em><h:outputText value="#{product.JSF}" /></em></li>
            <li><em><h:outputText value="#{product.LIFERAY_FACES_BRIDGE}" /></em></li>
            <li><em><h:outputText value="#{product.LIFERAY_FACES_UTIL}" /></em></li>
            <li><em><h:outputText value="#{product.PRIMEFACES}" /></em></li>
        </ul>
        </div>
    </h:body>
</f:view>

When viewed in Liferay, view.xhtml will display the following

  • Mojarra 2.2.13
  • Liferay Faces Bridge Implementation 4.0.0 (Aug 30, 2016 AD)
  • Liferay Faces Util 3.0.0 (Aug 29, 2016 AD)
  • PrimeFaces 6.1

4. Consuming Liferay Services in JSF Portlet

As previously mentioned, as part of this tutorial, we will consume UserLocalService service. In order to make the API available to us, we need to create an OSGi service by creating a class which extends org.osgi.util.tracker.ServiceTracker

Let’s take a look at the following class which we have created for this tutorial


package com.kiktronik.tut.service;


import com.liferay.portal.kernel.service.UserLocalService;
import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;

/**
 *
 * @author armelnene
 */
public class UserLocalServiceTracker extends ServiceTracker<UserLocalService, UserLocalService> {
    
    public UserLocalServiceTracker(BundleContext bundleContext){
        super(bundleContext, UserLocalService.class, null);
    }
}

Don’t forget to add the OSGi dependencies to your maven pom.xml


<dependency>
            <groupId>org.osgi</groupId>
            <artifactId>org.osgi.core</artifactId>
            <version>6.0.0</version>
            <scope>provided</scope>
</dependency>

The above class will look-up the service in the OSGi registry and make it available to our portlet. Let’s create a JSF bean to interact with the <code>UserLocalService</code>. As we are using JSF, it is important that we carry out the service lookup once the bean has been created. This is achieved as


@PostConstruct
    public void postConstruct() {
        Bundle bundle = FrameworkUtil.getBundle(this.getClass());
        BundleContext bundleContext = bundle.getBundleContext();
        userLocalServiceTracker = new UserLocalServiceTracker(bundleContext);
        userLocalServiceTracker.open();
    }

We have to ensure that references to the service are closed once the bean has been destroyed. We can achieve this as


 @PreDestroy
    public void preDestroy() {
        userLocalServiceTracker.close();
    }

We now have access to the full UserLocalService in our bean and we can use it as follow


public int getUserId(){
        UserLocalService userLocalService = userLocalServiceTracker.getService();
        userId = userLocalService.getUsersCount();
        return userId;
    }

This is what the full code of our JSF bean looks like


package com.kiktronik.tut.bean;

import com.kiktronik.tut.service.UserLocalServiceTracker;
import com.liferay.faces.util.context.FacesContextHelperUtil;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.model.User;
import com.liferay.portal.kernel.service.UserLocalService;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;

/**
 *
 * @author armelnene
 */
@ManagedBean
@RequestScoped
public class TutBacking {

    private String firstname;
    private String lastname;
    private UserLocalServiceTracker userLocalServiceTracker;
    private int userId;

    /**
     * Creates a new instance of TutBacking
     */
    public TutBacking() {
    }

    @PostConstruct
    public void postConstruct() {
        Bundle bundle = FrameworkUtil.getBundle(this.getClass());
        BundleContext bundleContext = bundle.getBundleContext();
        userLocalServiceTracker = new UserLocalServiceTracker(bundleContext);
        userLocalServiceTracker.open();
    }

    @PreDestroy
    public void preDestroy() {
        userLocalServiceTracker.close();
    }

    public void submit(ActionEvent actionEvent) {
        FacesContextHelperUtil.addGlobalSuccessInfoMessage();
    }

    /**
     * @return the firstname
     */
    public String getFirstname() {
        return firstname;
    }

    /**
     * @param firstname the firstname to set
     */
    public void setFirstname(String firstname) {
        this.firstname = firstname;
    }

    /**
     * @return the lastname
     */
    public String getLastname() {
        return lastname;
    }

    /**
     * @param lastname the lastname to set
     */
    public void setLastname(String lastname) {
        this.lastname = lastname;
    }
    
    public int getUserId(){
        UserLocalService userLocalService = userLocalServiceTracker.getService();
        userId = userLocalService.getUsersCount();
        return userId;
    }
    
    public User getUser() throws PortalException{
        UserLocalService userLocalService = userLocalServiceTracker.getService();
        String id = FacesContext.getCurrentInstance().getExternalContext().getRemoteUser();
        
        long idx = Long.parseLong(id);
        User user = userLocalService.getUserById(idx);
        return user;
        
    }
}

Here is how the JSF bean is invoke in the JSF page


<?xml version="1.0"?>

<f:view
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://xmlns.jcp.org/jsf/core"
    xmlns:h="http://xmlns.jcp.org/jsf/html"
    xmlns:p="http://primefaces.org/ui"
    xmlns:pe="http://primefaces.org/ui/extensions"
    xmlns:ct="http://www.chartistjsf.org/charts"
    >
    <h:head>
        <h:outputStylesheet library="css" name="main.css" />

    </h:head>
    <h:body>
        <p:panel id="panelId">
            <h:outputText styleClass="bold-hello-world" value="#{i18n['LiferayTuts-hello-world']}" />
        </p:panel>
        <ul>
            <li><em><h:outputText value="#{product.JSF}" /></em></li>
            <li><em><h:outputText value="#{product.LIFERAY_FACES_BRIDGE}" /></em></li>
            <li><em><h:outputText value="#{product.LIFERAY_FACES_UTIL}" /></em></li>
            <li><em><h:outputText value="#{product.PRIMEFACES}" /></em></li>
        </ul>

        <h:form>
            <h:messages globalOnly="true" />
            <h:outputLabel value="#{i18n['enter-your-name']}" />
            <h:inputText value="#{tutBacking.firstname}" />
            <h:commandButton actionListener="#{tutBacking.submit}" value="#{i18n['submit']}">
                <f:ajax execute="@form" render="@form" />
            </h:commandButton>
            <br />
            <h:outputText value="Hello #{tutBacking.firstname}" />
        </h:form>
        <h1>There are #{tutBacking.userId} users</h1>
        <h2>Here is the user E-mail: #{tutBacking.user.displayEmailAddress}</h2>
    </h:body>
</f:view>

There you have it. Build and deploy your portlet to Liferay 7 CE to see it perform its magic. We hope this tutorial was helpful.

Enjoy.