<img height="1" width="1" style="display:none;" alt="" src="https://px.ads.linkedin.com/collect/?pid=299788&amp;fmt=gif">

Quick Conversion to SpringMVC Portlets

Software Development, Portal, Spring, Software Solutions

WANT MVC?

Are you still using plain vanilla portlets and want to catch up with the rest of the modern web development world? Then it's time to move on to using a Model-View-Controller framework. SpringMVC is one of the most popular and actively maintained model view controller frameworks available today. In case you've been living under a rock, let's talk about what Spring is.
Spring is a powerful framework used for standard and enterprise Java application development. Most people know Spring as a dependency injection configuration framework capable of plugging into all your other frameworks. However, it goes well beyond this. To talk about all its capabilities would take a series of blog articles. Instead, I'll assume for now that you have a working browser and I'll direct you to read all about it at the springsource.org website:
http://www.springsource.org/spring-framework
We also don't have the space to cover all possible scenarios for portlet conversion, so we'll make a few other assumptions as well:

  • The existing portlet will be named ExamplePortlet
  • We are using at least Java 1.5+ with JSTL
  • We are using the Portlet 2.0 - JSR 286 specification (this way, I can show additional portlet cycle conversions)
  • We will also assume since you're still using plain portlets that we are manually downloading Spring, as this is the most likely case for old portlet projects. Otherwise, you'll use whichever dependency loading mechanism you have (i.e. Maven, etc.).

DOWNLOADING SPRING

First, download the Spring Framework. The newest release at this time of writing is 3.2. This link will take you to a page where you can register for code release notifications before downloading the framework (though it will allow you to skip past that):
http://www.springsource.org/spring-community-download
File name to download: spring-framework-3.2.x.RELEASE-dist.zip

INSTALLING SPRING

After unzipping the framework, drop the Spring JAR files into your WEB-INF/lib directory. These are the minimal required JARs:

  • spring-beans-3.2.2.RELEASE.jar
  • spring-context-3.2.2.RELEASE.jar
  • spring-context-support-3.2.2.RELEASE.jar
  • spring-core-3.2.2.RELEASE.jar
  • spring-expression-3.2.2.RELEASE.jar
  • spring-web-3.2.2.RELEASE.jar
  • spring-webmvc-3.2.2.RELEASE.jar
  • spring-webmvc-portlet-3.2.2.RELEASE.jar

Spring is very modularized. As you require additional functionality tied into Spring (i.e. JDBC, LDAP, etc.), you'll add additional Spring JAR files.
The first change to your portlet will be to add a portlet specific Spring context file. By default, it is named using the format <portlet_name>-portlet.xml. However, since we're using an existing portlet, we will specify and reference our name directly and place it in a directory named 'context' under the WEB-INF folder. This following fragment has the minimum needed for declaring a Spring portlet using annotations. Yes--we will catch up immediately with the rest of the world and use Spring annotated classes rather than using the older Spring portlet classes.

FILE: EXAMPLE-PORTLET.XML - PLACED IN /WEB-INF/CONTEXT:

[xml]
<?xml version="1.0" encoding="UTF-8"?>
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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- scan the package structure for our Spring annotated classes -->
<context:component-scan base-package="com.isostech"/>
<!-- default annotation handling beans -->
<bean class="org.springframework.web.portlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<bean class="org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
<!-- defines location and file extension of JSPs, requires JSTL -->
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
exampleportlet/"/>
<property name="suffix" value=".jsp" />
</bean>
</beans>
[/xml]

FILE: APPLICATIONCONTEXT.XML - PLACED IN /WEB-INF/CONTEXT:

The second file added (though not required) is a general portlet application context file. Remember that a portlet application is the entire set of portlets contained within a single WAR file and defined within a single portlet.xml. The benefits of having a top-level context file is that you'll be able to share Spring beans between portlets instead of duplicating the effort between all the <port_name>-portlet.xml files. For example, if the directory structure for JSPs is the same between all your portlets, then the viewResolver could be defined once here and shared between your portlets. We will place this in the same location as the portlet context file.
[xml]
<?xml version="1.0" encoding="UTF-8"?>
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.xsd">
<!-- declare beans shared between portlets here -->
</beans>
[/xml]

EDIT YOUR WEB.XML

Two things are required in the web.xml file: (1) the location/listener to load the portlet application context (2) the Spring view rendering servlet -- a bridge servlet that allows the portlet framework to use the Spring servlet framework's renderers.
[xml]
<!-- previous content -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/context/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<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>
<!-- content after -->
[/xml]

EDIT YOUR PORTLET.XML

There are two pieces we'll add to your portlet.xml file to use SpringMVC. The first is a declaration of Spring's Dispatcher Portlet as your portlet class (in the case of our example portlet, this will replace it). The second is the location of the portlet context file matching the name we defined earlier.
[xml]
<portlet-class>org.springframework.web.portlet.DispatcherPortlet</portlet-class>
<init-param>
<name>contextConfigLocation</name>
<value>/WEB-INF/context/exampleportlet-portlet.xml</value>
</init-param>
[/xml]

CONVERTING THE PORTLET CLASS

First off you may be wondering, "Why convert our portlet at all?" The answer is you may not need to. You can easily add in additional Spring portlets alongside your existing portlets within your portlet application. However, let's assume you need to make further enhancements to your portlet and you'd like to start breaking it up into separate controllers instead of adding onto your single monolithic portlet class. With this assumption, let's go over the changes.
At a minimum, and for simplicity, we will split up our portlet into different controllers each declaring the portlet mode they handle. Each controller will be the default controller used for its corresponding portlet mode. Now you may be asking, "Why not change our portlet class into a single controller taking care of all modes?" It is possible to do this, but only with additional portlet parameters specified in our JSPs (which you'll see at the end of this blog). However, if we declare different controllers, we can avoid changing the existing JSPs -- a huge plus. It's a lot easier and cleaner splitting up into controllers rather than hunting down all your portlet tags to add parameters.
Let's assume your portlet uses view and edit modes. You will create two controllers each with the contents of your existing portlet class.

Existing portlet class:

[java]public class ExamplePortlet extends javax.portlet.GenericPortlet { }[/java]

New replacement controller classes:

Note the @Controller annotation and the @RequestMapping annotation specifying the portlet mode.  The normal convention for the controller value (the ID to refer to this object) is to use the class name in lowercase.
[java]
@Controller(value="exampleViewController")
@RequestMapping(value="VIEW")
public class ExampleViewController { }
@Controller(value="exampleEditController")
@RequestMapping(value="EDIT")
public class ExampleEditController { }
[/java]

Required import statements for controllers (depending on portlet cycles handled):

[java]
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.portlet.bind.annotation.RenderMapping;
import org.springframework.web.portlet.bind.annotation.ActionMapping;
import org.springframework.web.portlet.bind.annotation.EventMapping;
import org.springframework.web.portlet.bind.annotation.RenderMapping;
import org.springframework.web.portlet.bind.annotation.ResourceMapping;
[/java]
Strip away the unneeded functionality from each controller retaining the required code for the corresponding portlet mode -- your view controller holding the view code and the edit controller holding the edit code. In addition, you will convert each of your standard GenericPortlet cycle methods or annotated methods to Spring-specific annotated methods. Please note for these method signature changes that we will not change the way exceptions have been handled in the portlet up to this point. So if you're handling in code or specifying through your web.xml file, it'll stay the same for now. Spring has some nice exception handling mechanisms that you can use going forward.

RENDER CYCLE METHOD CONVERSION

If you declare a doView method in your portlet or you specify the portlet annotation, they will both convert to the same Spring styled annotated declaration. NOTE: Spring allows for very flexible method signatures (more on this in a future blog), but for ease of use we will change the void signature method to return a String and retain the same method parameter signature. The String that will be returned by the method will be the name of the JSP (remember, we already specified the path and extension in the portlet context file). We will no longer use the file name directly to do a 'PortletRequestDispatcher include' as the portlet currently does.

From standard method:

[java]
public void doView(RenderRequest renderRequest, RenderResponse renderResponse)
throws PortletException, IOException { }
[/java]

Or annotated method:

[java]
@RenderMode()
public void myRenderMethod(RenderRequest, RenderResponse)
throws PortletException, IOException { }
[/java]

To Spring:

[java]
@RenderMapping()
public String anyMethodName(RenderRequest renderRequest, RenderResponse renderResponse)
throws PortletException, IOException { }
[/java]

ACTION CYCLE METHOD CONVERSION

If you're handling actions the old-fashioned way through a single processAction method containing all the conditions for your separate actions, all you need to do is add the Spring @ActionMapping annotation.

From standard method:

[java]
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, IOException { }
[/java]

To Spring:

[java]
@ActionMapping()
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, IOException { }
[/java]
However, if you're using the portlet action annotations and handling your actions separately, passing in a 'javax.portlet.action' value from your JSP, you will change from the standard portlet annotation to the Spring annotation.

From annotated method:

[java]
@ProcessAction(name="myJavaxPortletAction")
public void myActionMethod(ActionRequest request, ActionResponse response)
throws PortletException, IOException { }
[/java]

To Spring:

[java]
@ActionMapping(value="myJavaxPortletAction")
public void myActionMethod(ActionRequest request, ActionResponse response)
throws PortletException, IOException { }
[/java]

RESOURCE CYCLE METHOD CONVERSION

If you're serving up resources (i.e. AJAX calls, streaming files, etc.), add Spring's @ResourceMapping annotation to the portlet's serveResource method.

From standard method:

[java]
public void serveResource(ResourceRequest request, ResourceResponse response)
throws PortletException, IOException { }
[/java]

To Spring:

[java]
@ResourceMapping()
public void serveResource(ResourceRequest request, ResourceResponse response)
throws PortletException, IOException { }
[/java]
Not required as part of this conversion, but a nice treat going forward, Spring allows you to specify separate resource mappings so that you can use separate resource methods instead of a single serveResource method. Very nice! To use them, specify the 'id' attribute in the portlet resource URL tag on the JSP.

In JSP:

[xml]<portlet:resourceURL id="myResourceId"> ... [/xml]

Resource mapping by ID:

[java]
@ResourceMapping(value="myResourceId")
public void myResourceMethod(ResourceRequest request, ResourceResponse response)
throws PortletException, IOException { }
[/java]

EVENT CYCLE METHOD CONVERSION

Similar to the action declarations above, if you're using a single processEvent, you declare a single @ProcessEvent annotation. Otherwise, use separate annotation methods for each declared event.

From standard method:

[java]
public void processEvent(EventRequest request, EventResponse response)
throws PortletException, IOException { }
[/java]

To Spring:

[java]
@EventMapping()
public void processEvent(EventRequest request, EventResponse response)
throws PortletException, IOException { }
[/java]

From annotated method:

[java]
@ProcessEvent(name="myEventName")
public void myEventMethod(EventRequest request, EventResponse response)
throws PortletException, IOException { }
[/java]

To Spring:

[java]
@EventMapping(value="myEventName")
public void myEventMethod(EventRequest request, EventResponse response)
throws PortletException, IOException { }
[/java]

CONVERTING OTHER CUSTOM PORTLET CYCLES

If you use other portal container specific portlet modes (i.e. IBM Websphere Portal 'config' mode) specified in your portlet.xml, you can use the same @Controller and @RequestMapping declarations as above using your custom mode name directly. This is a nice feature as it doesn't require additional overrides like a traditional portlet class requires in order to do your custom mode.
[java]
@Controller(value="exampleConfigController")
@RequestMapping("config")
public class ExampleConfigController { }
[/java]
Once all these changes have been made (and any other corrections along the way), your portlet should behave as it did previously. Now, you can add additional Spring controllers using as many or few additional parameters in order to distinguish them from the default portlet mode controllers you've just created. Here's an example declaration.
[java]
@Controller(value="anotherExampleViewController")
@RequestMapping(value="VIEW", params="anyParamName=anotherValue")
public class AnotherExampleViewController { }
[/java]
Now, any JSP that has a portlet parameter called 'anyParamName' with a value of 'anotherValue' during the VIEW cycle will go to this controller. Additionally, you can even add portlet parameters to specify separate render methods now. Though it's best to avoid having your controllers get too big, this does come in handy on occasion.
[java]
@RenderMapping(params="anyOtherParamName=anotheRenderValue")
public String anyMethodName(RenderRequest renderRequest, RenderResponse renderResponse)
throws PortletException, IOException { }
[/java]
I hope this quick Spring conversion exercise has shown you how fast you can get up and running using Spring MVC with your existing portlets. In future blogs, I will cover more Spring-specific features to help abstract away some of the tedious repetitive work that comes with writing portlets. In the meantime, explore and have fun using SpringMVC!

TAGS: Software Development, Portal, Spring, Software Solutions

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Subscribe to Our Newsletter

Recent Blog Posts