Liferay Spring MVC portlets
Here I will guide you step by step through the process of creating Liferay Spring MVC portlets. I've already showed you how to create Liferay Spring MVC Freemarker portlets and how to create Liferay Spring MVC Freemarker and JSP portlets. However, both of those posts require the prior knowledge of how to create Liferay Spring MVC portlets. There are already several resources online that describe how to do this, but I find them either too verbose, or faulty, so I'm writing my own hoping to fix this.
Personally I find Spring MVC portlets highly superior to Liferay MVC portlets in many different ways. You can easily take advantage of spring injections. You use spring annotations for render requests, action requests and resource requests which, if you do it right, results in much more structured controllers with much less boiler plate code that are much more readable to human beings. Still doing things right, your whole project is more structured, organized and logical. You can also use Freemarker instead of JSP preferable. If you don't know why you should read 10 reasons to replace your JSPs with Freemarker templates on the Stack Hunter blog. Finally, your portlets end up much more maintainable simply because there are a whole lot more programmers out there that know Spring compared to the amount of programmers who know Liferay MVC.
Now, Spring MVC portlets might be great, and I highly recommend them if you are creating traditional portlets. However, if you need to create webapp portlets, you probably shouldn't be using Spring MVC. For webapp portlets, I recommend using Vaadin if you are creating a business application. If you are creating a non-business application, my preference is AngularJS, possibly combined with Spring MVC. I'll be writing more about both Vaadin and AngularJS in future posts.
Unfortunately Liferay doesn't provide us a means of creating Spring MVC portlets out of the box in their IDE or Developer Studio. I'm still hoping that they soon add this feature, and also fix the issue with Spring MVC Freemarker portlets that I show a work around for in one of my previous posts mentioned above. However, their new Maven support is a great feature though still a bit rough around the edges and Liferay 6.2 is a huge step forward. If anybody else created Liferay 6.2 it would be given the name Liferay Next Gen or something like that.
Step 1: Creating the portlet
To be able to create a Spring MVC portlet in Liferay, you first need to create a regular Liferay MVC portlet using the Liferay SDK, and then manually modifying it to become a Spring MVC portlet. Do this however you like, either by using the command line tool in the SDK or in your favourite IDE.
Step 2: Turn your portlet class into Spring DispatcherPortlet
Open the portlet.xml file in your docroot/WEB-INF folder and replace
<portlet-class>com.liferay.util.bridges.mvc.MVCPortlet</portlet-class>
with
<portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class>
Step 3: Create a Spring application context file
Now you need to create a spring application context file. Personally I prefer calling this file nameofyourportlet-portlet.xml in lower case rather than applicationContext.xml, since this makes it easier to add multiple portlets to a single portlet project without collisions. The file should be created in docroot/WEB-INF (the same place as your portlet.xml file) and should contain the following:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<context:annotation-config />
<bean class="org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping" />
</beans>
Step 4: Telling the portlet class where to find the spring application contex
Back in your portlet.xml file located in docroot/WEB-INF replace
<init-param>
<name>view-jsp</name>
<value>/view.jsp</value>
</init-param>
with
<init-param>
<name>contextConfigLocation</name>
<value>/WEB-INF/nameofyourportlet-portlet.xml</value>
</init-param>
ensuring that you use the file name of the file you created in step 3.
Step 5: Adding Spring jars to project
Find and open the liferay-plugin-package.properties file in docroot/WEB-INF and add the following to the end of the file:
portal-dependency-jars=\
spring-web-servlet.jar,\
spring-web-portlet.jar,\
spring-web.jar,\
spring-transaction.jar,\
spring-jdbc.jar,\
spring-expression.jar,\
spring-core.jar,\
spring-context.jar,\
spring-beans.jar,\
spring-asm.jar,\
spring-aop.jar,\
commons-beanutils.jar,\
commons-collections.jar,\
commons-fileupload.jar,\
commons-io.jar,\
commons-lang.jar,\
jstl-api.jar,\
spring-context-support.jar,\
jstl-impl.jar,\
The jstl files are optional, but I highly recommend using jstl when you are forced to use JSP, so I included them here. Also note that if you are going to be using Freemarker, you need to include the freemarker.jar file here as well.
At this point you should build your project. Depending upon what IDE you are using, you might need to manually add the jar files to the projects classpath as well.
Step 6: Add ViewRenderServlet entry in the web.xml file
In the web.xml file located in docroot/WEB-INF add the following entry:
<servlet>
<servlet-name>view-servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.ViewRendererServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>view-servlet</servlet-name>
<url-pattern>/WEB-INF/servlet/view</url-pattern>
</servlet-mapping>
Step 7: Add Spring view resolver
Spring supports many different types of view resolvers. Personally I prefer the Freemarker resolver when creating Liferay MVC portlets. If you want to use Freemarker, you should read the post I linked to at the beginning of this post. Also note that with Spring 3.0 or higher you can use multiple prioritized view resolvers, again see the links at the beginning of this post.
In the application context file you created in step 3 add the following entry:
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.InternalResourceView" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
<property name="order" value="1" />
</bean>
This ensures that what ever you return form a Spring @RenderMapping annotated controller function will be resolved to a JSP file in docroot/WEB-INF/jsp/. So you if for example return "myview" from your controller render function, Spring will use the file docroot/WEB-INF/jsp/myview.jsp to render your portlet.
Step 7: Create a controller with a render request handler
Here we create a very simple Spring controller that adds a simple request handler to render the jsp file mentioned above. This is not an exhaustive Spring tutorial, and if you don't know Spring MVC, you should find a good tutorial explaining controllers, render requests, action requests, resource requests, etc, and read it after completing this tutorial.
package is.brendan.liferay.portlet.testportlet.controller;
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(value = "MyController")
@RequestMapping("VIEW")
public class MyController {
@RenderMapping
public String handleRenderRequest(RenderRequest request,RenderResponse response,Model model){
return "myview";
}
}
Step 8: Define the controller in the application context
In your application context file created in step 3 add the following:
<bean class="is.brendan.liferay.portlet.controller.MyController" />
Congratulations. If you did everything right, you have now created a fully functioning Liferay Spring MVC portlet.
Photo by Carli Jeen / Unsplash