Create maven Spring MVC Portlet in Liferay

Liferay provides various framework to create plugins. Spring MVC is one of the most popular framework to create portlets on Liferay.

In this article, I will be showing how to create Maven Spring MVC Portlet in Liferay. A newer version of Liferay (6.2 & DXP) supports creating plugins with Maven.

Maven Spring MVC Portlet in liferay

Pre-requisite

Before starting further, I would recommend visiting my previous blog on configuring maven, setting maven profile, and basic understanding about creating a new Liferay maven plugin.

If you are creating your Liferay plugin with maven the first time, you must refer to the above link.

Create Maven Spring MVC Portlet

Click File–>New–>Liferay Plugin Project. It will show plugin project wizard windows as below. You can also use Liferay IDE 3.1.2  GA3.

Create new maven Spring MVC Portlet

Click on next, and you will see the screen from where you can choose the portlet framework.

Maven Spring MVC Portlet - Create Portlet framework

You can see that creating the Spring MVC Portlet option is now out of the box from the LDS 2.x version or eclipse IDE 3.1.2 GA3. Choose Spring MVC and click Finish. Wizard will create the project structure as below.

Maven Spring MVC Portlet - Project Structure

Quick Observation :

  • All your Java code resides in the src/main/java folder.
  • All your resource files (i.e., language properties file) reside in the src/main/resources folder.
    Wizard created one controller for us.
  • All your static contents (Js, CSS, etc.) reside in respective folders under the src/main/webapp path.
  • Wizard had put the view.jsp under src/main/webapp/WEB-INF/jsp/sample-spring-mvc-portlet folder. It’s not required to put the JSPs on this location. You can use any location (even outside of WEB-INF), but then you need to set that path accordingly in the spring application context path. (we will see this in a short while).
  • Wizard had put 2 spring application context files.

Why two application context files?

You will see the wizard put 2 spring application context files in src/main/webapp/WEB-INF/spring-context folder. One is immediate under spring-context while the other is inside portlet folder below the spring-context folder.

  • portlet-application-context.xml
    • This is directly under the spring-context folder.
    • This is a global application context file irrespective of any portlet/controller.
    • This will be only useful when you want to implement common functionality across all the portlets defined in your plugin.
    • You can choose a custom location, but then you need to update its path in the web.xml file
  • sample-spring-mvc-portlet-portlet.xml
    • This is under the spring-context/portlet folder
    • It’s a specific set of controllers defined for a particular portlet.
    • Per each portlet, one such application context file is required.
    • You can change the location, but you need to change its path in the respective portlet entry defined in portlet.xml.

At this point, I will also show you how to put two Maven Spring MVC portlets in a single plugin.

For our example, we don’t need the global spring application context file (portlet-application-context.xml) file. Delete it and add the following content in the sample-spring-mvc-portlet-portlet.xml file

<context:component-scan base-package="com.opensource.techblog.springmvc.**" />

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="contentType" value="text/html;charset=UTF-8" />
    <property name="prefix" value="/WEB-INF/jsp/portlet1" />
    <property name="suffix" value=".jsp" />
    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
</bean>

It’s just a path where you can place your JSPs. You may find more information in step 7 of my previous blog, Spring MVC portlet in Liferay.

When you delete the global application context file (portlet-application-context.xml in our case), you need to delete the corresponding entry from web.xml, or else it will show an error when you deploy the WAR.

Just delete the below two sections from the web.xml file. It’s located at src–>main–>webapp–>WEB-INF path.

Section 1:

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring-context/portlet-application-context.xml</param-value>
</context-param>

Section 2:

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

There is no need for this listener as we are deleting the global spring application context file.

Keep the following lines of code in web.xml as is. DO NOT Delete them.

<servlet>
    <servlet-name>ViewRendererServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.ViewRendererServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>ViewRendererServlet</servlet-name>
    <url-pattern>/WEB-INF/servlet/view</url-pattern>
</servlet-mapping>

This is useful to map spring portlet context to spring web context, and it’s required, or else it will not work properly.

Renaming portlet

I will be showing how to add multiple Maven Spring MVC Portlet (s) in a single Liferay plugin WAR. To make it clearer, rename our first portlets, and it’s resources as per the below steps.

Step 1: Changes in JSP and its folder.

  • Rename sample-spring-mvc-portlet folder (under JSP) to portlet1. Update content of view.jsp as below.
<%@ page contentType="text/html" pageEncoding="UTF-8" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

${name}

Step 2: Rename package and portlet controller name

  • Update the package from com.opensource.techblog.springmvc to com.opensource.techblog.springmvc.portlet1
  • Update the controller name (created by the wizard) from PortletViewController to Portlet1ViewController and put the following content in it.
package com.opensource.techblog.springmvc.portlet1;

import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.portlet.bind.annotation.RenderMapping;


@Controller
@RequestMapping("VIEW")
public class Portlet1ViewController {

    @RenderMapping
    public String defaultPortlet1Render(RenderRequest renderRequest, RenderResponse renderResource , Model model) {
        
        renderRequest.setAttribute("name", "This is Portlet 1 JSP");		
        return "view";
    }

}

Step 3: Changes in the portlet specific application context file.

  • Rename sample-spring-mvc-portlet-portlet.xml to portlet1.xml under spring-context/portlet folder
  • You also need to update the base package for context:component-scan and corresponding path in viewResolver bean (inside porlet1.xml), so it looks like below.
<?xml version="1.0"?>

<beans
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
>
    <context:component-scan base-package="com.opensource.techblog.springmvc.portlet1.**" />
    
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="contentType" value="text/html;charset=UTF-8" />
        <property name="prefix" value="/WEB-INF/jsp/portlet1" />
        <property name="suffix" value=".jsp" />
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
    </bean>
    
</beans>

Quick observation:

  • Package is changed from com.opensource.techblog.springmvc to com.opensource.techblog.springmvc.portlet1 since we changed the controller package.
  • context:component-scan would ask spring to scan all the classes (on the path defined by base-package) having annotation @Controller under com.opensource.techblog.springmvc.portlet1 package and consider them as a controller for request navigation.
  • If you don’t want to write this line, you have to manually put <bean> entry for each of your controllers.

Step 4: Change in portlet.xml file

  • We renamed the spring application context file from sample-spring-mvc-portlet-portlet.xml to portlet1.xml. The same needs to be updated in portlet.xml.
  • Open portlet.xml file and update the value of init parameter contextConfigLocation from
    /WEB-INF/spring-context/portlet/sample-spring-mvc-porlet-portlet.xml to /WEB-INF/spring-context/portlet/portlet1.xml
  • Change portlet-name and display-name from sample-spring-mvc-portlet to portlet1 in portlet.xml. You may also change the title, short-title, and keywords from sample-spring-mvc-portlet to portlet1.

Step 5: Change in liferay-portlet.xml file

  • If you rename the portlet-name, the same need to update in liferay-portlet.xml file. Open liferay-portlet.xml and rename portlet-name from sample-spring-mvc-portlet to portlet1.

Step 6: Change in liferay-display.xml file

  • Change portlet Id from sample-spring-mvc-portlet to portlet1(Portlet name that we gave in portlet.xml)

Add new Maven Spring MVC portlet.

You can take the help of Liferay developer studio’s wizard to add a new Maven Spring MVC portlet. But I prefer to add it manually. Let’s do it. Follow the steps below.

Step 1: Add a new spring controller java class with a different package.

  • You must set different packages for different portlets, or else it may create issues while finding the proper method handler.
  • Create new package called com.opensource.techblog.springmvc.portlet2 and add class Portlet2ViewController. Add following content in it.
package com.opensource.techblog.springmvc.portlet2;

import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.portlet.bind.annotation.RenderMapping;


@Controller
@RequestMapping("VIEW")
public class Portlet2ViewController {

    @RenderMapping
    public String defaultPortlet1Render(RenderRequest renderRequest, RenderResponse renderResource , Model model) {
        
        renderRequest.setAttribute("name", "This is Portlet 2 JSP");		
        return "view";
    }

}

Step 2: Add JSP

  • It’s always a good idea to have a separate folder for each portlet.
  • Create a folder called portlet2 under jsp folder and add view.jsp with the following content
<%@ page contentType="text/html" pageEncoding="UTF-8" %>

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

${name}

Step 3: Add a new Portlet specific Spring application context file.

  • It’s recommendable to add a new portlet specific spring application context file.
  • Add portlet2.xml under spring-context/portlet folder and add the following content in it.
<?xml version="1.0"?>

<beans
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
>
    <context:component-scan base-package="com.opensource.techblog.springmvc.portlet2.**" />
    
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="contentType" value="text/html;charset=UTF-8" />
        <property name="prefix" value="/WEB-INF/jsp/portlet2" />
        <property name="suffix" value=".jsp" />
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
    </bean>
    
</beans>

Step 4:  Add new portlet entry in portlet.xml, liferay-portlet.xml, and liferay-display.xml as below.

  • portlet.xml file
<portlet>
    <portlet-name>portlet2</portlet-name>
    <display-name>portlet2</display-name>
    <portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class>
    <init-param>
        <name>contextConfigLocation</name>
        <value>/WEB-INF/spring-context/portlet/portlet2.xml</value>
    </init-param>
    <expiration-cache>0</expiration-cache>
    <supports>
        <mime-type>text/html</mime-type>
    </supports>
    <portlet-info>
        <title>portlet2</title>
        <short-title>portlet2</short-title>
        <keywords>portlet2</keywords>
    </portlet-info>
    <security-role-ref>
        <role-name>administrator</role-name>
    </security-role-ref>
    <security-role-ref>
        <role-name>guest</role-name>
    </security-role-ref>
    <security-role-ref>
        <role-name>power-user</role-name>
    </security-role-ref>
    <security-role-ref>
        <role-name>user</role-name>
    </security-role-ref>
</portlet>
  • liferay-portlet.xml file
<portlet>
    <portlet-name>portlet2</portlet-name>
    <icon>/icon.png</icon>
    <header-portlet-css>/css/main.css</header-portlet-css>
    <footer-portlet-javascript>/js/main.js</footer-portlet-javascript>
</portlet>
  • liferay-display.xml file
<?xml version="1.0"?>
<!DOCTYPE display PUBLIC "-//Liferay//DTD Display 6.2.0//EN" "http://www.liferay.com/dtd/liferay-display_6_2_0.dtd">

<display>
    <category name="mavenspringmvc">
        <portlet id="portlet1" />
        <portlet id="portlet2" />
    </category>
</display>

At this point, the final project structure of our Maven Spring MVC Portlet would be as below.

Maven Spring MVC Portlet - final project structure

POM Structure

open pom.xml, which looks as below.

<?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.opensource.techblog.springmvc</groupId>
    <artifactId>sample-spring-mvc-portlet</artifactId>
    <packaging>war</packaging>
    <name>sample-spring-mvc-portlet Portlet</name>
    <version>1.0.0-SNAPSHOT</version>
    <build>
        <plugins>
            <plugin>
                <groupId>com.liferay.maven.plugins</groupId>
                <artifactId>liferay-maven-plugin</artifactId>
                <version>${liferay.maven.plugin.version}</version>
                <executions>
                    <execution>
                        <phase>generate-sources</phase>
                        <goals>
                            <!-- <goal>build-css</goal> -->
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <autoDeployDir>${liferay.auto.deploy.dir}</autoDeployDir>
                    <appServerDeployDir>${liferay.app.server.deploy.dir}</appServerDeployDir>
                    <appServerLibGlobalDir>${liferay.app.server.lib.global.dir}</appServerLibGlobalDir>
                    <appServerPortalDir>${liferay.app.server.portal.dir}</appServerPortalDir>
                    <liferayVersion>${liferay.version}</liferayVersion>
                    <pluginType>portlet</pluginType>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.5</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <source>1.6</source>
                    <target>1.6</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.5</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <dependencies>
        <dependency>
            <groupId>com.liferay.portal</groupId>
            <artifactId>portal-service</artifactId>
            <version>${liferay.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.liferay.portal</groupId>
            <artifactId>util-bridges</artifactId>
            <version>${liferay.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.liferay.portal</groupId>
            <artifactId>util-taglib</artifactId>
            <version>${liferay.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.liferay.portal</groupId>
            <artifactId>util-java</artifactId>
            <version>${liferay.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.portlet</groupId>
            <artifactId>portlet-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.4</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>3.0.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>3.0.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>3.0.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>3.0.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>3.0.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>3.0.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>3.0.7.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc-portlet</artifactId>
            <version>3.0.7.RELEASE</version>
        </dependency>
    </dependencies>
</project>

 

Spring-related dependencies are added in the dependencies section.

Build and Deploy

It’s time to build and deploy your plugin. Before building, just comment following line under the <execution> element in pom.xml

<!-- <goal>build-css</goal> -->

Open the command prompt and go to the project path. Execute command mvn clean install liferay:deploy. You can find more info from my previous blog on how to build liferay maven plugin(last section)

You will see the BUILD SUCCESS message. Congratulation. Your Maven Spring MVC portlet is completed.

Place it on liferay page and check the output.

Thumb rule

To add additional spring MVC portlet in a single plugin.

  • Add a new controller. Make sure you choose the new java package.
  • Add portlet jsp. Make sure you add it inside a new folder.
  • Add a portlet specific application context file. In this file, you need to set the parent path of JSP to viewResolver bean.
  • Update Liferay specific configuration files
    • portlet.xml
      • Add new portlet entry (with correct portlet name)
      • Make sure you give the correct path of portlet specific application context file (that you created in #3 above) in init-param
    • liferay-portlet.xml
      • Add new portlet entry (with correct portlet name)
    • liferay-display.xml
      • Add new portlet entry with id as its name defined in portlet.xml

It’s always a good idea to split your work into multiple spring controllers instead of just put all your methods in one big controller. In this scenario, you no need to create a new Maven Spring MVC Portlet. Just add additional controllers in the same package.

And this is done. Express your thoughts in the comment section to make this topic more interesting.

Summing Up

  • Spring MVC is one of the most popular frameworks to create portlets in Liferay.
  • Spring MVC portlet will have multiple controllers.
  • Mapping of controller and JSP is defined in the application context file.
  • Path of application context file is configured in portlet.xml file.
  • To add a new portlet, you need to create a separate controller package, jsp folder, application context path, and make separate portlet entry in portlet.xml, liferay-portlet.xml, and liferay-display.xml files.

Download

Download

If you don’t have Liferay developer studio 2.x, you can download the maven project structure and source from Git Hub Project.

Recommended For You

About the Author: Nilang

Nilang Patel is a technology evangelist who loves to spread knowledge and helping people in all possible ways. He is an author of two technical books - Java 9 Dependency and Spring 5.0 Projects.

6 Comments to “Create maven Spring MVC Portlet in Liferay”

  1. Thanks for posting this blog, i am following your guideline to develop liferay-spring project. But i am getting this below exception after deploying war file in liferay portal, followed the same procedure mentioned.

    error: “SEVERE: Servlet.service() for servlet jsp threw exception
    javax.servlet.ServletException: File "/WEB-INF/jsp/portlet1view.jsp" not found”

  2. In your opinion, which is the best way to migrate this kind of project to LR7?
    Minimally adapt and Deploy it as war /WAB or rebuild as a osgi module project?

    Also, in your case, migrate to gradle or keep it with maven?

    Thanks a lot

    1. Hi Alberto,
      If you are working with Liferay 7, then the OSGi module with Gradle is recommended. For migration, the best way is to create a blank portlet and then copy the code manually to make sure nothing is breaking specifically the configuration part.

      Feel free to ask any questions.
      Regards
      Nilang

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.