With the emergence of an increasing number of enterprise portals, a variety of APIs for portal components, which are called portlets, has been created by different vendors. Having multiple, incompatible interfaces creates problems for application providers, portal customers, and portal server vendors. The Java Portlet Specification, JSR 168, which is being defined by the Java Community Process (JCP), provides a standard for interoperability between portlets and portals. In time, it will help to overcome these problems.
The goal of this document is to compare the JSR 168 and the WebSphere Portal V5.0 Portlet API and to show some examples.
Important: The IBM Portlet API will still be supported in the next WebSphere Portal releases to give customers and partners a convenient migration window.
The Java Standardization Request 168 (JSR 168) defines a portlet specification, including a contract between the portlet container and the portlet. JSR 168 is defined by the Java Community Process (JCP). The JSR 168 was co-led by IBM and Sun and had a large Expert Group that helped to create the final version which is now available. This Expert Group consisted of Apache Software Foundation, Art Technology Group Inc.(ATG), BEA, Boeing, Borland, Citrix Systems, Fujitsu, Hitachi, IBM, Novell, Oracle, SAP, SAS Institute, Sun, Sybase, Tibco, Vignette. More details about this JSR can be found at http://jcp.org/en/jsr/detail?id=168.
Important: The information provided in this analysis is provided without warranty of any kind. The analysis is intended to be an informational tool only for the reader.
This section provides basic JSR 168 definitions so that you can understand how JSR 168 fits into the overall portal architecture.
A portal is a Web application which typically provides personalization, single sign-on, content aggregation from different sources, and hosts the presentation layer of different backend systems.
The main task of a portal is to aggregate different applications into one page with a common look and feel for the portal user. A portal may also have sophisticated personalization features which provide customized content to users. Portal pages may have different sets of portlets to create content for different users.
Figure 1 depicts the basic architecture of portals, such as a portal that is served by WebSphere Portal. A client request is processed by the portal Web application, which retrieves the portlets on the current page for the current user. The portal Web application then calls the portlet container for each portlet to retrieve its content through the Container Invoker API. The portlet container calls the portlets through the Portlet API. The Container Provider Service Provider Interface (SPI) enables the portlet container to retrieve information from the portal.
Figure 1. Basic portal architecture
The portlet container runs the portlets, provides them with the required runtime environment, and manages their lifecycle. It provides persistent storage for portlet preferences which enables the portlet to produce customized output for different users.
Figure 2 depicts the basic portal page components. The portal page represents a complete markup document and aggregates several portlet windows; that is, it combines different application user interfaces into one unified presentation. The portal page lets the user authenticate to the portal through the login dialog to access a personalized portal view. Most portal pages include some navigation mechanism to enable the user to navigate to other portal pages.
Figure 2: Portal page
A portlet window consists of:
- Title bar, with the title of the portlet
- Decorations, including buttons to change the window state of the portlet (such as maximize or minimize the portlet) and buttons to change the mode of a portlet (such as show help or edit the predefined portlet settings)
- Content produced by the portlet (also called a markup fragment).
Figure 3 shows such a portlet window on different browsers. As you can see, the markup fragment produced by the portlet is not restricted to HTML, but can be any markup.
Figure 3: Portlet displayed in an HTML browser (top) and in a WML browser (bottom)
The basic portlet lifecycle is to:
- Initialize, using the init class to initialize the portlet and put it into service.
- Handle requests, processing different kinds of actions and rendering content.
- Complete, using the destroy class to take the portlet out of
The portlet receives requests based on the user interaction with the portlet or portal page. The request processing is divided into two phases:
- Action processing
If a user clicks on a link on the portlet, an action is triggered.The action processing must be finished before any rendering of the portlets on the page is started. In the action phase the portlet can change the state of the portlet.
- Rendering content
In the render phase, the portlet produces its markup to be sent back to the client. Rendering should not change any state. It allows a page re-fresh without modifying the portlet state. Rendering of all portlets on a page can be performed in parallel.
Figure 4 depicts the request flow from the client to the portlets, and shows the action and render phases in more detail. In this example, portlet A has received an action. After the action is processed, the render methods of all portlets on the page (A, B, C) are called.
Figure 4: Request flow from client to portlets
Portlets perform different tasks and create content according to their current function. A portlet mode indicates the function a portlet is performing, at a point in time. A portlet mode specifies the kind of task the portlet should perform and what content it should generate. When invoking a portlet, the portlet container provides the mode for the current request to the portlet. Portlets can programmatically change their portlet mode while processing an action request.
JSR 168 defines three categories of portlet modes.
- Modes which are required to support (same semantic as above)
- Edit
- Display one or more views that let the user personalize portlet settings.Help
- Help
- Display help views.
- View
- Display the portlet output.
- Optional custom modes
- About
- Display the portlets purpose, origin, version, and other information.
- Config
- Display one or more configuration views that let administrators configure portlet settings which are valid for all users.
- Edit_defaults
- Set the default values for the modifiable preferences that are typically changed in the EDIT screen.
- Preview
- Render output without the need to have back-end connections or user specific data available.
- Display a view which is suitable for printing.
- Portal vendor specific modes
These modes are only available in a specific vendor portal.
A window state is an indicator of the amount of portal page space assigned to the content generated by a portlet. The portlet container provides the current window state to the portlet, and the portlet uses the window state to decide how much information it should render. However, portlets can also programmatically change their window state while processing an action request.
JSR 168 defines the following window states:
- Normal
- The portlet shares the space with other portlets and should take this into account when producing its output.
- Maximized
- A window has more real estate to render its output than in normal window state.
- Minimized
- The portlet should only render minimal or no output.
In addition to these window states, JSR 168 allows the portal to define custom window states.
JSR 168 defines different mechanisms for the portlet to access transient and persistent data.
The portlet can set and get transient data in the following scopes:
| Request | The request has attached data, such as request parameters and attributes, similar to the servlet request. The request can contain properties to allow extension, and client header fields being transported from the portal to the portlet and vice versa. |
| Session | The portlet can store data in the session with either global scope, to let other components of this Web application access the data, or portlet scope, which is private to the portlet. |
| Context | The portlet can store data in the Web application context, similar to servlets |
The portlet can access persistent data with these scopes:
| Per portlet | The portlet can store configuration and personalization data in the portlet preferences to enable the portlet to create personalized output. The portlet can define which data the user is allowed to change in the edit mode (for example, stock quotes), and which data are configuration settings can only be changed by an administrator in config mode (for example, the stock quote server). |
| Per user | User profile information can be read by the portlet to tailor its output towards the user (for example, show the weather of the city where the user lives). |
All resources, portlets, and deployment descriptors are packaged together into one Web application archive (WAR) file. There are two deployment descriptors:
- All Web application resources that are not portlets must be specified in the web.xml deployment descriptor.
- All portlets and portlet related settings must be specified in the portlet.xmldeployment descriptor.
Comparing JSR 168 and the IBM Portlet API
This section gives a high level comparison between the new JSR 168 Portlet API and the IBM Portlet API. First, it covers the concepts that are similar; then, it explains some of the differences between the two.
The following concepts are very similar in JSR 168 and the IBM Portlet API.
| Feature | Similarities | Differences |
| Portlet modes | Both support the basic portlet modes: Edit, Help, and View. | The config mode is optional in the JSR 168. The other optional JSR 168 modes (About, Edit_defaults, Preview, Print) are not supported by the IBM Portlet API. |
| Window states | These window states are supported: Maximized, Normal, and Minimized. | The Solo window state is only supported by the IBM Portlet API. |
| Portlet lifecycle | The lifecycle life cycle is the same: init, process requests, destroy. | none |
| Request processing | Request processing is divided into an action phase for processing user actions and a render phase for producing the markup. | none |
| URL encoding | Both support creating URLs pointing to the portlet or to a resource. | none |
| Include servlets/JSPs | Servlets and JSPs can be included in the portlet. | none |
| Portlet session | Portlets can store transient information that should span requests in a session. | none |
| Portlet application packaging | Both package portlet applications as WAR files with an additional deployment descriptor called
portlet.xml. | The portlet.xml format differs. |
| Expiration-based caching | The portlet can support expiration based caching. | The APIs use different mechanisms to implement this functionality. The IBM Portlet API uses a polling mechanism where the portal queries the portlet for how long the markup will be valid, whereas in the JSR 168 the portlet can attach an expiration time to each created markup. Sharing the cache entry across users is only possible in the IBM Portlet API. |
The JSR 168 and the IBM Portlet API differ in the following ways.
| Feature | IBM Portlet API | JSR 168 |
| Portlet application entities | Lets you define an abstract portlet application and different instance of this portlet application as concrete portlet applications via the deployment descriptor. This allows reusing settings of the abstract portlet application and only overwriting the parts that are unique for each concrete portlet application. | The deployment descriptor follows the web.xml deployment descriptor and defines one portlet application and the portlet definitions for this application. |
| Portlet entity | There is one portlet object instance per portlet configuration in the Web deployment descriptor. There may be many PortletSettings objects parameterizing the same portlet object according to the Flyweight pattern, provided on a per-request basis. Changes in the PortletSettings apply to all portlet instances of this concrete portlet. The user can also have personal views of concrete portlets that are rendered using the PortletData for customization of the output. |
PortletSettings and PortletData are merged into one object called
PortletPreferences. |
| Request/Response objects | The request/response object that the portlet receives in the render call is the same as the one received in the action call. | In the JSR 168 these are two different objects. |
These items are only available in the JSR 168.
| Feature | Description |
| Render parameters | Render parameters allow the portlet to store its navigational state. Render parameters stay the same for subsequent render requests and only change when the portlet receives a new action. This enables bookmarkability and solves the browser back button problem. |
| Global HttpSession scope | Portlets can store data not only with the visibility of the portlet, but also with the visibility of the whole Web application. |
| Redirect | Portlets can redirect to other Web resources in the action phase. |
Exclusive to the IBM Portlet API
The following concepts are only available in the IBM Portlet API.
| Feature | Description |
| Eventing | Events can be sent between portlets. |
| Additional lifecycle listeners | Lifecycle listeners besides action and render, (such as begin page) are not available in the first version of the JSR 168. |
| Portlet menus | Lets the portlet contribute content to a menu bar to facilitate navigation through portal pages. |
| Invalidation based caching | Lets the portlet explicitly invalidate cached content. |
This section shows a HelloWorld portlet implemented in each of the two APIs. The key differences are highlighted in the second portlet.
The two example portlets implement the same functions:
- Use JSPs for rendering the output
- Customize the portlet output, based on user-specific data
- Handle actions to allow the user to change these
This example shows a hello world portlet which uses personalization. It addresses the user
by name, and it allows the user into edit mode to set a new user name. In the action method (called actionPerformed in the IBM Portlet API and processAction in JSR 168),
the portlet persistently stores the new user name which the user enters.
Listing 1. IBM Portlet API example portlet
First look at the portlet implemented using the IBM Portlet API.
public class HelloWorld extends AbstractPortlet implements ActionListener
{
¦
public void doView (PortletRequest request, PortletResponse response)
throws PortletException, IOException
{
//Get the user's name to display from persistent storage
PortletData portletData = request.getData();
String stringToDisplay=(String)portletData.getAttribute("userName");
if (stringToDisplay == null) {
stringToDisplay = defaultString; // set default string
}
// Add the display string to the portlet request to make it
// accessible by the view JSP
request.setAttribute("userName", stringToDisplay);
// Get a context for the current session for invoking the JSP
PortletContext context = getPortletConfig().getContext();
context.include(viewJSP, request, response);
}
public void doEdit(PortletRequest portletRequest,
PortletResponse portletResponse )
throws PortletException, IOException
{
// Create the cancel return URI for the edit page
PortletURI cancelURI = portletResponse.createReturnURI();
// Preserve the Cancel URI in the request to make it
// accessible by the edit JSP
portletRequest.setAttribute("cancelURI", cancelURI.toString());
// Create the save URI for the edit page
PortletURI saveURI = portletResponse.createReturnURI();
// For the "Save" button the return URI must include the "Save" action
// so the Action Listener for this portlet will be invoked
SimpleActionHelper.
addSimplePortletAction(getPortletConfig().getContext(), saveURI, "save");
// Preserve the Save URI in the request to make it accessible by
// the edit JSP
portletRequest.setAttribute("saveURI", saveURI.toString());
//Get the user's name to display from persistent storage
String stringToDisplay = (String)
portletRequest.getData().getAttribute("userName");
if (stringToDisplay == null) {
stringToDisplay = defaultString; // none found, set default string
}
// Add the display string to the request to make it accessible by the
// edit JSP as an inital value of the input field on the edit form
portletRequest.setAttribute("userName", stringToDisplay);
// Get a context for the current session for invoking the JSP
PortletContext context = getPortletConfig().getContext();
context.include(editJSP, portletRequest, portletResponse);
}
public void actionPerformed(ActionEvent event) {
String action =
SimpleActionHelper.getActionString(getPortletConfig().getContext(),
event);
HelloWorld helloPortlet = (HelloWorld)event.getPortlet();
PortletLog log = helloPortlet.getPortletLog();
// If this is a save action, then see if the user specified a name
if ( action!=null ) {
if ( action.equals("save") ) {
PortletRequest request = event.getRequest();
PortletData portData = request.getData();
String userName = request.getParameter("userName");
try {
// Save the name specified by the user
if ( userName != null ) {
portData.setAttribute("userName", userName);
portData.store();
}
} catch ( AccessDeniedException ade ) {
} catch ( IOException ioe ) {
log.error( "
Couldn't write the user date to
persistence because an I/O Error occurred.
" );
}
}
}
}
}
|
Listing 2. JSR example portlet
Now, look at the same portlet that has been modified to use JSR 168. The changed text is marked in bold.
public class HelloWorld extends GenericPortlet
{
¦
public void doView (RenderRequest request, RenderResponse response)
throws PortletException, IOException
{
//Get the user's name to display from persistent storage
PortletPreferences portletData = request.getPreferences();
String stringToDisplay = (String) portletData.getValue("userName",defaultString);
// Add the display string to the portlet request to make it
// accessible by the view JSP
request.setAttribute("userName", stringToDisplay);
// Get a context for the current session for invoking the JSP
PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(viewJSP);
rd.include(request, response);
}
public void doEdit(RenderRequest portletRequest,
RenderResponse portletResponse )
throws PortletException, IOException
{
// Create the cancel URI for the edit page
PortletURL cancelURI = portletResponse.createActionURL();
// Preserve the Cancel URI in the request to make it
// accessible by the edit JSP
portletRequest.setAttribute("cancelURI", cancelURI.toString());
// Create the save URI for the edit page
PortletURL saveURI = portletResponse.createActionURL();
// For the "Save" button the return URI must include the "Save" action
// so the Action Listener for this portlet will be invoked
saveURI.setParameter("action", "save");
// Preserve the Save URI in the request to make it accessible by
// the edit JSP
portletRequest.setAttribute("saveURI", saveURI.toString());
//Get the user's name to display from persistent storage
String stringToDisplay = portletRequest.getPreferences().getValue("userName",
defaultString);
// Add the display string to the request to make it accessible by the edit JSP
// as an inital value of the input field on the edit form
portletRequest.setAttribute("userName", stringToDisplay);
// Get a context for the current session for invoking the JSP
PortletRequestDispatcher rd = getPortletContext().getRequestDispatcher(editJSP);
rd.include(portletRequest, portletResponse);
}
public void processAction(ActionRequest request, ActionResponse response)
throws PortletException, IOException
{
String action = request.getParameter("action");
// If this is a save action, then see if the user specified a name
if ( action!=null ) {
if ( action.equals("save") ) {
PortletPreferences portData = request.getPreferences();
String userName = request.getParameter("userName");
// Save the name specified by the user
if ( userName != null ) {
portData.setAttribute("userName", userName);
portData.store();
}
} |
The major differences to the IBM Portlet API version of the portlet are:
- Request dispatcher usage
The request dispatcher usage differs slightly between JSR 168 and the IBM Portlet API. JSR 168 uses the same mechanism to get a request dispatcher from context with the path as parameter as the servlet API.
- Persistent portlet data
JSR 168 persistent data,
PortletPreferences, have an API similar to the JDK 1.4Preferencesand allows specifying a default value for the get methods. The code to check for a null return value and explicitly set a default value are no longer necessary . JSR 168 only allows String and String arrays as values of preferences; arbitrary objects are allowed by the IBM Portlet API. - Action handling
The action handling in JSR 168 is simplified and does not need specific action objects to be created. Creating an action URL automatically triggers a call to the
processActionmethod.
With the definition of JSR 168, there is finally a standard portlet API which allows programming portlets independent of portal vendors and lets you run the same portlet unchanged on different portals.
The concepts of the JSR 168 are closely aligned with the IBM Portlet API. When compared to the IBM Portlet API, the functionality of JSR 168 is restricted, because it is the first version of the portlet specification. Developers familiar with the IBM Portlet API should not have difficulty using the JSR 168 API quickly.
-
Java Portlet Specification JSR 168
-
Java Community Process
-
Web Services for Remote Portlets Specification
-
Portlet API Javadoc for WebSphere Portal 5.0
Stefan Hepper is a software architect on the WebSphere Portal team at the IBM Silicon Valley Lab in the U.S. He is responsible for rendering WCM content in portal and the convergence of WCM and portal. Previously, he was leading the Java Portlet standards JSR 286 and 168. He has conducted lectures at international conferences, published papers, filed patents, and is a co-author of the book Pervasive Computing (Addison-Wesley 2001).




