In October 2003, the Java Community Process issued the final release of Java Specification Request (JSR) 168: the Portlet Specification (see Resources). JSR 168 articulates the first programming standard for Java portlet development. Previously, a portlet developed for WebSphere Portal Server, for example, couldn't run in another portal container such as BEA WebLogic Portal. A portal container isn't a required component for a J2EE application server. Nevertheless, this lack of portability was a deviation from standard J2EE enterprise applications, which (when built according to spec) can be deployed to any J2EE-compliant application server. Lack of portlet portability and the associated vendor lock-in deterred businesses from buying portal servers. By putting an end to portlet-development anarchy, JSR 168 has laid those businesses' fears to rest.
Java developers have a freely available tool for testing that they've written their portlets in accordance with the Portlet Specification. Apache Pluto is the reference implementation for JSR 168. It is a portlet container that implements the Portlet API. Portlet containers like Pluto and IBM WebSphere Portal Server serve as the runtime environment for portlets, much the way a servlet is powered by the runtime environment of a Web application server's servlet container. However, the portlet container is not standalone; it lives on top of a servlet container and relies on its services. In this article, we'll show you how to write a simple portlet and test it against the Pluto portlet container.
Apache Pluto requires Java SE 5. If you haven't already installed this version of the JDK, download and install it to proceed with this article's exercise (see Resources).
Next, you need to set or change the JAVA_HOME environment variable, which Pluto references to locate your JVM. (The Sun installer does not set the value of JAVA_HOME by default.) If you do not have a previous JVM version installed on your system, follow these steps (these instructions assume that you are running Windows):
- Right-click My Computer and select Properties from the context menu.
- Click the Advanced tab.
- At the bottom of the window, click the Environment Variables button.
- In the System variables pane, click New.
- Enter
JAVA_HOMEfor the variable name. For the variable value, enter the directory containing the Sun JVM install.
Figure 1 shows what the value of JAVA_HOME would be if you chose to install the Java 5.0 SDK to the C:\Program Files\IBM\Java50 directory:
Figure 1. Changing the
JAVA_HOME environment variable
Now that your Java environment is properly set up, download the ZIPet file containing the binaries for Pluto (pluto-current.zip) from Apache's Web site (see Resources).
Assuming you're as security-conscious as we are, you'll want to be sure that the ZIP file download did come from Apache Software Foundation. The files are digitally signed using both PGP and md5. Using GnuPG (see Resources) and the ASCII armor file (pluto-current.zip.asc), you can verify the authenticity of this download.
First, add the Apache Pluto public key to your public key ring by entering the command shown in the top line of Listing 1. The KEYS file is available on the Pluto download page.
Listing 1. Import the KEYS file
C:\>gpg --import KEYS gpg: key E41EDC7E: public key "Carsten Ziegeler <cziegeler@apache.org>" importe gpg: key 320B4FB4: public key "Nick Lothian (Apache) <nlothian@apache.org>" imp rted gpg: key DD4200EA: public key "David H. DeWolf <david@daviddewolf.com>" importe gpg: Total number processed: 3 gpg: imported: 3 gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model gpg: depth: 0 valid: 1 signed: 0 trust: 0-, 0q, 0n, 0m, 0f, 1u gpg: next trustdb check due at 2011-03-08 |
Next, enter the command shown in the top line of Listing 2 to verify the signed ZIP file you received from Apache:
Listing 2. Verify pluto-1.0.1.zip.asc
C:\>gpg --verify pluto-1.0.1.zip.asc gpg: Signature made 10/19/05 09:11:31 using DSA key ID DD4200EA gpg: Good signature from "David H. DeWolf <david@daviddewolf.com>" gpg: aka "David H. DeWolf <ddewolf@apache.org>" gpg: aka "David H. DeWolf <ddewolf@rocketmail.com>" gpg: aka "David H. DeWolf <ddewolf@gmail.com>" gpg: aka "David H. DeWolf <david.dewolf@digitalfocus.com>" gpg: WARNING: This key is not certified with a trusted signature! gpg: There is no indication that the signature belongs to the owner. Primary key fingerprint: 6AA5 5850 9A7B 275C E0BC 2851 B217 63FA DD42 00EA |
If the gpg --verify command generates output similar to what's displayed in Listing 2, you can be confident that the Pluto archive file is in fact from Apache. You can ignore the warning indicating that the signature is not trusted. The only way to guarantee that the public key signature is from the owner is for the owner to hand you the key on a disk, in person. However, with the steps you've taken, you can be reasonably certain that the ZIP file you downloaded from Apache has not been compromised. (See Resources for more information on verifying Apache downloads.)
Now you're ready to extract the ZIP file onto your machine. We'll assume you're unzipping Pluto into the root directory of your C:\ drive:
C:\>unzip pluto-current.zip |
This creates a C:\pluto-1.0.1 directory that includes a bin subdirectory. Navigate to the bin subdirectory and enter startup.bat at the command prompt to start the Pluto server.
You'll administer and view your portlets from the Pluto home page, shown in Figure 2. Launch your Web browser and navigate to http://localhost:8080/pluto/portal.
Figure 2. The Pluto home page
Note: If the Pluto home page does not appear, check to make sure that your firewall is not preventing Pluto from accepting connections.
Now that you have Pluto up and running, you'll create a simple portlet and use Pluto to test it for JSR 168 compliance.
To see how to use Pluto as a JSR 168-compliant test bed for your portlets, you need a portlet to test. For this exercise, you'll create a simple portlet that, depending on user input, converts the contents of a text box to all uppercase or all lowercase (see Figure 3):
Figure 3. The Change Case portlet
We'll start by looking at the portlet.xml file, shown in Listing 3. The portlet.xml file is a descriptor file that contains configuration details about the bundled portlet(s) in your WAR file.
Listing 3. The portlet.xml file
<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd
http://java.sun.com/xml/ns/portlet/portlet-app_1_0.xsd"
id="com.ibm.changecase.ChangeCasePortlet">
<portlet>
<portlet-name>ChangeCase</portlet-name>
<display-name>Change Case Portlet</display-name>
<portlet-class>com.ibm.changecase.ChangeCasePortlet</portlet-class>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
</supports>
<portlet-info>
<title>ChangeCasePortlet</title>
</portlet-info>
</portlet>
</portlet-app>
|
The <portlet-app> tag in Listing 3 defines the XML schema definition and ID for a portlet application. A portlet application can contain zero or more portlets. You use <portlet> tags to define the individual portlets within the portlet application:
<portlet-name>- Provides a name that can be used internally or programmatically to reference the portlet.<display-name>- A short name of the portlet, used to display the portlet name in GUI tools that vary by portal container.<portlet-class>- The class that acts as the controller for the portlet.<supports>- These tags define the portlet modes and mime types that the portlet will support.<title>- You can define the portlet's preferred title within portlet.xml. However, how that title will be used is up to the portlet container.
Listing 4 shows the com.ibm.changecase.ChangeCasePortlet portlet class referenced in portlet.xml. This class must implement the javax.portlet.Portlet interface, but fortunately, you don't need to implement the Portlet interface directly. JSR 168 defines a default implementation for the javax.portlet.Portlet interface called the javax.portlet.GenericPortlet class. The com.ibm.changecase.ChangeCasePortlet class extends the GenericPortlet class.
Listing 4. The ChangeCasePortlet class
package com.ibm.changecase;
import java.io.*;
import javax.portlet.*;
/**
*
* A sample portlet based on GenericPortlet
*
*/
public class ChangeCasePortlet extends GenericPortlet {
private static String VIEW_JSP = "/view.jsp";
protected void doView(RenderRequest request, RenderResponse response)
throws PortletException, IOException {
response.setContentType(request.getResponseContentType());
PortletContext context = getPortletConfig().getPortletContext();
context.getRequestDispatcher(VIEW_JSP).include(request, response);
}
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, java.io.IOException {
//Do Action Handling here.
}
}
|
Notice the choice to override the doView() and processAction() methods. The processAction() method is called any time that a portlet action occurs. The doView() method is called when the user is in the portlet's view mode. JSR 168 supports other modes, such as help mode and edit mode. However, if you refer back to Listing 3, you can see in the <supports> section that this portlet supports only the view mode.
Now take a closer look at the doView() method in Listing 4. The model-view-controller (MVC) design pattern is frequently used in portlet coding, as it is in Java servlet coding. Accordingly, in your portlet, the rendering responsibilities are handed off to view.jsp. Alternatively, you could implement the view in the doView() method using prinltn logic:
response.getWriter().println("<p>Hello World</p>()");
|
The problem with this approach is that the designer of your graphical user interface would need to have knowledge of portlet technology to write the doView() method. JSP developers are decoupled from the intricacies of Java development and can focus on the development of their front-end interfaces. Listing 5 shows view.jsp:
Listing 5. view.jsp
<%@ taglib uri="http://java.sun.com/portlet" prefix="portlet"%>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1" session="false"%>
<portlet:defineObjects />
<%String textBox = renderRequest.getParameter("textBox");
if (textBox == null)
textBox = "";
String caseString = renderRequest.getParameter("case");
boolean isUpperCase = true;
if ((caseString != null) && (caseString.equals("false"))) {
isUpperCase = false;
}
String errorMessage = renderRequest.getParameter("errorMessage");%>
<%if (errorMessage != null) {%>
<p><%=errorMessage%></p>
<%}%>
<FORM name="<portlet:namespace/>caseform" action="<portlet:actionURL/>">
<INPUT type="text" name="textBox" size="20" value="<%=textBox%>">
<p><INPUT type="radio" name="case" value="lowercase"
<%if (!isUpperCase) {%> checked="checked" <%}%>>To Lowercase</p>
<p><INPUT type="radio" name="case" value="uppercase"
<%if (isUpperCase) {%> checked="checked" <%}%>>To Uppercase</p>
<INPUT type="submit" name="<portlet:namespace/>submitCase"
value="Change Case"></FORM>
|
First, notice that you define the portlet tag libraries in view.jsp. This is necessary for the JSP parser to recognize your portlet tags. The first portlet tag that you use is <portlet:defineObjects/>. This tag gives you access to the renderRequest, renderResponse, and portletConfig objects. Using the renderRequest object gives you access to the requestParameters. Your portlet class passes values to the view JSP via the request parameters.
Next in your view.jsp, you create a form that sends the form data to the portlet class. To send the form data, you must create an actionURL, which causes the ChangeCasePortlet portlet class's processAction() method to be called. You use the <portlet:actionURL/> tag to create an actionURL. Notice that in view.jsp you set the value of the textbox and the radio button to the value passed back to the JSP by the server. Consequently, view.jsp is responsible for both handling request input and displaying the portlet's response.
Clicking the form's Submit button invokes the portlet's processAction() method, shown in Listing 6. processAction() receives an ActionRequest object from view.jsp as an input.
Listing 6. The processAction method
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, java.io.IOException {
String newCase = request.getParameter("case");
String textBox = request.getParameter("textBox");
String errorMessage = null;
boolean isUpperCase = true;
if ((newCase!=null) && (newCase.equals(ChangeCaseConstants.LOWER_CASE)))
isUpperCase = false;
else if ((newCase == null) || (newCase==ChangeCaseConstants.UPPER_CASE))
errorMessage = "Error no case selected! Select a case.";
if (textBox !=null) {
if (isUpperCase)
textBox = textBox.toUpperCase();
else
textBox = textBox.toLowerCase();
response.setRenderParameter("textBox", textBox);
} else
errorMessage = "Error, text in the text box is invalid";
response.setRenderParameter("case", Boolean.toString(isUpperCase));
if (errorMessage != null) {
response.setRenderParameter("errorMessage",errorMessage);
}
}
|
The ActionRequest object contains the data that was entered into the form. To retrieve the form data, you use the getParameter() method. In the processAction() method, you also perform the business logic, determining if the user wants uppercase or lowercase output. Based on that logic, you change the incoming text to the desired case and send the text back to the user. Data is sent back to the view using the setRenderParameter() method.
Compiling and packaging your JSR 168 portlet
Now that you've developed your portlet, you need to get it into compiled form and package it for deployment to Pluto. First, make sure that portlet-api-1.0.jar is in your CLASSPATH. Then use the javac compiler to compile ChangeCaseConstants.java and ChangeCasePortlet.java:
javac ChangeCaseConstants.java javac ChangeCasePortlet.java |
Next you need to create the required folder structure for your WAR file, which is the archive by means of which you deploy portlets to a portlet container. Place the two classes you just compiled in the classes\com\ibm\changecase directory.
To build your WAR file, you want the following directory structure, shown in Listing 7:
Listing 7. Directory structure for deployment
changeCaseWAR\
META-INF
MANIFEST.MF
WEB-INF
classes
com
ibm
changecase
ChangeCaseConstants.class
ChangeCasePortlet.class
lib
tld
portlet.tld
portlet.xml
web.xml
index.html
view.jsp
|
Note that Listing 7 introduces four files we haven't covered yet:
- portlet.tld: This is the portlet tag library. If you recall, you used the portlet tag library throughout your JSP with references such as
<portlet:defineObjects />. The portlet tag library is available in the Apache-SVN repository (see Resources). - MANIFEST.MF: Because there are no external dependencies, this manifest file contains nothing except Manifest-Version: 1.0.
- index.html: If for some reason the context root is accessed directly, index.html is displayed. You can have any properly formed HTML in your index.html.
- web.xml (shown in Listing 8): This defines the Web application that contains your individual portlet.
Listing 8. web.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app id="WebApp_ID">
<display-name>CasePortlet</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
<taglib>
<taglib-uri>http://java.sun.com/portlet</taglib-uri>
<taglib-location>tld/portlet.tld</taglib-location>
</taglib>
<taglib id="PortletTLD">
<taglib-uri>http://java.sun.com/portlet</taglib-uri>
<taglib-location>/WEB-INF/tld/std-portlet.tld</taglib-location>
</taglib>
</web-app>
|
Once you've generated MANIFEST.MF, portlet.tld, web.xml, and index.html, you can use the JAR utility to archive the structure shown in Listing 7 into one WAR file:
C:\temp>jar -cvf changeCase.war |
The result is a file called changeCase.war, ready for deployment to a JSR 168-compliant portal.
Making sure your portlet is JSR 168-compliant
Now you'll use Apache Pluto to see if your portlet passes the litmus test of JSR168 compliance.
Make sure Apache Pluto is up and running and navigate to the Pluto home page (http://localhost:8080/pluto/portal). Click Admin in the sidebar. You should see the Deploy War portlet displayed on the admin page, as shown in Figure 4. Click Browse and navigate to the location of the changeCase.war file you put together in the previous section and then click Submit.
Figure 4. Pluto's Deploy War portlet
Now you must enter the layout information for the portlet application -- a rather trivial task because you have only one portlet in your portlet application. As you can see in Figure 5, you tell Pluto the portlet has one row and one column and then click Submit to submit this choice to the server:
Figure 5. Entering page-layout information for the portlet application
Next, you must define where in the portlet application's page layout your portlet will appear. You do this by mapping all the portlets in the portlet application to the rows and columns that you just defined. Because you have only one portlet to deploy and you chose earlier to have a page layout with one row and one column, enter the ChangeCase portlet at that location and click Submit, as shown in Figure 6:
Figure 6. Mapping the portlet location
To deploy the portlet, you have the option of either restarting Pluto or hot deploying the portlet application containing the portlet, as shown in Figure 7:
Figure 7. Hot deploying the portlet
A link appears on the sidebar with the name of the portlet application (Change Case). Click that hyperlink. You'll now see the portlet application, with the portlet contained inside it, as shown in Figure 8. At this point you should interact with the Change Case portlet and make sure that it functions as expected. If it does, you can rest assured that this portlet is compliant with JSR 168.
Figure 8. The Change Case portlet deployed to Pluto
The Change Case Portlet will work in any Portal container that supports the JSR 168 portlet standard.
Apache Pluto lets portlet developers be certain that their portlets will run in any JSR 168-compliant portal container. Most portal containers, including WebSphere Portal Server, include extended offerings that Portlet Specification doesn't address. For example, IBM WebSphere Portal Server offers the nonstandard click-to-action extensions (cooperative portlets). Whether or not to use the extensions is a judgment call by the enterprise using the portal technologies. As a developer, you should keep in mind that you compromise the portability of your portlets by leveraging such technologies -- but sometimes doing so meets business needs. Apache Pluto lets an organization using portlets know how much they deviate from the Portlet Specification and decide if any steps should be taken to align those portlets with the standard.
JSR 168 went a long way toward bringing order to the portal arena. However, the work of portlet standardization isn't stagnant. The Java Portlet API version 2.0 specification (JSR 286), still in development when this article was written, aims to bring J2EE 1.4 support into the portlet specification (see Resources). Portlet technologies that many vendors addressed with their own implementations in a nonstandard way (portlet filters and formalized inter-portlet communication, to mention a couple) will now make their way into the 2.0 specification. Future versions of Apache Pluto will act as the reference implementation of the JSR 286 specification. When JSR 286 becomes the de facto portlet standard, you'll still be able to use Pluto to test for compliance.
At the time we wrote this article, the alpha release of Pluto 1.1 was not available. Pluto 1.1 will have a new container architecture and include some changes that will make portlet development easier. However, Pluto 1.0.1 will still be a good tool for verifying that your portlets are JSR 168-compliant.
| Description | Name | Size | Download method |
|---|---|---|---|
| Sample code | j-plutoJSR168.war | 8KB | HTTP |
Information about download methods
Learn
-
"JSR 168 - An Introduction to the Portlet Specification": Read more about JSR 168.
-
Apache Pluto: Visit the Pluto Web site.
-
JSR 168: Portlet Specification and JSR 286: Portlet Specification 2.0: The portlet specifications are developed and maintained by the Java Community Process.
-
"Sharing information between IBM portlets and JSR 168 portlets with WebSphere Portal V5.1" (Stefan Hepper and Jerry Zheng, developerWorks, February 2006): Write a custom portlet service to enable legacy IBM portlets and JSR 168 portlets to share information as properties.
-
"Caching data in JSR 168 portlets with WebSphere Portal V5.1" (Stefan Hepper and Jerry Zheng, developerWorks, August 2005): Learn how you can cache data in JSR 168 portlets in order to avoid unnecessary back-end requests.
-
"Converting the WorldClock portlet from the IBM Portlet API to the JSR 168 portlet API" (Franziska Paeffgen and Birga Rick, developerWorks, December 2004): Find out how to convert a portlet originally developed for the IBM WebSphere Portal proprietary Portlet API to one using the JSR 168 standard portlet API.
-
Verifying Apache HTTP Server Releases: More information on verifying downloads from the Apache Web site.
-
The Java technology zone: Hundreds of articles about every aspect of Java programming.
Get products and technologies
-
Apache Pluto: Download Pluto.
-
Apache Jetspeed: An open source portlet container.
-
Java 5.0 SDK: Pluto requires Java 5.
-
IBM's WebSphere Portal Server Version 5.1: A commercial solution that supports the JSR 168 standard.
-
GnuPG: A free PGP tool.

Kulvir Singh Bhogal works as an IBM Software Services for WebSphere consultant, devising and implementing J2EE solutions at customer sites across the United States.

Mark Talbot works for IBM's Industry Solutions, developing applications for the financial sector. You can reach Mark at talbotm@us.ibm.com.
Comments (Undergoing maintenance)





