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:
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.
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.
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.
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.
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.
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.
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.
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" />
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.
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.
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");
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.
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.
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.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>
...
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.
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.
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.
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.