Continuing my post of Liferay this the Part 2 of the series, developing a Spring MVC portlet on Liferay.
1.Spring MVC Portlet
From version 2.0, Spring Framework ships with Portlet MVC Framework, which is a replica of its Web MVC Framework which can be used for developing JSR-168-compliant portlet applications.
2.Advantages
* Portlets Portlet MVC Framework developed can be deployed in any JSR-168-compliant portlet container without making any changes. IBM Frameworks (Struts/JSF) have a dependency on IBM WebSphere Portal runtime.
* The portlet’s two phase request processing is handled by providing an interface which defines handleActionRequest() and handleRenderRequest() methods.
* Spring Portlet MVC Framework provides easy integration with popular view-rendering technologies such as JSTL, Apache Tiles, Apache Velocity, and FreeMarker.
* Provides a testing framework that you can use for test-driven development of your portlet.
3. Maven Project Setup
Spring POM dependency for will be like
<properties>
<spring.version>3.0.0.RELEASE</spring.version>
</properties>
Spring dependency,
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>org.springframework.web.servlet</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc-portlet</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.webflow</groupId>
<artifactId>org.springframework.js</artifactId>
<version>2.0.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>org.springframework.security.web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>org.springframework.security.config</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>org.springframework.security.taglibs</artifactId>
<version>${spring.version}</version>
</dependency>
4. Developing Portlet
4.1 Developing portlet.xml
1. Create the portlet.xml file in the /src/main/webapp/WEB-INF/ folder
<?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>Books</portlet-name>
<portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class>
<init-param>
<name>contextConfigLocation</name>
<value>/WEB-INF/context/portlet/adx-controller-mappings.xml</value>
</init-param>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
</supports>
<resource-bundle>messages</resource-bundle>
</portlet>
</portlet-app>
DispatcherPortlet is responsible for handling every client request. When it receives a request, it finds out which Controller class should be used for handling this request, and then it calls its handleActionRequest() or handleRenderRequest() method based on the request processing phase. The Controller class executes business logic and returns a View name that should be used for rendering markup to the user. The DispatcherPortlet then forwards control to that View for actual markup generation.
DispatcherPortlet is the central dispatcher for use within Spring Portlet MVC Framework. A portlet application can define more than one DispatcherPortlet. Each of these portlets operates its own namespace, loading its application context and handler mapping.
The DispatcherPortlet is also responsible for loading application context (Spring configuration file) for this portlet. First, it tries to check the value of the configLocation portlet initialization parameter. If that parameter is not specified, it takes the portlet name (that is, the value of the <portlet-name> element <portlet-name>Books</portlet-name>), appends-portlet.xml to it, and tries to load that file from the /WEB-INF folder;Books-portlet.xml in this case.
But in the example contextConfigLocation is mentioned, so any name can be defined. <name>contextConfigLocation</name><value>/WEB-INF/context/portlet/adx-controller-mappings.xml</value>
Also create a bundle messages.properties with values for Portlet title, key word etc. Once deployed this file should go inside the classes folder.
Ex: messages.properties
javax.portlet.title=SOME NAME
javax.portlet.short-title=SOME NAME
javax.portlet.keywords=key1
4.2 Developing the Controller classSpring 2.5 introduces an annotation-based programming model for MVC controllers, using annotations such as @RequestMapping, @RequestParam, @ModelAttribute, etc. This annotation support is available for both Servlet MVC and Portlet MVC. Controllers implemented in this style do not have to extend specific base classes or implement specific interfaces
The @Controller annotation indicates that a particular class serves the role of a controller. There is no need to extend any controller base class or reference the Portlet API.
The @RequestMapping annotation is used to map portlet modes like 'VIEW'/'EDIT' onto an entire class or a particular handler method
@RequestMapping at the type level may be used for plain implementations of the Controller interface as well. In this case, the request processing code would follow the traditional handle(ActionRender)Request signature, while the controller's mapping would be expressed through an @RequestMapping annotation
The code here shows annotation at the method level.
@Controller
public class AdxController {
/**
* @param request
* @param response
* @throws Exception
*/
public void handleActionRequest(ActionRequest request,
ActionResponse response) throws Exception {
System.out.println("handleActionRequest >>");
}
/**
* @param request
* @param response
* @return
* @throws Exception
*/
@RequestMapping("view")
public ModelAndView handleRenderRequest(RenderRequest request,
RenderResponse response) throws Exception {
Map<String, Object> model = new HashMap<String, Object>();
model.put("helloWorldMessage", "List of Books");
return new ModelAndView("displayBooks", model);
}
}
4.3 Developing the mapping for Controller class<?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"
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-3.0.xsd">
<context:annotation-config />
<bean class="org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping">
<property name="order" value="10" />
</bean>
<!--Adx Controllers -->
<bean id="adxController" class="lab.liferay.AdxController" />
<bean id="portletModeHandlerMapping"
class="org.springframework.web.portlet.handler.PortletModeHandlerMapping">
<property name="portletModeMap">
<map>
<entry key="view">
<ref bean="adxController" />
</entry>
</map>
</property>
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass"
value="org.springframework.web.servlet.view.InternalResourceView" />
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
adxController: bean definition
portletModeHandlerMapping: The PortletModeHandlerMapping class is a simple implementation of the HandlerMapping interface and is used by DispatcherPortlet to find a suitable Controller for every request. The PortletModeHandlerMapping class uses Portlet mode for the current request to find a suitable Controller class to use for handling the request. The portletModeMap property of portletModeHandlerMapping bean is the place where we map the Portlet mode name against the Controller class.
Here adxController is responsible for handling View mode requests
4.4 Developing web.xml
According to Portlet Specification 1.0, every portlet application is also a Servlet Specification 2.3-compliant Web application, and it needs a Web application deployment descriptor (that is, web.xml).
Main configurations in web.xml
* ViewRendererServlet. The ViewRendererServlet is acting as the bridge servlet for portlet support. During the render phase, DispatcherPortlet wraps PortletRequest into ServletRequest and forwards control to ViewRendererServlet for actual rendering. ContextLoaderListener. The
* ContextLoaderListener class takes care of loading Web application context at the time of the Web application startup. The Web application context is shared by all the portlets in the portlet application. Default location is /WEB-INF/applicationContext.xml.
<?xml version="1.0"?>
<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>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<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>
</web-app>
4.5 applicationcontext.xml
Spring beans can be defined here.
Now the application is ready for deployment. But to deploy in liferay 2 more xml’s to be configured, liferay-portlet.xml and liferay-display.xml
5. Configuring the application for Liferay
5.1 liferay-portlet.xml
This xml defines the portlets. Portlet name is defined using portlet-name tag. <portlet-name>Books</portlet-name>.
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE liferay-portlet-app PUBLIC "-//Liferay//DTD Portlet Application 5.2.0//EN" "http://www.liferay.com/dtd/liferay-portlet-app_5_2_0.dtd">
<liferay-portlet-app>
<portlet>
<portlet-name>Books</portlet-name>
<instanceable>true</instanceable>
</portlet>
<role-mapper>
<role-name>administrator</role-name>
<role-link>Administrator</role-link>
</role-mapper>
<role-mapper>
<role-name>guest</role-name>
<role-link>Guest</role-link>
</role-mapper>
<role-mapper>
<role-name>power-user</role-name>
<role-link>Power User</role-link>
</role-mapper>
<role-mapper>
<role-name>user</role-name>
<role-link>User</role-link>
</role-mapper>
</liferay-portlet-app>
5.2 liferay-display.xml
Here the category under which the portlets in the application must be grouped is defined. By default the portlets will come under “Samples”. As in the example the Portlet Books will come under “My Spring Portlet” category.
<?xml version="1.0"?>
<display>
<category name=" My Spring Portlet">
<portlet id="Books" />
</category>
</display>
See Part 1 (Start-Stop Liferay from Eclipse) and Part 3 (Deploy from Maven Project)