Skip to main content

skip to main content

developerWorks  >  Sample IT projects  >

WebSphere Portal V4 programming, Part 1: Portlet application programming

Introduction to portlet structure and programming

developerWorks
Document options

Document options requiring JavaScript are not displayed

Discuss

Sample code


Rate this page

Help us improve this content


Level: Introductory

David Lection (lection@us.ibm.com), Senior Programmer, IBM

01 Aug 2002

In this article, David introduces the concept of portlet programming for WebSphere Portal Version 4.1. He shows you how to construct a portlet application, including the required classes and support files, and explains how to build and deploy a portlet to a working portal server. You can use the example portlets presented here as building blocks for larger portlet applications.

Part 2 of this series shows you how to implement portlet-to-portlet messaging.

Editor's Note: This is an updated version of an article written by David Lection in November 2001 about WebSphere Portal Server Versions 1.2 and 2.1. This version describes the most recent follow-on product, WebSphere Portal Version 4.1.

Introduction to portals

Portals and portal servers are red hot in Internet and Web development today. Just about everyone developing a Web site is using some sort of portal technology.

Why is portal technology so popular? If you remember back to the MS-DOS days, you know there was a time when PCs ran one application at a time. When you wanted to switch applications, you'd have to close one application and enter the name of the new app at the DOS prompt. Each application occupied the entire PC screen, and used all of the computer's resources.

Then Microsoft introduced Windows. With Windows came the promise of higher worker productivity, since each application was displayed in its own window. You could run multiple applications and switch between them simply by clicking on any running application. Windows as an operating system advanced the state of the art of computing in many ways. One of the most popular extensions was the windowed user interface. Using overlapping window-like rectangles, Windows was able to aggregate the display of several applications at the same time. Application aggregation is a primary function of a portal server.

Now, fast-forward to the birth of the World Wide Web. While the Web was a great advance in the information age, the tools to view the Web were in their design infancy. The typical design of a browser at the time was in a way like the MS-DOS of the 1980's. You entered a URL and viewed one Web site.

As the Web and associated browsers evolved, several schemes emerged that allowed the user to view aggregates of data; browsers offered support for Java and Java applets, plus other kinds of plugin interfaces. These gained popularity for presenting other channels of information -- primarily audio and video information.

Because these applet and plugin interfaces were never standardized across the available browsers, it was a nightmare for Web-savvy programmers who tried to build Web pages that would work properly on all of the most popular Web browsers.

Meanwhile, the Web servers that served up the Web sites were evolving in their own ways. These servers initially just served up very static content that comprised a Web site, but they evolved quickly into the application servers we know today. With these application servers, dynamic content could be served. Static pages gave way to servlets and JavaServer Pages (JSP) components.

Support for portals evolved from application servers and the desire to aggregate and render multiple streams of dynamic content to the user. Initially portals were custom applications built specifically to support a given Web site (Yahoo and eBay are good examples of custom-built portals).

As these portals evolved, systems and frameworks to allow customers to build their own portals began to emerge. The Epicentric Portal and Plumtree Corporate Portal are examples of portal server products that allowed customers to craft portals unique to their businesses. These portals gave the customer two functions: a server to aggregate content in uniform and novel ways, and a framework to build portals and extensions.

Portlets, the application building blocks of a portal, are in many ways very much like Windows applications. A Windows application presents its data in a window. A portlet also presents its data in a window-like display. The title bar of a Windows application contains controls that let users expand (maximize), and shrink (minimize) the application. Portlets have title bars and similar controls. Take a look at the following sample Windows application and a similar portlet, and you'll see the similarities in each application's user interface.


Figure 1. Sample Windows application
Sample Windows application

Figure 2. Sample portlet application
Sample portlet application

Portlets, like their Windows application predecessors, need to work cooperatively within the operating system. An unstable or poorly performing Windows application can impact the overall system: this is also true of a portlet that is not well developed.



Back to top


Introducing WebSphere Portal Version 4.1

Last year IBM introduced WebSphere Portal Server, which supports creating and managing portal-based Web sites. WebSphere Portal empowers you to build first-class business-to-business, business-to-employee, and business-to-customer portals.

IBM announced WebSphere Portal Version 4.1 in April 2002. This latest release of IBM's flagship portal server offering raises the bar in function, performance, and scalability for portal servers. Version 4.1 supports many new functions and capabilities:

  • J2EE compliance
  • Support for Web-services-based portlets
  • A new, more powerful page customizer
  • Page groups
  • New portal look and feel including more selectable themes and skins
  • Portlet API enhancements

This is a just partial list of the new enhancements in WebSphere Portal Version 4.1. For further detailed information on this release see Resources.

The WebSphere Portal Version 4.1 portlet programming API gives you an improved, robust, second-generation framework for developing portlets and portal applications. This framework is based on J2EE Java programming standards and offers a well-crafted set of functions for developing portlets, including:

  • Portlet markup rendering
  • Event handling
  • Single sign-on
  • Portlet metadata storage
  • User profile
  • Client device capabilities

WebSphere Portal is now a J2EE-compliant application. This means, among other things, that portlets now get their Java inheritance from the J2EE Servlet class. This is good news for portlets. Any function or feature available within the Servlet class is now, in general, available to a portlet as well.

If this is your first journey into the world of portlet programming, the following examples show you how to set up and use WebSphere Portal to develop portlets of your own.



Back to top


Portlet programming

Before you can develop a portlet, you need to set up a portlet development and runtime environment. This environment can be set up on one machine, but you may want to use two machines for better performance.

In a dual-machine setup, the first machine will be your portlet development machine where you'll install your Java and Web development tools. These tools will be used to develop the various components of your portlets. It is a good idea to use the same Java Development Kit (JDK) level as the application server. WebSphere Portal Version 4.1 requires WebSphere Application Server Version 4.02, which uses JDK 1.3.1

As you install tools for the development machine you might want to try out the Portal Toolkit included with WebSphere Portal Version 4.1. The Portal Toolkit is a packaged set of tools and samples that plug in to WebSphere Studio Application Developer and give you a complete portlet development environment. With Application Developer and the Portal Toolkit you can develop new portlets in minutes. Several sample portlets are included in the toolkit, as well as a portlet development wizard that speeds the development of portlet application by creating a complete templated portlet including the Java code, JSP pages, and XML deployment descriptors. (In a future article I'll introduce the Portal Toolkit in further detail and include a tutorial on the installation of the toolkit and associated software components.)

The second machine will be the portal runtime machine. On this machine you need to install WebSphere Application Server 4.02 along with the latest set of patches. If you intend to install the production version of WebSphere Portal you'll also need to install IBM DB2 Universal Database Version 7.1 and IBM Secureway Directory Version 3.2. These prerequisite products for the full release of WebSphere Portal are included in the portal package.

If your runtime machine is meant only for testing your portlets, then you can install the developers' version of the portal. Version 4.1 of the portal server requires the installation of WebSphere Application Server, DB2, and the developers' version of the portal.

Once you've installed and tested the software on both machines, you need to add references to the following Java archive (JAR) files, required for portlet development, to your development machine's CLASSPATH:

  • portlet-api.jar
  • wpsportlets.jar
  • j2ee.jar
  • wps.jar
  • jlog-2.2.jar

Note: Other JAR files may be required for your portlet application. For the examples in this article, these JAR files are the only ones required.

The easiest way to use these JAR files on your development machine is to reference the files from the runtime machine across a network to the development machine. Consult the WebSphere Portal documentation (see Resources) for the exact location of these JAR files within the portal install directories.

Now that you have a complete development environment, it's time to do some portlet programming!

The obligatory first portlet: HelloWorld!

The standard way to introduce a new programming method or language is to start with a Hello World application. This is an easy way to get started on portlet programming, and you can use your HelloWorld portlet to validate the setup of the development and runtime machines.

HelloWorld (portlet example 1)

//********************************************************************
//*
//* HelloWorld.java - Example Portlet #1 Shows simple portlet structure
//*
//********************************************************************

package com.ibm.wp.samples.helloworld;

import org.apache.jetspeed.portlet.*;
import org.apache.jetspeed.portlets.*;
import java.io.*;

public class HelloWorld extends PortletAdapter
{
  public void init(PortletConfig portletConfig)
    throws UnavailableException
  {
    super.init( portletConfig );
  }
  public void service( PortletRequest portletRequest, 
                       PortletResponse portletResponse )
     throws PortletException, IOException
  {
    PrintWriter writer = portletResponse.getWriter();

    writer.println("<p>Hello Portal! This is my first Portlet!</p>");
  }
}

HelloWorld, while not high-function, shows the basic structure of a portlet. A simple portlet requires a single method, the service method. A portlet's service method is called when the portal is requesting the portlet to render its data. In this sample the data is this markup:

  <p>Hello Portal! This is my first Portlet!</p>

Portlets, portlet applications, and portlet application descriptors

A portlet produces markup and is rendered in a rectangular area in the portal page. A collection of related portlets make up a portlet application and are packaged together in a Web archive (WAR) file. Portlets packaged together in a single WAR file may share images, stylesheets, JSP components, and other kinds of resources. A WAR file is a specially structured JAR file that includes a special XML descriptor called the Web application deployment descriptor. The filename for the XML descriptor file is web.xml. This file is always stored in the "web-inf" directory of the WAR file.

In addition to the web.xml file in the portlet application's WAR file, a WAR file that contains a portlet application and associated portlets must also contain a portlet application descriptor. The portlet application descriptor filename is portlet.xml, and this file is also always stored in the "web-inf" directory of the WAR file.

 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD 
  Web Application 2.2//EN"
                         "http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
<web-app id="WebApp_504848313">
    <display-name>Hello World Portlet Application - Portlet 
  Sample #1</display-name>
   <servlet id="Servlet_439329280">
    <servlet-name>HelloWorld</servlet-name>
    <servlet-class>com.ibm.wp.samples.helloworld.HelloWorld</servlet-class>
   </servlet>
   <servlet-mapping id="ServletMapping_439329280">
        <servlet-name>HelloWorld</servlet-name>
        <url-pattern>/HelloWorld/*</url-pattern>
    </servlet-mapping>
</web-app>

The Web application descriptor describes the portlets in the portlet application WAR file to the application server as servlets. Each portlet is defined as a servlet, and the portlet application is synonymous with the definition of the Web application.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE portlet-app-def PUBLIC "-//IBM//DTD Portlet Application 1.1//EN"
                                 "portlet_1.1.dtd">
<portlet-app-def>
    <portlet-app uid="504848313">
        <portlet-app-name>Hello World Portlet Application - Portlet 
             Sample #1</portlet-app-name>
        <portlet href="WEB-INF/web.xml#Servlet_439329280" 
                 id="Portlet_439329280">
            <portlet-name>HelloWorld</portlet-name>
            <cache>
                <expires>0</expires>
                <shared>no</shared>
            </cache>
            <allows>
                <minimized/>
            </allows>
            <supports>
                <markup name="html">
                    <view/>
                </markup>
            </supports>
        </portlet>
    </portlet-app>
    <concrete-portlet-app uid="640682430">
        <portlet-app-name>Concrete Hello World Portlet Application -  
          Portlet Sample #1</portlet-app-name>
        <context-param>
            <param-name>Portlet Master</param-name>
            <param-value>yourid@yourdomain.com</param-value>
        </context-param>
        <concrete-portlet href="Portlet_439329280">
            <portlet-name>HelloWorld</portlet-name>
            <default-locale>en</default-locale>
            <language locale="en_US">
                <title>Hello World - Sample Portlet #1</title>
                <title-short>Hello-Worldd</title-short>
                <description>Hello World - Sample Portlet #1</description>
                <keywords>portlet hello world</keywords>
            </language>
        </concrete-portlet>
    </concrete-portlet-app>
</portlet-app-def>

This document describes the portlet application, each portlet in the application, and each portlet's title and capabilities. This is a fairly vanilla version of a portlet application descriptor with the required tags specified. I won't discuss the meaning of each tag here -- you can consult the WebSphere Portal Guide to Portlet Programming (see Resources) for a complete description of each tag and related tag values for both the Web application descriptor and the portlet application descriptor documents.

Building and packaging the portlet application

Now that you have the source code for the portlet and the XML descriptor documents, you can build and package the application components into a WAR file. Lay out the source code, and the Web and portlet descriptor documents, in a directory structure that will make the building of the portlet and WAR file an easy process, such as this structure:

d:/HelloWorld
d:/HelloWorld/WEB-INF/web.xml
d:/HelloWorld/WEB-INF/portlet.xml
d:/HelloWorld/WEB-INF/classes/com/ibm/wp/samples/helloworld/HelloWorld.java

With this directory structure you can create a batch file that compiles the portlet and builds the associated WAR file. This batch file assumes the JDK CLASSPATH has been set up to point at the portal JAR files required to compile this portlet. For this example, I named this batch file BuildEx1.bat and placed it in the HelloWorld directory. Be sure to modify line 2 of the batch file to point at the base directory where you stored your portlet files, as shown below.

Note that some lines in the example below (and in the following examples) have been split so that they'll display here.

  echo off
  set bd=D:/HelloWorld
  javac -d 
    %bd%/WEB-INF/classes 
    %bd%/WEB-INF/classes/com/ibm/wp/samples/helloworld/HelloWorld.java
  jar cvf0 HelloWorld.war WEB-INF

Running this batch file compiles the portlet and builds the WAR file. Now that you have the portlet application successfully built it is time to run the portlet application on your runtime portal machine.

Installing the portlet application

You need to log into the portal in order to install your portlet and test it. You display the portal by entering the URL of your portal in your browser's address field. The portal will display showing the generic home page. You'll need to click the login icon (Edit icon ) to log into the portal, and then log in as an administrator. Here's a sample rendering of the standard portal home page that's included with WebSphere Portal:


Figure 3. Sample portal homepage
Sample portal home page

Once you're logged in to the portal, select the Portal Administration page group to display the Portlet Administration pages. For this example, you'll install the HelloWorld portlet application using the Install Portlets page. You need to copy your portlet WAR file to a directory accessible from the portal server machine. Click Install and install the portlet. You' see a series of screens and confirmations as the installation of the portlet is completed.

Adding the portlet to a portal page

Once the portlet is installed, you'll need to use the portal page customizer to add the portlet to a portal page. The customizer is an application of the portal that lets you add and arrange portlets on a portal page. To enter the page customizer you'll need to change to the Work with Pages page group and select Edit Layout and Content tab. The page displayed will show the page customizer.

To keep it simple, we'll add the portlet to the Welcome page. This page is grouped in the Home page group of pages. At the top of the customerizer page you select the page group and page to be modified. Make sure the Home page group and welcome page are selected.

Click on the get portlets link to get a list of portlets to add to the page. Select HelloWorld the list of all portlets and press Go. HelloWorld will be returned in the customizer's list of portlets. Select the portlet and then click the Add portlet icon in the top left column of the customizer. This will add the portlet to the left column of the page.

When you have completed page customization you'll need to activate the page.


Figure 4. The WebSphere Portal page customizer
The WebSphere Portal page customizer

When you display the page, you'll see the portlet and the familiar "Hello Portal World" phrase.


Figure 5. HelloWorld portlet screen shot
HelloWorld portlet on Welcome page

HelloAgain (portlet example 2)

HelloWorld is a great introduction to portlet programming, but I need to introduce a couple more portlet concepts to round out the portlet development picture.

Portlets and JavaServer Pages

When the portal aggregator calls a portlet to render its data, the portlet responds by emitting a stream of markup and placing this markup in a print writer available from the portlet response object. This is simple enough to implement from a portlet's perspective, but it becomes more difficult as the complexity of the portlet's user interface increases. What is needed is a method for a portlet to delegate rendering to another entity, and in WebSphere Portal that entity is a JSP component.

The WebSphere Portal portlet API provides first-class support for the use of JSP components to render portlet markup. In fact, portlets and servlets use the runtime linkage to pass data to a JSP component.

A portlet creates a Java bean containing data to be passed to a particular JSP component. The portlet places a reference to this bean in the portlet request. The portlet then calls the portlet context method include() to invoke the component. The JSP component uses the useBean tag to establish a reference to the bean, extracts data from the bean as needed, and combines the bean data with appropriate markup. The component then emits this markup to the output stream directly. The portlet aggregator combines the result with other markup from other portlets into a portal page.

Your portlet may also store the reference to your Java bean in the portlet session object instead of the portlet request object. These beans will have a longer lifecycle; they will typically be available until explicitly deleted, or when the portal session is terminated. Beans placed in a session have higher overhead in application server configurations with multiple machines arranged in a cluster. The beans must be serialized and copied to each machine in the cluster to maintain the portal session state across all cluster machines.

The HelloAgain portlet sample demonstrates using a bean and a JSP component to render a personalized hello message.


//******************************************************************
//*
//* HelloAgain - A sample portlet that uses a JSP for rendering
//*
//******************************************************************

package com.ibm.wp.samples.helloagain;

import org.apache.jetspeed.portlet.*;
import org.apache.jetspeed.portlets.*;
import java.io.*;

public class HelloAgain extends PortletAdapter
{
  public void init(PortletConfig portletConfig) 
    throws UnavailableException
  {
    super.init( portletConfig );
  }
  protected static final String jsp = "/WEB-INF/html/HelloAgain.jsp";

  public void service( PortletRequest request, PortletResponse response)
     throws PortletException, IOException
  {
    // Make a bean
    HelloAgainBean hab = new HelloAgainBean();       
                                                     
    // Get the portlet context
    PortletContext context = getPortletConfig().getContext(); 
     
    // Get portal user object 
    User user = request.getUser();                       
                                                         
    // get user's full name   
    String userName = user.getFullName();             
                                                      
    // A valid name returned? 
    if( userName == null )                                  
    {                                                       
      userName = "Unknown WebSphere Portal User";           
    }                                                       
                                                            
    // Save name in bean
    hab.setUserName( userName );                        
                                                        
    // Save bean in request
    request.setAttribute( "HelloAgainBean", hab );        
                                                          
    // Invoke the JSP to render
    context.include(jsp, request, response);    
  }
}

In the above portlet code the first order of business is to create the bean that will be passed to the JSP. Once the bean is created the user's name is retrieved from the portal user object and copied to the bean. A reference to the bean is placed in the portlet request and the portlet context include method is called to invoke the JSP to render the markup for the portlet.

The associated Java bean has a single Java String attribute:

//********************************************************************
//*
//* HelloAgainBean.java - The bean used to pass data to the display
//*                       JSP in the HelloAgain portlet application
//*
//********************************************************************

package com.ibm.wp.samples.helloagain;

import java.util.*;

public class HelloAgainBean 
{
  private String userName = "";

  public void setUserName(String s)
  {
    userName = s;
  }

  public String getUserName()
  {
    return( userName );
  }
}

The JSP component establishes a reference to the bean, and embeds the value of the user's name in the output markup:

<!---------------------------------------------------------------------->
<!--                                                                  -->
<!-- HelloAgain,jsp - A sample JSP used to render data from a portlet -->
<!--                                                                  -->
<!---------------------------------------------------------------------->

<%@ page contentType="text/html" errorPage="" %>

<jsp:useBean id="HelloAgainBean" 
   class="com.ibm.wp.samples.helloagain.HelloAgainBean" scope="request" />

<h1>Hello Again <%=HelloAgainBean.getUserName()%></h1>
<br><h2>This is markup rendered from a JSP</h2>

A batch file is included with the example to build the sample. The build process is the same process as the HelloWorld Portlet. Once you have successfully built the portlet, install it on your portal and run it. You will have output similar to this:


Figure 6. HelloAgain Portlet Screen Shot
HelloAgain Portlet Screen Shot

World Time (portlet example 3)

A final sample, WorldTime, ties together all of the concepts presented. This example displays the time for your city. The portlet offers a customization page where you can enter your hour offset from Greenwich Mean Time and your home city name. If the portlet is maximized it will display the time in a city from each of the 24 time zones. The portlet also includes a help page that explains how to customize the portlet.

This sample introduces four new portlet concepts. These new concepts, plus the use of JSP components and the portlet structure shown in the first two samples, give you the concepts you need to construct powerful portlets for your application.

Portlet modes

When a portlet is called to render content it is passed a mode indicator in the portlet request object. The following portlet modes are possible:

  • View mode

    This is the default mode for a portlet. When a portlet is in this mode it is rendering data in its default display mode.
  • Edit mode

    This mode is made active when the user presses the edit icon Edit icon displayed in the portlet's title bar. When a portlet is in edit mode it displays a dialog that let the user customize the settings of the portlet.
  • Configure mode

    A portal administrator, during the manage portlets process, can invoke configuration of the portlet by selecting the Modify Parameters function. When in this mode the portlet displays a dialog that lets the administrator configure the portlet for the portal. These settings are global in nature, and typically settings for the portlet (the target back-end server URL, for example), and not settings for a particular user.
  • Help mode

    This mode is made active when the user presses the help icon Help icon displayed in the portlet's title bar. When a portlet is in help mode it should display help information for the user. This help information should explain how the user customizes the portlet settings and how the portlet display is to be interpreted.

Portlets must check this mode value and render the appropriate markup to produce the desired user interface. Typically portlets will do this by creating a JSP component and Java bean for each mode, and invoking this bean and JSP when the associated portlet mode is requested for display.

Portlet window states

A portlet in some cases will also want to check its window state to determine if the user has maximized the portlet. The use maximizes a portlet by clicking the maximize icon Maximize icon displayed in the portlet's title bar. When a portlet is maximized all other portlets on the page are hidden. This gives the portlet the maximum amount of the screen area for rendering its content.

Portlets that can be maximized typically have a special JSP component for the maximized view. This JSP component takes advantage of the extra screen area to display more content, or content of higher detail to the user.

So far I've only covered the display of content to the user. Typically portlets offer more than just content display to their users, they also offer user interface metaphors that allow the user to interact with the content in various ways. Most portlets use forms to present their user interface. Forms can consist of entry data fields and various buttons (including radio buttons, check boxes, and push buttons).

The following Java snippet is the core class for the WorldTime portlet. I'll further describe the processing taking place in the service and actionPerformed methods.

//******************************************************************
//*
//* WorldTime.java - A sample portlet that uses a JSP for rendering
//*
//******************************************************************

package com.ibm.wp.samples.worldtime;

import org.apache.jetspeed.portlet.DefaultPortletAction;
import org.apache.jetspeed.portlet.*;
import org.apache.jetspeed.portlets.*;
import org.apache.jetspeed.portlet.event.*;
import java.io.*;

public class WorldTime extends AbstractPortlet implements ActionListener
{
  public void init(PortletConfig portletConfig) throws 
      UnavailableException
  {
    super.init( portletConfig );
  }

  public final static String homeCity              
     = "HomeCity";         // Field and Persistence
  public final static String timeOffset            
     = "TimeOffset";       // Names
  
  public final static String portletSaveAction     
     = "save";             // Save action
  public final static String unspecifiedCityValue  
     = "Unspecified City"; // Unspecified city

  protected static final String jspNormalView  
     = "/WEB-INF/html/WorldTimeNormalView.jsp";
  protected static final String jspMaxView     
     = "/WEB-INF/html/WorldTimeMaxView.jsp";
  protected static final String jspEditView    
     = "/WEB-INF/html/WorldTimeEditView.jsp";
  protected static final String jspHelpView    
     = "/WEB-INF/html/WorldTimeHelpView.jsp";

  public void service( PortletRequest request, PortletResponse response)
     throws PortletException, IOException
  {                                                        
    String jsp = null;                                     
                                                           
    // View Mode?
    if( request.getMode() == Portlet.Mode.VIEW )           
    {                                                      
      // Make a bean
      WorldTimeBean wtb = new WorldTimeBean();           
                                                          
      String homeCity   = null;                           
      String timeOffset = null;                           
                                                          
      // Get Data Store Reference
      PortletData pData = request.getData();             
                                                         
      homeCity   = (String) pData.getAttribute(WorldTime.homeCity);
      timeOffset = (String) pData.getAttribute(WorldTime.timeOffset);
                                                         
      wtb.setHomeCity( ((homeCity != null && homeCity.length() > 0) 
                       ? homeCity : unspecifiedCityValue) );
      wtb.setTimeOffset( ((timeOffset != null && timeOffset.length() > 0)
                       ? timeOffset : "0") );
      
      // Save bean in request
      request.setAttribute( "WorldTimeBean", wtb );       
                                                          
      // Is window maximized?
      if( (request.getWindow()).getWindowState() == 
             PortletWindow.State.MAXIMIZED )  
      {  
        // Maximized View JSP
        jsp = jspMaxView;      
      }                        
      else                     
      {                        
        // Normal view JSP
        jsp = jspNormalView;                 
      }                                      
    }                                        
    // Edit mode?
    else if( request.getMode() == Portlet.Mode.EDIT )      
    {                                                      
      // Make a bean
      WorldTimeBean wtb = new WorldTimeBean();            
                                                          
      String homeCity   = null;                           
      String timeOffset = null;                           
                                                          
      // Get Data Store Reference
      PortletData pData = request.getData();           
                                                       
      homeCity   = (String) pData.getAttribute(WorldTime.homeCity);
      timeOffset = (String) pData.getAttribute(WorldTime.timeOffset);
                                                       
      wtb.setHomeCity( ((homeCity != null && homeCity.length() > 0) 
                       ? homeCity : unspecifiedCityValue) );

      wtb.setTimeOffset( ((timeOffset != null && timeOffset.length() > 0)
                         ? timeOffset : "0") );
      // Create a save URI encoded with a save action 
      PortletURI saveURI = response.createReturnURI();
      DefaultPortletAction action = new 
            DefaultPortletAction( WorldTime.portletSaveAction );
      saveURI.addAction(action);                      

      // Save the URI in the bean
      wtb.setSaveURI(saveURI.toString());              
                                                       
      // Create a cancel URI and save in the bean
      PortletURI cancelURI = response.createReturnURI(); 
      wtb.setCancelURI(cancelURI.toString());            
                                                         
      // Save bean in request
      request.setAttribute( "WorldTimeBean", wtb ); 
                                                    
      // Point at edit JSP 
      jsp = jspEditView;   
    }                                                    
    else if( request.getMode() == Portlet.Mode.HELP )    
    {                                                    
      jsp = jspHelpView;                                 
    }                                                    

    // delegate rendering to JSP
    (getPortletConfig().getContext()).include( jsp, request, response );
  }

  // Portlet's action handler 
  public void actionPerformed(ActionEvent event)
  {
    PortletLog pLog = getPortletLog();
    System.out.println( "WorldTimeActionListener: Event" );

    System.out.println( "WorldTimeActionListener: Event 
       type:("+event.getAction()+")" );

    DefaultPortletAction action = (DefaultPortletAction) event.getAction();

    if( (action != null) && 
       (action.getName().equalsIgnoreCase(WorldTime.portletSaveAction)) )
    {
      PortletRequest request = event.getRequest();

      System.out.println( "WorldTimeActionListener: Action Event" );

      try   // Attempt retrieval and save of portlet form data
      {
        PortletData pData = request.getData();

        String homeCityValue   = request.getParameter( WorldTime.homeCity );
        String timeOffsetValue = request.getParameter( WorldTime.timeOffset );

        if( (homeCityValue != null) && (homeCityValue.length() > 0) )
        {
          homeCityValue = homeCityValue.trim();
        }
        else
        {
          homeCityValue = WorldTime.unspecifiedCityValue;
        }

        if( (timeOffsetValue != null) && (timeOffsetValue.length() > 0) )
        {
          timeOffsetValue = timeOffsetValue.trim();  
        }                                            
        else                                         
        {                                            
          timeOffsetValue = "0";                     
        }

        pData.setAttribute( WorldTime.homeCity,   homeCityValue );   
        pData.setAttribute( WorldTime.timeOffset, timeOffsetValue ); 

        pData.store();
      }
      catch( AccessDeniedException ade )
      {
        pLog.error( "WorldTime: Java AccessDeniedException Exception ( " + ade
                    + " ) thrown when saving instance data to the portlet store");
	  }
      catch( java.io.IOException e )  
      {
        pLog.error( "WorldTime: Java I/O Exception ( "+ e 
                    + " ) thrown when saving instance data to the portlet store");
      }
    }
  }
}

This portlet's service method has been enhanced to test for the portlet mode and window state. For the view and edit portlet modes a bean is passed to the appropriate JSP component. In normal view mode the user's time and home city are displayed. In maximized view mode a list of cities and their local time is displayed. In the portlet's customize mode (edit mode in the code), users enter their home city and time offset from Greenwich Mean Time.

In edit mode two URIs are created. These URIs are passed to the JSP component and assigned to the save and cancel buttons in the display form. These URIs are encoded so that when one a user presses one of these buttons, the portal can decode the URI, determine which portlet generated the button event, and call the portlet event handler with the event.

When the JSP component is determined (and the bean created and loaded with data, for portlet display modes that require a bean), control is passed to the component to render the portlet's user interface.

The following screens show the various portlet display modes and window sizes.


Figure 7. WorldTime normal view
WorldTime normal view

Figure 8. WorldTime maximized view
WorldTime maximized view

Figure 9. WorldTime edit view
WorldTime edit view

Figure 10. WorldTime help view
WorldTime help view

Notice that all portlet modes display within the portal page. The portlet help mode is an exception to this rule. When you invoke portlet help, the help is displayed in a separate browser window.

Portlet event handling

When the user interacts with one of these user interface controls, and the control generates an http submit event to the application server, the http flow is reflected to the portal server. The portal server determines which portlet generated the event and delivers the event to the portlet as an action event.

It is the portlet's responsibility to include an action handler that is called when the portlet needs to process an event. The WorldTime portlet class includes an actionPerformed method to process action events. In previous versions of the portlet API the action handler was stored in a separate Java class. With WebSphere Portal Version 4.1 and portlet API 1.1 the action handler (and any other included portlet event handlers) are implemented directly within the portlet class.

The action handler tests for a specific event. This handler is implemented to process a save action. A save action is generated when the user clicks Save on the customization page of the portlet.

Portlet instance data

The primary function of the above event handler is to retrieve the data the user just entered into the portlet customization page. You need to retrieve the data from each field on the form and save it in the portlet's data store so that you can use it for subsequent display. Fields defined in the JSP component by name are accessible to the event handler as parameters of the portlet request and are retrieved using the getParameter() method. The data values are subsequently stored by name in the portlets data store for the user, by calling setAttribute for each data item on the portlet data object. Calling the store method on the portlet data object commits the data values to persistent storage.

The following is the source for the JSP components used in the WorldTime portlet.

The following class is the implementation of the bean that is passed to the JSP in the view and edit modes of the portlet. The bean handles calculation and formatting of the time for any time zone. The bean also contains the home city, home city time offset, and the event handler URLs used to represent the Save and Cancel buttons on the customization page.

//********************************************************************
//*
//* WorldTimeBean.java - A sample Java bean that passes data from a
//*                      portlet to a JSP for markup rendering 
//*
//********************************************************************

package com.ibm.wp.samples.worldtime;                      
                                                            
import java.util.*;                                         
import java.text.*;                                         
                                                            
public class WorldTimeBean                                  
{                                                           
  private String homeCity = WorldTime.unspecifiedCityValue;
  private int    timeOffset = 0;                             
  private String saveURI    = ""; 
  private String cancelURI  = ""; 
  
  public void setSaveURI(String s) 
  {                                                          
    saveURI = s;         
  }                      
                         
  public String getSaveURI()      
  {                                                          
    return( saveURI );                                      
  }

  public void setCancelURI(String s)                         
  {                          
    cancelURI = s;           
  }    
                             
  public String getCancelURI()                               
  {                                                          
    return( cancelURI );                                  
  }

  public void setHomeCity(String s)                          
  {                                           
    homeCity = s;                             
  }                                           
                                              
  public String getHomeCity()         
  {                                   
    return( homeCity );               
  }                                   
                                      
  public String getHomeTime()                                
  {                                            
    return( getLocalTime(timeOffset) );        
  }                                            
                                               
  public String getLocalTime( int localTimeOffset )             
  {                                                             
    GregorianCalendar now = new GregorianCalendar();            
                                                                
    int realTimeOffset = (-timeOffset) + localTimeOffset;      
    now.add( Calendar.HOUR_OF_DAY, realTimeOffset );            
                                                                
    Date timeNow = now.getTime();                               
                                                                
    String localTime = 
     DateFormat.getTimeInstance(DateFormat.SHORT).format(timeNow); 
                                                                
    return( localTime );                                        
  }                                                             
                                                                
  public void setTimeOffset( String inTimeOffset )              
  {                                                             
    try                                                         
    {                                                           
      timeOffset = (int) Integer.parseInt( inTimeOffset );      
    }                                                           
    catch( Exception e )                                        
    {                                                           
      timeOffset = 0; 
    }                                                           
  }                                                             
                                                                
  public String getTimeOffset() 
  {                                                              
    return( Integer.toString(timeOffset) );                     
  }                                                             
}                                                                



Back to top


Summary

This article only scratches the surface of the potential in portlet programming. I've shown how to get started in portlet programming, including setting up a portlet development and runtime environment. I also used the HelloWorld portlet to show a very basic portlet and give you a feel for the structure of a portlet as well as how to build, package and deploy a portlet on a portal server. I introduced the concepts of portlet modes, portlet window states, using JSP components and Java beans to render data in a portlet, and portlet event handling.

These concepts give you the basics of portlet programming. If you combine the concepts with your existing application's data connectors, you will be able to build powerful portlets and bring your product into the portal era of the Internet.

In future articles I'll discuss portlet programming topics that include: portlet messaging, portlet performance, as well as tutorials and introductions for the Portal Toolkit.




Back to top


Download

NameSizeDownload method
i-portalv4.zipHTTP
Information about download methods


Resources



About the author

David Lection

David B. Lection is a Senior Programmer and tools architect for WebSphere Portal and related products. You can contact David at lection@us.ibm.com.




Rate this page


Please take a moment to complete this form to help us better serve you.



YesNoDon't know
 


 


12345
Not
useful
Extremely
useful
 


Back to top