Developing plugins for Liferay DXP with Maven

July 27, 2016

A new major version of Liferay has been released past May with a lot of new features. One of the most important is related to plug-ins architecture. Basically Liferay is a web application running in a Java EE application container where plug-ins were built as separate web applications. But DXP introduces a module framework based on the OSGi architecture.

**But… What it is OSGi? **As defined by OSGi Alliance (the organization in charge of this specification): *The OSGi technology is a set of specifications that define a dynamic component system for Java. These specifications enable a development model where applications are (dynamically) composed of many different (reusable) components. *As the aim of this article is not the OSGi architecture but how it is implemented within Liferay, we are not going to deepen in OSGi specification.  In fact, you do not need to be very familiar with all the OSGI architecture for develop plug-ins for Liferay DXP. You only need to understand this 2 concepts: modules and components.

A module can contain one or more components

From now on, when we create a Portlet, what we are going to define is a component (foo.baar.MyPortlet) that implements the module (interface) Portlet. So the basic definition of a portlet will be like this:

@Component(
    service = Portlet.class
)
public class MyMvcPortlet extends MVCPortlet {
}

What happens with the old way plug-ins?

Liferay defined a new module architecture, but this change does not break completely with the old way of building plug-ins. You can reuse your WAR based plug-ins, or even create new ones. But these won’t be deployed as WAR plug-ins. The underlying system will split WAR plug-ins in one or more modules for the OSGi architecture.

Blade Tools Blade Tools is a set of modular developer tools for building Liferay 7.0 modules built with OSGi. With that a developer could:

We are not going deeply with Blade Tolls in this article (maybe in another one) because the scope is just the opposite, using Maven.

**How to develop OSGi modules for Liferay DXP with Maven.**As we said before, Liferay recommends Blade Tools for developing Liferay DXP modules. But most of the companies have a well-defined development environment where the tools for building and managing projects is a basic part. And as it is well known, Maven is the most used build tool for working with Java projects. Either because of comfort, the knowledge or because a company has a lot of reusable components in a Maven repository, using Blade Tools could not be the first option for most of the dev teams. Fortunately, Liferay also provide us with a starting point for developing Liferay DXP plug-ins with our favourite build tool.

Let’s get started. First you can clone the Blade Tools repo from here: https://github.com/rotty3000/blade.git.  Once you had cloned the repository you will see that you have 3 options for start working: gradle, maven and bndtools. We will copy blade/maven/pom.xml file to our project folder, and use this pom.xml as a parent pom for our plug-ins.  After that we should re-factor Artifact, GroupId and version

Once we create our parent pom.xml, we should choose which kind of plug-in we want to develop. The list of plug-in types is increasing constantly and you can check them in Blade Tools Github repository.

First we want to create a portlet, so we copy the blade.portlet.osgiapi folder to our project and we re-factor it to fit with parent pom.xml. This is how a basic pom.xml for a portlet looks like:

  
<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/xsd/maven-4.0.0.xsd"
>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>sample-overview</artifactId>
   <name>samplee overview</name>
   <version>1.0.0</version>
   <packaging>bundle</packaging>
   <parent>
      <groupId>com.mimacom.liferay</groupId>
      <artifactId>parent.felix.bundle.plugin</artifactId>
      <version>1.0.0</version>
      <relativePath>../parent.felix.bundle.plugin</relativePath>
   </parent>
   <dependencies>
      <dependency>
         <groupId>javax.portlet</groupId>
         <artifactId>portlet-api</artifactId>
         <version>2.0</version>
         <scope>provided</scope>
      </dependency>
      <dependency>
         <groupId>org.osgi</groupId>
         <artifactId>org.osgi.core</artifactId>
         <version>6.0.0</version>
         <scope>provided</scope>
      </dependency>
      <dependency>
         <groupId>org.apache.commons</groupId>
         <artifactId>commons-lang3</artifactId>
         <version>3.1</version>
      </dependency>
   </dependencies>
   <build>
      <plugins>
         <plugin>
            <groupId>org.apache.felix</groupId>
            <artifactId>maven-bundle-plugin</artifactId>
            <configuration>
               <instructions>
                  <Bundle-Activator>com.mimacom.sample.Activator</Bundle-Activator>
                  <Private-Package>com.mimacom.sample</Private-Package>
                  <Import-Package>*</Import-Package>
               </instructions>
            </configuration>
         </plugin>
      </plugins>
   </build>
</project>

Let’s analyse it. The first thing that could attract attention is the packaging tag with the value bundle. As we said before it is basically a jar with classes and a MANIFEST.MF file. We can build this file manually or we can use some maven plug-in who makes it easier. And that is related to the next section of pom.xml file, the parent tag.

As value for parent, instead of project main pom.xml, there is an artifact called parent.felix.bundle.plugin.  This artifact it is also included in the blade repository. You need to download and update it in order to add it to your project.  The important part of this pom.xml is this:

      
<build>
      <plugins>
      <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-compiler-plugin</artifactId>
         <version>3.1</version>
         <configuration>
            <source>1.7</source>
            <target>1.7</target>
         </configuration>
      </plugin>
      <plugin>
         <groupId>org.apache.felix</groupId>
         <artifactId>maven-bundle-plugin</artifactId>
         <version>3.0.1</version>
         <extensions>true</extensions>
      </plugin>
   </plugins>
</build>      

This pom.xml file contains the dependencies needed to build OSGI bundles, for that we use the Apache Felix Maven Bundle Plug-in. This plug-in provides a good integration between Maven and Bnd which is, in words from his own author, a tool that “helps […] create and diagnose OSGi R4 bundles”

And, what does Apache Felix Maven Bundle plug-in do? Basically it packages an OSGi bundle, and generates a MANIFEST.MF file based in the instructions defined in the respective pom.xml file of a bundle, and also in its dependencies.

And the final section of the portlet pom.xml file that is interesting for us is the build tag.

<build>
   <plugins>
      <plugin>
         <groupId>org.apache.felix</groupId>
         <artifactId>maven-bundle-plugin</artifactId>
         <configuration>
            <instructions>
                  <Bundle-Activator>com.mimacom.sample.Activator</Bundle-Activator>
                  <Import-Package>*</Import-Package>
            </instructions>
         </configuration>
     </plugin>
   </plugins>
</build>

In this section you configure how you are going to build your module. These are the main instructions:

And here there is an example of a MANIFEST.MF file that has been generated from our pom.xml file.

Manifest-Version: 1.0
Bnd-LastModified: 1458513969781
Build-Jdk: 1.7.0_79
Built-By: daniel
Bundle-Activator: com.mimacom.sample.Activator
Bundle-ManifestVersion: 2
Bundle-Name: samplee overview
Bundle-SymbolicName: com.mimacom.liferay.sample-overview
Bundle-Version: 1.0.0
Created-By: Apache Maven Bundle Plugin
Export-Package: com.mimacom.sample;uses:="javax.portlet,org.osgi.framework";version="1.0.0"
Import-Package: javax.portlet;version="[2.0,3)",org.apache.commons.lang3;version="[3.1,4)",org.osgi.framework;version="[1.8,2)"
Require-Capability: osgi.ee;filter:="(&(osgi.ee=JavaSE)(version=1.7))"
Tool: Bnd-3.0.0.201509101326

And finally… the portlet

There is more than one way to manage the life-cycle of a bundle. The simplest one is the Bundle Activator.  The basic Bundle Activator for a portlet will look like this:

public class Activator implements BundleActivator {

   @Override
   public void start(BundleContext bundleContext) throws Exception {
      Dictionary<String, Object> properties = new Hashtable<String, Object>();
         properties.put("com.liferay.portlet.display-category", "category.sample");
         properties.put("com.liferay.portlet.instanceable", "true");
         properties.put("javax.portlet.display-name", "sampleer Portlet");
         properties.put("javax.portlet.security-role-ref",new String[] {"power-user", "user"});
      _serviceRegistration = bundleContext.registerService(
            Portlet.class, new SampleOverviewPortlet(), properties);
   }
   @Override
   public void stop(BundleContext bundleContext) throws Exception {
      _serviceRegistration.unregister();
   }
   private ServiceRegistration<Portlet> _serviceRegistration;
}

In start method is where we defined which component (SampleOverviewPortlet) implements the Portlet module. When we register a component we also could add properties for the service. If you are familiar with Liferay and see the properties defined in the Dictionary in the example above, you will notice that these are the portlet descriptors that usually are defined in the portlet.xml and liferay-portlet.xml files. You can check how to map portly descriptors to OSGi Service properties.

And finally the portlet class. This class looks like any other Portlet

public class SampleOverviewPortlet extends GenericPortlet {
       @Override
    protected void doView(RenderRequest request, RenderResponse response) 
            throws PortletException, IOException {
        PrintWriter printWriter = response.getWriter();
        printWriter.print(StringEscapeUtils.escapeHtml4("<br>sample portlet - Hello World!</br>"));
    }
}

There is also another pattern for defining Portlet bundle’s and is called Declarative Services. A portlet defined with this method will look like this

@Component( immediate = true,

   property = {
      "com.liferay.portlet.display-category=category.sample",
      "com.liferay.portlet.instanceable=true",
      "javax.portlet.display-name=DS Portlet",
      "javax.portlet.security-role-ref=power-user,user"
   },
   service = Portlet.class
)

public class Sample2Portlet extends GenericPortlet {

   @Override

   protected void doView(RenderRequest request, RenderResponse response) 
           throws PortletException, IOException {

      PrintWriter printWriter = response.getWriter(); 
      printWriter.print("Sampl2Portlet - Hello World!");

  }
}

Both examples define the same portlet in different ways. So the only thing that is left is to package portlets and deploy to our Liferay. For that, you only have to execute mvn clean package inside your module directory, and copy the resulting jar file into liferay-home/deploy folder, same as if it was a war file. Easy, isn’t it?

Useful links:

About the author: Daniel Botran
Comments
Join us