Developing Spring Portlet for use inside Weblogic Portal / Webcenter Portal
- by Murali Veligeti
We need to understand the main difference between portlet workflow and servlet workflow.The main difference between portlet workflow and servlet workflow is that, the request to the portlet can have two distinct phases: 1) Action phase 2) Render phase. The Action phase is executed only once and is where any 'backend' changes or actions occur, such as making changes in a database. The Render phase then produces what is displayed to the user each time the display is refreshed. The critical point here is that for a single overall request, the action phase is executed only once, but the render phase may be executed multiple times. This provides a clean separation between the activities that modify the persistent state of your system and the activities that generate what is displayed to the user.The dual phases of portlet requests are one of the real strengths of the JSR-168 specification. For example, dynamic search results can be updated routinely on the display without the user explicitly re-running the search. Most other portlet MVC frameworks attempt to completely hide the two phases from the developer and make it look as much like traditional servlet development as possible - we think this approach removes one of the main benefits of using portlets. So, the separation of the two phases is preserved throughout the Spring Portlet MVC framework. The primary manifestation of this approach is that where the servlet version of the MVC classes will have one method that deals with the request, the portlet version of the MVC classes will have two methods that deal with the request: one for the action phase and one for the render phase. For example, where the servlet version of AbstractController has the handleRequestInternal(..) method, the portlet version of AbstractController has handleActionRequestInternal(..) and handleRenderRequestInternal(..) methods.The Spring Portlet Framework is designed around a DispatcherPortlet that dispatches requests to handlers, with configurable handler mappings and view resolution, just as the DispatcherServlet in the Spring Web Framework does.
Developing portlet.xml
Let's start the sample development by creating the portlet.xml file in the /WebContent/WEB-INF/ folder as shown below:
<?xml version="1.0" encoding="UTF-8"?>
<portlet-app version="2.0"
xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<portlet>
<portlet-name>SpringPortletName</portlet-name>
<portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
</supports>
<portlet-info>
<title>SpringPortlet</title>
</portlet-info>
</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.
As you can see, DispatcherPortlet is the central
dispatcher for use within Spring Portlet MVC Framework. Note that your
portlet application can define more than one DispatcherPortlet. If it
does so, then 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), appends "-portlet.xml" to it, and tries to load that file from
the /WEB-INF folder. In the portlet.xml file, we did not specify the
configLocation initialization parameter, so let's create SpringPortletName-portlet.xml file in the next section.
Developing SpringPortletName-portlet.xml
Create the SpringPortletName-portlet.xml file in the /WebContent/WEB-INF folder of your application as shown below:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
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-2.0.xsd">
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean id="pointManager" class="com.wlp.spring.bo.internal.PointManagerImpl">
<property name="users">
<list>
<ref bean="point1"/>
<ref bean="point2"/>
<ref bean="point3"/>
<ref bean="point4"/>
</list>
</property>
</bean>
<bean id="point1" class="com.wlp.spring.bean.User">
<property name="name" value="Murali"/>
<property name="points" value="6"/>
</bean>
<bean id="point2" class="com.wlp.spring.bean.User">
<property name="name" value="Sai"/>
<property name="points" value="13"/>
</bean>
<bean id="point3" class="com.wlp.spring.bean.User">
<property name="name" value="Rama"/>
<property name="points" value="43"/>
</bean>
<bean id="point4" class="com.wlp.spring.bean.User">
<property name="name" value="Krishna"/>
<property name="points" value="23"/>
</bean>
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="messages"/>
</bean>
<bean name="/users.htm" id="userController" class="com.wlp.spring.controller.UserController">
<property name="pointManager" ref="pointManager"/>
</bean>
<bean name="/pointincrease.htm" id="pointIncreaseController" class="com.wlp.spring.controller.IncreasePointsFormController">
<property name="sessionForm" value="true"/>
<property name="pointManager" ref="pointManager"/>
<property name="commandName" value="pointIncrease"/>
<property name="commandClass" value="com.wlp.spring.bean.PointIncrease"/>
<property name="formView" value="pointincrease"/>
<property name="successView" value="users"/>
</bean>
<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="pointincrease">
<ref bean="pointIncreaseController" />
</entry>
<entry key="users">
<ref bean="userController" />
</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="userController" />
</entry>
</map>
</property>
</bean>
</beans>
The SpringPortletName-portlet.xml file is an application context
file for your MVC portlet. It has a couple of bean
definitions:
viewController. At this point, remember that
the viewController bean definition points to the
com.ibm.developerworks.springmvc.ViewController.java class.
portletModeHandlerMapping. As we discussed in
the last section, whenever DispatcherPortlet gets a client request, it
tries to find a suitable Controller class for handling that request.
That is where PortletModeHandlerMapping comes into the picture. 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. In the sample code, we
show that viewController is responsible for handling View mode requests.
Developing UserController.java
In the preceding section, you learned that the
viewController bean is responsible for handling all the View mode
requests. Your next step is to create the UserController.java class as
shown below:
public class UserController extends AbstractController {
private PointManager pointManager;
public void handleActionRequest(ActionRequest request, ActionResponse response) throws Exception {
}
public ModelAndView handleRenderRequest(RenderRequest request, RenderResponse response)
throws ServletException, IOException {
String now = (new java.util.Date()).toString();
Map<String, Object> myModel = new HashMap<String, Object>();
myModel.put("now", now);
myModel.put("users", this.pointManager.getUsers());
return new ModelAndView("users", "model", myModel);
}
public void setPointManager(PointManager pointManager) {
this.pointManager = pointManager;
}
}
Every controller class in Spring Portlet MVC Framework must implement
the org.springframework.web. portlet.mvc.Controller interface directly
or indirectly. To make things easier, Spring Framework provides
AbstractController class, which is the default implementation of the
Controller interface. As a developer, you should always extend your
controller from either AbstractController or one of its more specific
subclasses. Any implementation of the Controller class should be
reusable, thread-safe, and capable of handling multiple requests
throughout the lifecycle of the portlet.
In the sample code, we create the ViewController class by
extending it from AbstractController. Because we don't want to do any
action processing in the HelloSpringPortletMVC portlet, we override only
the handleRenderRequest() method of AbstractController. Now, the only
thing that HelloWorldPortletMVC should do is render the markup of
View.jsp to the user when it receives a user request to do so. To do
that, return the object of ModelAndView with a value of view equal to
View.
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). Let’s create the web.xml file in the /WEB-INF/ folder as
shown in listing 4. Follow these steps:
Open the existing web.xml file located at
/WebContent/WEB-INF/web.xml.
Replace the contents of this file with the code as shown below:
<servlet>
<servlet-name>ViewRendererServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.ViewRendererServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ViewRendererServlet</servlet-name>
<url-pattern>/WEB-INF/servlet/view</url-pattern>
</servlet-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
The web.xml file for the sample portlet declares two things:
ViewRendererServlet. The ViewRendererServlet
is the bridge servlet for portlet support. During the render phase,
DispatcherPortlet wraps PortletRequest into ServletRequest and forwards
control to ViewRendererServlet for actual rendering. This process allows
Spring Portlet MVC Framework to use the same View infrastructure as
that of its servlet version, that is, Spring Web MVC Framework.
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. In
case of duplicate bean definition, the bean definition in the portlet
application context takes precedence over the Web application context.
The ContextLoader class tries to read the value of the
contextConfigLocation Web context parameter to find out the location of
the context file. If the contextConfigLocation parameter is not set,
then it uses the default value, which is
/WEB-INF/applicationContext.xml, to load the context file.
The Portlet Controller interface requires two methods that handle the two phases of a portlet request: the action request and the render request. The action phase should be capable of handling an action request and the render phase should be capable of handling a render request and returning an appropriate model and view. While the Controller interface is quite abstract, Spring Portlet MVC offers a lot of controllers that already contain a lot of the functionality you might need – most of these are very similar to controllers from Spring Web MVC. The Controller interface just defines the most common functionality required of every controller - handling an action request, handling a render request, and returning a model and a view.
How rendering works
As you know, when the user tries to access a page with PointSystemPortletMVC portlet on it or when the user performs some action
on any other portlet on that page or tries to refresh that page, a
render request is sent to the PointSystemPortletMVC portlet. In the
sample code, because DispatcherPortlet is the main portlet class, Weblogic Portal / Webcenter Portal calls its render() method and then the following
sequence of events occurs:
The render() method of DispatcherPortlet calls the doDispatch() method, which in turn calls the doRender() method.
After the doRenderService() method gets control,
first it tries to find out the locale of the request by calling the
PortletRequest.getLocale() method. This locale is used while making all
the locale-related decisions for choices such as which resource bundle
should be loaded or which JSP should be displayed to the user based on
the locale.
After that, the doRenderService() method starts
iterating through all the HandlerMapping classes configured for this
portlet, calling their getHandler() method to identify the appropriate
Controller for handling this request.
In the sample code, we have configured only
PortletModeHandlerMapping as a HandlerMapping class. The
PortletModeHandlerMapping class reads the value of the current portlet
mode, and based on that, it finds out, the Controller class that should
be used to handle this request. In the sample code, ViewController is
configured to handle the View mode request so that the
PortletModeHandlerMapping class returns the object of ViewController.
After the object of ViewController is returned, the doRenderService() method calls its handleRenderRequestInternal() method.
Implementation of the handleRenderRequestInternal()
method in ViewController.java is very simple. It logs a message saying
that it got control, and then it creates an instance of ModelAndView
with a value equal to View and returns it to DispatcherPortlet.
After control returns to doRenderService(), the next
task is to figure out how to render View. For that, DispatcherPortlet
starts iterating through all the ViewResolvers configured in your
portlet application, calling their resolveViewName() method.
In the sample code we have configured only one
ViewResolver, InternalResourceViewResolver. When its resolveViewName()
method is called with viewName, it tries to add /WEB-INF/jsp as a prefix
to the view name and to add JSP as a suffix. And it checks if
/WEB-INF/jsp/View.jsp exists. If it does exist, it returns the object
of JstlView wrapping View.jsp.
After control is returned to the doRenderService()
method, it creates the object PortletRequestDispatcher, which points
to /WEB-INF/servlet/view – that is, ViewRendererServlet. Then it sets
the object of JstlView in the request and dispatches the request to
ViewRendererServlet.
After ViewRendererServlet gets control, it reads the
JstlView object from the request attribute and creates another
RequestDispatcher pointing to the /WEB-INF/jsp/View.jsp URL and passes
control to it for actual markup generation. The markup generated by
View.jsp is returned to user.
At this point, you may question the need for
ViewRendererServlet. Why can't DispatcherPortlet directly forward
control to View.jsp? Adding ViewRendererServlet in between allows Spring
Portlet MVC Framework to reuse the existing View infrastructure. You
may appreciate this more when we discuss how easy it is to integrate
Apache Tiles Framework with your Spring Portlet MVC Framework.
The attached project SpringPortlet.zip should be used to import the project in to your
OEPE Workspace. SpringPortlet_Jars.zip contains jar files required for the application.
Project is written on Spring 2.5.
The same JSR 168 portlet should work on Webcenter Portal as well.
Downloads:
Download WeblogicPotal Project which consists of Spring Portlet.
Download Spring Jars
In-addition to above you need to download Spring.jar (Spring2.5)