Wednesday, August 31, 2005

Understanding JSR168 Portlets by comparison with servlets

What follows is a few loose developer notes for those planning to produce portlets who have an existing understanding of JSP and servlets. I started writing this as a means to crystallise my burgeoning understanding of the portlet specification, if it seems a little dislocated in places that is probably because I don't fully understand the portlet specification properly yet. I've started developing portals using the Pluto portal as my development platform. Using the Pluto admin portlet Deploy War means that I can upload and install my portlets without getting bogged down with hand editing portlet.xml etc.

Firstly for comparisons sake a quick reminder of what should be very familiar to JSP and Servlet programmers. In our servlet containers we have the context interfaces:

  • ServletContext, HttpSession, PageContext
When writing JSP pages these correspond to several attribute scopes:
  • Application (ServletContext)
  • Session (HttpSession)
  • Request (HttpServletRequest)
  • Page (PageContext)

Servlet and Portlet requirements

Whilst a servlet requires only a servlet container to work (i.e. Tomcat, Resin, Orion etc.) a portlet requires a servlet container, a portlet container and a portlet consumer (i.e. a portal). Apache Pluto provides the reference implementation of the JSR168 portlet standard. Pluto is comprised of a portlet container, a simple example portal and some test portlets. The portlet implementation makes use of the underlying servlet container and so there is much direct correspondence between how you would expect a servlet to behave and how the corresponding portlet functionality behaves, there are also some significant differences.

Comparing javax.portlet with javax.servlet

What I have found both comforting and confusing is the apparent similarity between the Servlet specification and the JSR168 Portlet specification. Let us first examine some similarities:

javax.servlet
  • Interfaces
    • RequestDispatcher
    • Servlet
    • ServletConfig
    • ServletContext

    • ServletRequest



    • ServletResponse


  • Classes
    • GenericServlet
javax.servlet.http
  • Interfaces
    • HttpSession
javax.portlet
  • Interfaces
    • PortletRequestDispatcher
    • Portlet
    • PortletConfig
    • PortletContext

    • PortletRequest
    • ActionRequest
    • RenderRequest

    • PortletResponse
    • ActionResponse
    • RenderResponse
  • Classes
    • GenericPortlet
javax.portlet
  • Interfaces
    • PortletSession

Note: The above list is not the full API specification of either javax.servlet or javax.portlet and has been arranged to enable a clear comparison.

Portlets should use portlet methods and portlet tag library

When writing a portlet, however tempting it might be, you should try and abstain from using the javax.servlet specification methods inappropriately. Although technically you can store/access portlet objects using javax.servlet methods there are many facets of the portlet which are wholly dependant on the portlet container and portal implementations. You should learn to make use of the portlet taglib, rather than using relative URLs and such. The portlet taglib will generate portlet instance specific URLs which it obtains via the portlet container and portal.

Pseudo-MVC architecture and the portlet lifecycle

You will notice above that ServletRequest and ServletResponse map to several equivalent Portlet interfaces. For example there are two specialised versions of PortletRequest; ActionRequest and RenderRequest. Much like in MVC frameworks, e.g. Struts, there are several distinct concerns that can be distinguished in a portlet, not all portlet methods need necessarily result in new content being produced in the portal. Like a servlet, a portlet must have the lifecycle methods init() and destroy(). Additionally a portlet must implement processAction() and render() methods. However, if a portlet extends the GenericPortlet it does not need to implement all these methods explicitly and the GenericPortlet adds facility for new "modes" of rendering.

Although clearly related there is an intentional clear separation between ActionRequest and the related RenderRequest object which is usually created subsequently. Attributes stored in ActionRequest scope are not automatically transferred into RenderRequest scope but can be transferred into the render cycle via a special setRenderParameter() methods in the ActionResponse object. To borrow some Struts terminology:

"Action" method of the portlet
processAction(ActionRequest request, ActionResponse response): this is usually called to change state resulting from a user action. It can be called directly from the portlet view by using the portlet taglib <portlet:actionURL> in a hyperlink or form submission.
"View" methods of the portlet
render(RenderRequest request, RenderResponse response): this can either be invoked directly from a view by using the portlet taglib <portlet:renderURL> in a hyperlink or can follow a processAction response.
doView(RenderRequest request, RenderResponse response), doEdit(RenderRequest request, RenderResponse response), doHelp(RenderRequest request, RenderResponse response): called to render specific portlet "MODE" screens
"Controller" component of the portlet
Strictly speaking the Portal and Portlet container act like a MVC controller, at least as far as they control when a portlet is invoked for actions and rendering etc.

To add further MVC-style separation it is advisable that your "View" content should be included using a RequestDispatcher from JSP pages or from XSL transformations but this is not enforced.

I use the term Pseudo-MVC because unlike with Struts the order of interaction is not rigidly enforced by the portlet container. I have yet to understand what best practices for portlet creation are but it is no co-incidence that there is a lot of work around bridging existing MVC architectures like Struts and JSF with the portlet specification. The fit of MVC to portlet appears to be quite a natural one.

crossContext

The Pluto portal is itself a web application. It requires "crossContext" access in order to access portlets stored in other webapp contexts on the application server. This allows the portal to obtain the appropriate ServletContext and invoke the portlets using an appropriate RequestDispatcher.

emptySessionPath for sharing data between servlet and portlet

One thing that confused me was that under certain circumstances, i.e. Tomcat 5.5X with "emptySessionPath" set to true, the portal can use share the same sessionid as the portlets even when they exist outside of the portal's ServletContext. This does not mean that portal and portlet share the same session content. What it does facilitate is that portlets and servlets running in the same web application can share data but the sessions can become very confused if you have both stand alone web applications and portlets sharing the same web application.

PortletSession scopes

The concept of scope extends to the portlet specification but is slightly different when it comes to the PortletSession. There are two possible portlet attribute scopes APPLICATION_SCOPE and PORTLET_SCOPE. A portlet application is stored within a webapp context. There can be several portlets within a single portlet application. Each portlet may have more than one instance in the portal. An object stored in Portlet application scope is available to all portlets and instances thereof installed in that portlet application. An attribute stored in portlet scope is private to a specific portlet instance.

0 comments: