In Liferay you can split your application logic vertically exposing each component as a separate independent service. This service can be consumed by your portlet applications and any other applications. Liferay itself exposes several services to you: GroupService (for managing communities) and UserService (for managing users) for example. In this article I will show you how you can create a reusable service yourself and host it in Liferay.

In this article we will build a simple hello world service, nothing to fancy (I want to leave something for you to do yourself ;) ). We will code the service from scratch without using tools like service builder.

The project will contain 3 modules:

  1. A library containing the service contract
  2. A web application implementing the service contract
  3. A web application consuming the service

I hope you like this article, please let me know if you have any questions or comments. Lets get started. I assume you have read my previous article about using Maven to build Liferay applications. If not please read it first.

0. Project Setup

Please download and unzip the empty project and load it into an IDE of choice or use the command shell if you want. The project contains empty files. Please familiarizes yourself with the project structure and notice the dependencies in the different pom.xml files. Nothing special going on though.

1. Service Contract

This library contains the code contract of our HelloWorld service and a utility class which will provide easy access for non Spring portlets. Lets first set up our service contract, copy he following code into HelloWorldService.java:

package nl.devatwork.hello.world.service;

public interface HelloWorldService {
    String sayHello(String name);
}

That wasn’t so bad now was it? I think you have written more complex service contracts ;) .

Lets implement the access utility class. Copy the following code into HelloWorldServiceUtil.java:

package nl.devatwork.hello.world.service;

public class HelloWorldServiceUtil {
    private static HelloWorldService _service;

    public static HelloWorldService getService() {
        if (_service == null) {
            throw new RuntimeException("HelloWorldService is not set");
        }

        return _service;
    }

    public void setService(HelloWorldService service) {
        _service = service;
    }

    public static String sayHello(String name) {
        return getService().sayHello(name);
    }
}

Again a really simple class. It has a getter and a setter which will be used to inject our service instance into this utility class. The static sayHello method is a proxies the service method.

You can build the service contract library by executing the following command in the /hello-world-service/ folder:

mvn clean package

Copy the resulting JAR file (/hello-world-service/target/hello-world-service-1.0.0-SNAPSHOT.jar) to the /lib/ext folder of your application server. You have to do this in order to avoid class loader issues. We will use this library later.

2. Service Implementation

Now we will implement the service in a self contained web application.  This application can be deployed using the auto deploy mechanism of your application server.

Lets implement the base class for our service implementation which implements the required plumbing. Copy the following code into HelloWorldServiceBaseImpl.java:

package nl.devatwork.hello.world.service.base;

import com.liferay.portal.SystemException;
import com.liferay.portal.kernel.annotation.BeanReference;
import com.liferay.portal.service.base.PrincipalBean;
import com.liferay.portal.util.PortalUtil;
import nl.devatwork.hello.world.service.HelloWorldService;

public abstract class HelloWorldServiceBaseImpl extends PrincipalBean implements HelloWorldService {
    @BeanReference(name = "nl.devatwork.hello.world.service.HelloWorldService.impl")
    protected HelloWorldService helloWorldService;

    public HelloWorldService getHelloWorldService() {
        return helloWorldService;
    }

    public void setHelloWorldService(HelloWorldService helloWorldService) {
        this.helloWorldService = helloWorldService;
    }

    protected void runSQL(String sql) throws SystemException {
        try {
            PortalUtil.runSQL(sql);
        } catch (Exception e) {
            throw new SystemException(e);
        }
    }
}

Now that we have the plumbing in place lets concentrate on our state of the art business logic. Copy the following code into HelloWorldServiceImpl.java:

package nl.devatwork.hello.world.service.impl;

import nl.devatwork.hello.world.service.base.HelloWorldServiceBaseImpl;

public class HelloWorldServiceImpl extends HelloWorldServiceBaseImpl {
    public String sayHello(String name) {
        return "Hello, " + name;
    }
}

Very exiting business logic isn’t it? ;) .

Ok, now that we have our code in place lets apply some glue to make it all work. Copy the following content to service.properties:

##
## Properties Override
##

    #
    # Specify where to get the overridden properties. Updates should not be made
    # on this file but on the overridden version of this file.
    #
    include-and-override=service-ext.properties

##
## Build
##

    build.namespace=DevAtWork
    build.number=1
    build.date=1269952033689
    build.auto.upgrade=true

##
## Spring
##

    #
    # Input a list of comma delimited Spring configurations. These will be
    # loaded after the bean definitions specified in the
	# portalContextConfigLocation parameter in web.xml.
    #
    spring.configs=\
        WEB-INF/classes/META-INF/portlet-spring.xml

Notice the last two lines. This tells Liferay to load our Spring configuration.

Lets create our Spring configuration now. Copy the following code to portlet-spring.xml:

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

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" default-init-method="afterPropertiesSet" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
    <bean id="nl.devatwork.hello.world.service.HelloWorldService.impl" class="nl.devatwork.hello.world.service.impl.HelloWorldServiceImpl"/>
    <bean id="nl.devatwork.hello.world.service.HelloWorldServiceUtil" class="nl.devatwork.hello.world.service.HelloWorldServiceUtil">
        <property name="service" ref="nl.devatwork.hello.world.service.HelloWorldService.impl"/>
    </bean>
</beans>

We register two beans into the Spring container. One for our service implementation instance and one for our service util.

Lets add the following code to web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">

    <listener>
        <listener-class>com.liferay.portal.kernel.spring.context.PortletContextLoaderListener</listener-class>
    </listener>

</web-app>

This will load the Spring configuration for us.

Add the following code to liferay-plugin-package.properties:

name=Hello World Service Implementation
module-group-id=nl.devatwork.service.impl
module-incremental-version=1
short-description=
change-log=
page-url=http://www.devatwork.nl
author=Dev @ Work
licenses=Free

portal-dependency-jars=hello-world-service-1.0.0-SNAPSHOT.jar

Notice that we declare a dependency on the library we deployed earlier. Liferay will load the library on our class path.

We are ready with the service implementation now. You can build the service implementation by executing the following command in the /hello-world-service-impl/ folder:

mvn clean package

Copy the resulting WAR file (/hello-world-service-impl/target/hello-world-service-impl-1.0.0-SNAPSHOT.war to the auto deploy folder of your application server. The service implementation will be installed now.

3. Consumer Implementation

Now that we have the service contract and implementation in place lets create a portlet which consumes the service.

Copy the following code into SayHelloPortlet.java:

package nl.devatwork.hello.world.portlets;

import nl.devatwork.hello.world.service.HelloWorldServiceUtil;

import javax.portlet.GenericPortlet;
import javax.portlet.PortletException;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

public class SayHelloPortlet extends GenericPortlet {
    public void doView(RenderRequest request, RenderResponse response) throws PortletException, java.io.IOException {
        String msg = HelloWorldServiceUtil.sayHello("Liferay Dev");
        response.setContentType("text/html");
        response.getWriter().print(msg);
    }
}

Copy the following code to portlet.xml:

<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.0"
             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>SayHelloPortlet</portlet-name>
        <display-name>Say Hello Portlet</display-name>
        <portlet-class>nl.devatwork.hello.world.portlets.SayHelloPortlet</portlet-class>
        <expiration-cache>0</expiration-cache>
        <supports>
            <mime-type>text/html</mime-type>
            <portlet-mode>view</portlet-mode>
        </supports>
        <portlet-info>
            <title>Say Hello Portlet</title>
            <short-title>Say Hello Portlet</short-title>
            <keywords>DevAtWork</keywords>
        </portlet-info>
    </portlet>

</portlet-app>

Copy the following code to liferay-plugin-package.properties:

name=Hello World Portlets
module-group-id=nl.devatwork.portlets
module-incremental-version=1
short-description=
change-log=
page-url=http://www.devatwork.nl
author=Dev @ Work
licenses=Free

portal-dependency-jars=hello-world-service-1.0.0-SNAPSHOT.jar

Copy the following code to web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
</web-app>

We are ready with the consumer implementation now. You can build the portlet by executing the following command in the /hello-world-portlets/ folder:

mvn clean package

Copy the resulting WAR file (/hello-world-portlets/target/hello-world-portlets-1.0.0-SNAPSHOT.war to the auto deploy folder of your application server. The portlet will be installed now. Add the new portlet to a page and if everything works well then it should display the message: ‘Hello, Liferay Dev’.

You can download the complete project here.

That is it for this article, I hope you enjoyed reading it and that you learned something. If you have any questions or comments please post a comment and I will get back to you as soon as I can. In the next article I will show you how you can expose the HelloWorld service as a web service so it can be consumed by external applications.

Be Sociable, Share!