Handling Multiple Actions in Spring Portlet MVC Framework

In the Handling actions in Spring Portlet MVC Framework entry, we looked at the sample of how to handle action request inside your portlet. But that portlet had only one controller. In most of the real world scenarios you will want to break your portlet into multiple Controllers where a different Controller class will be responsible for handling different actions.

In this entry we will look at sample of how you can use multiple Controllers in one Mode and how to forward control to these controllers based on URL parameter. The MultipleActionsPortlet will have 4 Controllers for VIEW mode. ViewModeController will be the default Controller, which will get control when you call portlet for first time, It will have Action1Controller, Action2Controller and Action3Controller, depending on value of the action parameter. Each of these Controllers simply forward control to different JSPs. You can download sample code from here.

This is how the ViewModeController.java looks like

package com.webspherenotes.portlet;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

import org.springframework.web.portlet.ModelAndView;
import org.springframework.web.portlet.mvc.AbstractController;

public class ViewModeController extends AbstractController{

public void handleActionRequest(ActionRequest request,
ActionResponse response) throws Exception {
System.out.println("Entering ViewModeController.handleActionRequest()");
System.out.println("Exiting ViewModeController.handleActionRequest()");
}

public ModelAndView handleRenderRequest(RenderRequest request,
RenderResponse response) throws Exception {
System.out.println("Entering ViewModeController.handleRenderRequest()");
ModelAndView modelAndView = new ModelAndView("view");
System.out.println("Exiting ViewModeController.handleRenderRequest()");
return modelAndView;
}


}


The ViewModeController is very simple, only thing that it does is forward control to view.jsp for generating markup. This is how the view.jsp looks like

<%@page language="java" contentType="text/html; %>
<%@taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%>

<portlet:defineObjects />

<h1>view.jsp</h1>
<table>
<tr>
<td>
<a href='<portlet:actionURL>
<portlet:param name="action" value="action1" />
</portlet:actionURL>'>Action 1</a>
</td>
</tr>
<tr>
<td>
<a href='<portlet:actionURL>
<portlet:param name="action" value="action2" />
</portlet:actionURL>'>Action 2</a>
</td>
</tr>
<tr>
<td>
<a href='<portlet:actionURL>
<portlet:param name="action" value="action3" />
</portlet:actionURL>'>Action 3</a>
</td>
</tr>
</table>


The view.jsp is generating 3 different actionURL's with different values for action parameter. The Spring Portlet MVC framework reads the value of action parameter to decide which Controller class will get control for handling the URL.

I did create three similar ActionControllers for handling other 3 actions, This is how the Action1Controller looks like

package com.webspherenotes.portlet;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;

import org.springframework.web.portlet.ModelAndView;
import org.springframework.web.portlet.mvc.AbstractController;

public class Action1Controller extends AbstractController{

public void handleActionRequest(ActionRequest request,
ActionResponse response) throws Exception {
System.out.println("Entering Action1Controller.handleActionRequest()");
System.out.println("Exiting Action1Controller.handleActionRequest()");
}

public ModelAndView handleRenderRequest(RenderRequest request,
RenderResponse response) throws Exception {
System.out.println("Entering Action1Controller.handleRenderRequest()");
ModelAndView modelAndView = new ModelAndView("action1");
System.out.println("Exiting Action1Controller.handleRenderRequest()");
return modelAndView;
}

}


The handleActionRequest of Action1Controller does not do anything only thing that it does in handleRenderRequest is to forward control to action1.jsp for rendering markup.

The most important part in handling multiple actions is the Spring Configuration, this is how my Spring configuration looks like

<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

<bean id="viewController"
class="com.webspherenotes.portlet.ViewModeController" />
<bean id="action1Controller"
class="com.webspherenotes.portlet.Action1Controller" />
<bean id="action2Controller"
class="com.webspherenotes.portlet.Action2Controller" />
<bean id="action3Controller"
class="com.webspherenotes.portlet.Action3Controller" />


<bean id="parameterMappingInterceptor"
class="org.springframework.web.portlet.handler.ParameterMappingInterceptor" />
<bean id="portletModeParameterHandlerMapping"
class="org.springframework.web.portlet.handler.PortletModeParameterHandlerMapping">
<property name="order" value="1" />
<property name="interceptors">
<list>
<ref bean="parameterMappingInterceptor" />
</list>
</property>
<property name="portletModeParameterMap">
<map>
<entry key="view">
<map>
<entry key="view">
<ref bean="viewController" />
</entry>
<entry key="action1">
<ref bean="action1Controller" />
</entry>
<entry key="action2">
<ref bean="action2Controller" />
</entry>
<entry key="action3">
<ref bean="action3Controller" />
</entry>

</map>
</entry>
</map>
</property>
</bean>


<bean id="portletModeHandlerMapping"
class="org.springframework.web.portlet.handler.PortletModeHandlerMapping">
<property name="order" value="2" />
<property name="portletModeMap">
<map>
<entry key="view">
<ref bean="viewController" />
</entry>
</map>
</property>
</bean>
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass">
<value>org.springframework.web.servlet.view.JstlView</value>
</property>
<property name="prefix">
<value>/WEB-INF/jsp/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>




As you can see, we have defined two new bean definitions in the SpringContactManagPortlet-portlet.xml file.

  1. parameterMappingInterceptor. The parameterMappingInterceptor is used to forward the value of the action request parameter from ActionRequest to RenderRequest, so that Spring Portlet MVC Framework uses the same controller for handling both the action request and the render request. If you dont use this interceptor you will have to manually copy value of action parameter in handleActionRequest as render parameter

  2. portletModeParameterHandlerMapping. portletModeParameterHandlerMapping is an advanced implementation of HandlerMapping that uses the portlet mode as well as the value of the action request parameter to resolve the handler for the request. In the sample code, we have more than one handler in View mode so we use portletModeParameterHandlerMapping. The portletModeParameterHandlerMapping bean has the portletModeParameterMap property, which is a map of all the portlet modes that your portlet supports. This map takes key value pairs with the name of the portlet mode as the key. Value is another map that takes the value of the action request parameter as the key and the reference to the controller that handles its request as its value.



We also have the old org.springframework.web.portlet.handler.PortletModeHandlerMapping bean which is mapping ViewModeController as default Controller for the VIEW mode, its required for handling the default request or the request which does not have action parameter. It has property order equal to 2 to demonstrate that this HandleMapping bean should be used only if the portletModeParameterHandlerMapping is not able to map request to Controller

2 comments:

hugo said...

Just what i was looking for!
Excelent post

Sycorax said...

Please how this could determine, that the parameter action was used. I dont see it here. Thanx