Introduction

This document describes how to take a Jetspeed-1 Velocity portlet or an OGCE 1.x Velocity portlet and convert it to a JSR 168 compliant portlet for use in a JSR 168 compliant portlet container. It is assumed that the reader already knows something about the JSR 168 API and Velocity templating. Hence, this is not a tutorial on writing JSR 168 portlets nor on using Velocity as the templating framework for Java portlets.

If you need more background information to get started, we suggest the following:

  1. The OGCE GGF 12 Portal Tutorial includes how-to material on JSR 168 portlet writing, Velocity, and HttpUnit.
  2. If you are looking for more information on JSR 168, click here.
  3. If you are interested in learning more about Velocity, try the Velocity Developer's Guide.

Porting the Java Code

Render Methods

Change method buildNormalContext to buildViewContext.

        public void buildNormalContext(VelocityPortlet portlet,
                                       Context context,
                                       RunData runData)
        
        

becomes

        public void buildViewContext(RenderRequest request,
                                     RenderResponse response,
                                     Context context)
        
        <!-- doAction methods (ActionRequest/Response) -->
        

Likewise change buildConfigureContext to buildEditContext. The old buildMaximizedContent has no equivalent in the JSR 168 world. However, JSR 168 introduces a new viewing mode, the Help mode, and so this VelocityPortlet API also supplies you with a buildHelpContext method.

The buildXXXContext methods are called when the portlet container needs to get a particular view of the portlet not initiated by an action on the portlet. None of these methods are called if an action has just been processed. For example, when first loading a portlet, the buildViewContext method gets called to create the initial Velocity context for the default view template. If the user then submits a form within that portlet and this gets handled by an action method, then the action method creates the context for the response and buildViewContext is not called.

Action Methods

Change the method signature of action methods from

          public void doSomething(RunData runData, Context context)
        

to

          public void doSomething(ActionRequest request,
                                  ActionResponse response,
                                  Context context)
        

As before there is a special action method, doCustomize that gets called whenever the portlet processes an action while in EDIT mode, and you'll need to implement that if your portlet supports the EDIT mode.

Setting the Template

Change setTemplate calls to give the full file name of the template as opposed to just the part without the extention, and pass it the PortletRequest object as opposed to the RunData object. From this:

        setTemplate(runData, "my-template");
        

To this:

        setTemplate(request, "my-template.vm");
        

Where request could be a RenderRequest or an ActionRequest object.

Portlet Preferences

In Jetspeed-1, you set preferences (string name value pairs) for a portlet typically by doing the following:

        String mypref = portlet.getAttribute("myprefname",
                                             "defaultvalue", 
                                             runData);
        

Now you must use the JSR 168 API, like so:

        PortletPreferences prefs = request.getPreferences();
        prefs.getValue("myprefname", "defaultvalue");
        prefs.setValue("myprefname", "newvalue");
        prefs.store();
        

Note that the JSR 168 specification says that you can only set preferences (via the setValue() and store() methods) within the scope of the processAction method, i.e., only within one of the doXXX action methods. See the JSR 168 specification for more details.

Note also that with Turbine you could also set something like preferences, or persistent portlet settings, with the following:

        runData.getUser().setPerm("myobject", myObject);
        

Nothing like this is available in the JSR 168 API. Persistent storage is left up to the portal framework or the portlet developer.

Portlet Session

For storing objects to exist only during the lifetime of the user's session, one would have used something like:

        runData.getUser().setTemp("mytemp", myTemp);
        

or using the HttpSession object. In JSR 168, you have access to a PortletSession object, and can do the following:

        PortletSession session = request.getPortletSession(true);
        session.setAttribute("mytemp", myTemp);
        

Note that the PortletSession object has two different scopes, a Portlet scope and an Application scope. By default, and in the above example, session attributes are set in the Portlet scope. If you wish for session attributes set in a portlet to be available to servlets within the same portlet application, then you must set these session attributes in the Application scope. For example,

        PortletSession session = request.getPortletSession(true);
        session.setAttribute("mytemp", myTemp, PortletSession.APPLICATION_SCOPE);
        

Please see the JSR 168 specification for more details regarding the PortletSession and its different scopes.

Request Parameters

In Jetspeed 1 portlets, you retrieved the request parameters with a Turbine call:

        String paramValue = runData.getParameters().getString("paramName");
        

Now you should use:

        String paramValue = request.getParameter("paramName");
        

Where request could be a RenderRequest or a ActionRequest object.

Porting the Velocity Templates

Skins

Skins are quite a bit different in JSR 168. The JSR 168 specification defines a set of standard CSS style classes that portlets can use to blend in with the portal's look and feel. So, you'll need to replace things like this:

        <font color="${skin.Color}">
        </font>
        

with:

        <div class="portlet-font">
        </div>
        

where portlet-font is one of the CSS style classes that JSR 168 defines. For more information, see Appendix C of the JSR 168 specification.

Context Path

Because JSR 168 splits portlets and containers across different webapps, you'll find that you need to add the full context path to relative URL's within your Velocity templates. This is facilitated with the provided $contextPath context variable. For example, the following:

        <img src="images/back.png" />
        

should change to:

        <img src="${contextPath}/images/back.png" />
        

Portlet Links

This VelocityPortlet code supplies you with two Velocity variables, $renderURL and $actionURL. $renderURL gives you a link to the initial view of the portlet (the view setup by buildViewContext). $actionURL gives you a link to your portlet that will invoke the processAction method. So, instead of setting up your form in Velocity like this:

        <form action="$jslink.Template" method="post">
        

do this:

        <form action="$actionURL" method="post">
        

If you need to include parameters in your action URL, you probably did something like the following before:

        #set ($link=$jslink.getAction("portlets.MyPortlet", "doSomething").
          addQueryData("name1", $value1).addQueryData("name1", $value2))
        

Now you'll need to use the following convenience method instead:

        #set ($link=$portlet.createActionURLWithParams( $response, 
          ["actionMethod_doSomething", "name1", "name2"], 
          ["", "$value1", "$value2"] ))
        

Note that a set in Velocity needs to all be on one line, it is wrapped here for formatting purposes. The $response variable and the $portlet variable are provided for you; they refer to the RenderResponse and this portlet implementation (you can think of $portlet as equivalent to Java's this). The actionMethod_ prefix will be explained shortly.

eventSubmit_ vs. actionMethod_

In Jetspeed 1, you prefix eventSubmit_ to the name of the method you want to be invoked in your portlet code to handle the a particular web request, for example, a form submission. With this VelocityPortlet, use actionMethod_ as the prefix instead. For example, the following:

        <input type="submit" name="eventSubmit_doUpload" value="Upload File" />
        

becomes:

        <input type="submit" name="actionMethod_doUpload" value="Upload File" />
        

However, the OGCE VelocityPortlet is backward compatible with the eventSubmit_ prefix, so this change isn't mandatory.

Portlet Configuration, portlet.xml

Turbine and Jetspeed properties

Properties set in Turbine or Jetspeed properties files need to be moved into the portlet's portlet.xml deployment descriptor as init params. In the portlet.xml, an init param is defined as follows:

        ...
        <init-param>
            <name>name1</name>
            <value>value1</value>
        </init-param>
        ...
        

You can then access the value of this in your portlet code with:

        String value1 = getPortletConfig().getInitParameter("name1");
        

Porting xreg files

The old xreg file cannot be directly ported. Create a new portlet.xml file and put the information (as much as is possible) from the old xreg into the new portlet.xml following the portlet.xml schema. See the JSR 168 specification for details, or any of the OGCE velocity portlets for examples.

Velocity Settings, velocity.properties

You can create a velocity.properties settings file to be passed to the Velocity engine when it is created. If you do not supply one, the following default settings hold.

  • The velocity.log file will be placed in this portlet application's root directory, i.e. tomcat/webapps/myportlet/velocity.log will be it's location.
  • The directory where Velocity should look to find template files is templates/, in the root of the portlet's application directory.

If you create your own velocity.properties file (refer to the Velocity Developer's Guide for more information), you can place it in the root of the portlet application directory, and the VelocityPortlet code will discover and load it. If you wish to place it else where, provide it's absolute path from the root of the portlet application as an init parameter with the name of org.apache.velocity.properties. For example, if you create a velocity.properties file and place it in /WEB-INF/velocity.properties, then add the following to your portlet.xml:

        ...
        <init-param>
            <name>org.apache.velocity.properties</name>
            <value>/WEB-INF/velocity.properties</value>
        </init-param>
        ...
        

Default Templates

To specify the default template for the portlet to use when in view mode, set the init-param with name default-view. For example:

        ...
        <init-param>
            <name>default-view</name>
            <value>my-view.vm</value>
        </init-param>
        ...
        

Likewise, you can set the default edit and help mode templates using the default-edit and default-help init params.

Alternatively, you could use the built-in defaults and name your view, edit and help default templates view.vm, edit.vm, and help.vm respectively. In this case, init params aren't required.

Miscellany

Macros

If your old Velocity portlets made use of macros provided by Jetspeed or other macros, you'll need to similarly port those as well. You then need to place your Velocity macros file in the same directory as your templates.

Portlet Help Documentation

You may have create links in your portlet which cause a pop-up window when clicked with help documentation. Or you may have used some other mechanism to get help documentation to the user through your portlet. Now that there is a standard help portlet mode, consider making your help documentation available via this mechanism instead.

Action and Render Methods Calling Each Other

One of the things that one could do with the older API was have an action (i.e., "doSomething") method call one of the buildXXXContext methods rather trivially. This is a little bit trickier with JSR 168. You can't do this, for example:

        doSomething(ActionRequest request, ActionResponse response,
                    Context context) {
        ...
            buildViewContext(request, response, context);
        ...
        }
        

The reason this doesn't work is that an ActionRequest object doesn't cast to a RenderRequest object and likewise for the response objects. In the case that you have code in your action method or buildXXXContext method that you wish to be called by other methods, put that code into a different function like so:

        setupContext(PortletRequest request, PortletResponse response,
                     Context context) {
        ...
        }
        

You'll be able to call this method from either a buildXXXContext method or an action method.