Developing Spring Portlet for use inside Weblogic Portal / Webcenter Portal

Posted by Murali Veligeti on Oracle Blogs See other posts from Oracle Blogs or by Murali Veligeti
Published on Wed, 28 Nov 2012 04:01:49 +0000 Indexed on 2012/11/28 5:12 UTC
Read the original article Hit count: 551

Filed under:

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:

  1. Open the existing web.xml file located at /WebContent/WEB-INF/web.xml.
  2. 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:

  1. The render() method of DispatcherPortlet calls the doDispatch() method, which in turn calls the doRender() method.
  2. 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.
  3. 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.
  4. 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.
  5. After the object of ViewController is returned, the doRenderService() method calls its handleRenderRequestInternal() method.
  6. 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.
  7. 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.
  8. 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.
  9. 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.
  10. 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)


© Oracle Blogs or respective owner

Related posts about /Weblogic Portal