Spring MVC Portlet on Liferay


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 class

Spring 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)



6 comments:

  1. Good series on Liferay.. Thanks

    ReplyDelete
  2. Very Good explanation Chandra..

    ReplyDelete
  3. How do you manage to generate a PDF on the fly within a portlet?

    ReplyDelete
  4. Use iText for PDF creation

    ReplyDelete
  5. hi chandra. this is very helpful... currently im using Spring and my controllers are not annotation based. i would like to try this.

    i have a question. i already searched google and unfortunately i found nothing. anyway my question is how do i make my portlet's URL become friendly. im having this url

    http://localhost:8080/web/myportlet/home?p_p_state=normal&p_p_lifecycle=0&p_p_id=SpringTest_WAR_SpringTestportlet_INSTANCE_9wUi&p_p_col_count=3&p_p_col_id=column-1&_SpringTest_WAR_SpringTestportlet_INSTANCE_9wUi_action=main&p_p_mode=view

    ReplyDelete
  6. Thanks Photog-Runner,
    Check this,
    http://www.liferay.com/web/connor.mckay/blog/-/blogs/5262286

    ReplyDelete