Skip to main content

Build and test JSR 168-compliant portlets with Apache Pluto

Are your portlets compliant with the Portlet Specification reference implementation?

Kulvir Singh Bhogal (kbhogal@us.ibm.com), Senior IT Specialist, IBM
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 (talbotm@us.ibm.com), Software Engineer, IBM
Mark Talbot photo
Mark Talbot works for IBM's Industry Solutions, developing applications for the financial sector. You can reach Mark at talbotm@us.ibm.com.

Summary:  With Apache Pluto, Java™ developers have a freely available tool for testing whether their portlets comply with the JSR 168 Portlet Specification. Developers Mark Talbot and Kulvir Singh Bhogal take you on a guided tour of portlet testing. You'll install Pluto and build, compile, package, and deploy a simple portlet to Pluto to test it for JSR 168 compliance. You'll also get a hint of what's in store for the next version of the Portlet Specification.

Date:  25 Apr 2006
Level:  Introductory
Activity:  3592 views

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.

The JSR 168 Portlet API is quite extensive. This article isn't intended to be a JSR 168 tutorial, so we won't delve deeply into the API. If you're unfamiliar with the JSR 168 API, we suggest you spend some time getting yourself up to speed with some of the materials in Resources.

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.

Installing Pluto

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).

If JAVA_HOME is already set to a previous version of the JVM that's installed on your system, changing it could break your existing JVM-dependent applications. If you already have an older JVM installed, you should update the JAVA_HOME variable directly inside of the Pluto startup script: startup.bat on Windows systems or startup.sh on Linux.

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):

  1. Right-click My Computer and select Properties from the context menu.
  2. Click the Advanced tab.
  3. At the bottom of the window, click the Environment Variables button.
  4. In the System variables pane, click New.
  5. Enter JAVA_HOME for 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).

Verifying the download

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.)

Unzipping the Pluto download

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
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.


Developing a JSR 168 portlet

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.


Conclusion

Can I use Pluto in my production environment?

Apache Pluto is suitable only as a test bed for JSR 168-compliance and is not recommended for use in production environments. For production, you should consider a more scalable container. The Apache Software Foundation has an open source portal container named Jetspeed that you can consider (see Resources). WebSphere Portal Server Version 5.1 from IBM is a commercial solution that also supports the JSR 168 standard (see Resources).

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.

The future: JSR 286

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.



Download

DescriptionNameSizeDownload method
Sample codej-plutoJSR168.war8KB HTTP

Information about download methods


Resources

Learn

Get products and technologies

About the authors

Kulvir Singh Bhogal

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 photo

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)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology, WebSphere
ArticleID=109565
ArticleTitle=Build and test JSR 168-compliant portlets with Apache Pluto
publish-date=04252006
author1-email=kbhogal@us.ibm.com
author1-email-cc=
author2-email=talbotm@us.ibm.com
author2-email-cc=crothemi@us.ibm.com

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers