© Copyright International Business Machines Corporation 2002. All rights reserved.
In this paper, we describe an easy-to-implement scheme for increasing user productivity, using features of the Portlet programming model in WebSphere Portal. We illustrate how information displayed on one portlet can be transferred to and acted upon by another portlet on the same page. You can select specific fields in your portlets that might be useful to other portlets within the same page. For instance, you might want to expose the name of the destination city from a travel booking portlet. A weather portlet within the same portal application could then display weather-related information for that destination city. You can drive the entire interaction visually on the browser window using a user interface model described later in this paper.
In this paper, we describe a specific scenario that has been modified to support such user-driven integration of diverse portlets. We discuss the design of the application, and the portal features and additional technologies used for the integration. The design described here uses some of the message-passing design ideas discussed in WebSphere Portal programming: Portlet application programming, Part 2 . Source code examples are given throughout the paper.
We describe our scheme using a sample application for illustration. The sample application is provided with this paper, and runs on WebSphere Portal Version 4.1. The portlet APIs used by the sample application and discussed in this paper pertain to WebSphere Portal 4.1.
Scenario: e-tailer customer service representative
Acme Corporation is an online retailer (e-tailer). Acme receives a number of requests from customers about their orders and would like to empower their customer service representatives with an 'Order Status' portal page that would provide a single point of access for all order-related information. Using this page, Acme's customer service representatives would be able to provide customers with any required order-related information in a fast and accurate manner.
Acme builds an Order Status portal application containing a number of different portlets, which connect to one or more back-end systems to provide information about specific aspects of customer orders, such as:
- Order Summary - Connects to Acme's order tracking back-end to provide a listing of all the orders for a particular month. Each entry in the list contains an order ID and a customer ID.
- Order Details - Connects to Acme's order tracking back-end to provide order details corresponding to a specific order ID.
- Customer Details - Connects to Acme's customer relationship back-end to provide customer details corresponding to a specific customer ID.
- Tracking Details - Connects to an external vendor site to provide tracking details corresponding to a tracking ID.
- Accounts Receivable - Connects to Acme's financial back-end to provide information on the accounts receivable information corresponding to a specific order ID.
Figure 1 shows a view of the Order Status entry page. The user interacts individually with each portlet; it is possible and even likely that the portlets were developed independently. For example, the Tracking Details portlet might access the database of an external shipping company and have been provided by that company, while the remaining portlets might access the databases of various departments within Acme (Orders, Accounts, Customer Relations etc.) and may have been developed independently by those departments.
Figure 1: A view of the Order Status entry page

To retrieve complete details about a particular order, an Acme customer service representative has to enter various identifiers displayed in one portlet view, into other portlet's entry fields. Thus, after a series of interactions, each portlet retrieves and displays details about some aspect of a particular order. Figure 2 shows the Order Status portal page after a series of interactions have been completed to display details about various aspects of a particular order.
Figure 2: A view of the Order Status results page

Acme would like to facilitate interaction among the Order Status portlets by enabling easy transfer of compatible data between them. For example, Acme would like to let a customer service representative click on a specific order ID and, through a menu, pass that order ID to the OrderDetails portlet, which would then react by displaying specific details about that order. Such pre-configured interaction options between portlets have the advantage of eliminating the manual observation and entry of bits of data from one portlet's view into another's entry field, and would also simplify the end-user's learning process, eliminating errors.
Such a menu-driven interaction for determining compatible interactions is shown in Figure 3. When the user clicks on the arrow icon next to an order ID in the Order Summary portlet, a list of target portlets that can react to the order ID are displayed in a menu. In our example, the Order Details and Accounts Receivable portlets are valid destinations for an order ID. When the user selects a particular target, the data contained in the source portlet (in this case the order ID) is passed to the target portlet. The target portlet reacts to this information by querying its back-end system using the given order ID, and refreshes its display with information relevant to that order ID.
Figure 3: A view of the Order Status page showing the menu-driven interaction

The design of the sample application follows the traditional MVC approach for developing portlet-based Web applications. Each component portlet is associated with a portlet class implementing the ActionListener interface from the Portlet API. The control logic is encapsulated in the portlet class. Each component portlet is associated with a Java bean for accessing and encapsulating data from the back-end system. The Java bean represents the Model, and is passed by the portlet to a JSP, which renders a View by accessing data from the bean. Listing 1 shows relevant portions of the Order Summary portlet code. Listing 2 shows relevant portions of the JSP used to render the Order Summary view.
Listing 1 : Order Summary portlet
OrderSummaryPortlet
extends PortletAdapter
implements ActionListener
{
//Constant declarations omitted
public void doView(PortletRequest request, PortletResponse response)
throws PortletException,IOException
{
String action = (String) request.getSession().getAttribute(ACTION_NAME);
OrderMonthBean omb = new OrderMonthBean();
request.setAttribute(ORDER_MONTH_BEAN, omb);
if (action == null || action.equals(MONTH_ENTRY)) {
//Show initial page; code omitted
} else if (action.equals(ORDERS_FOR_MONTH)) {
String month = (String) request.getSession().getAttribute(MONTH);
Order orderList[] = ShippingDB.getOrders(month);
String actionURI = ShippingUtils.createActionURI(MONTH_ENTRY, response);
omb.setActionURI(actionURI);
if (orderList != null) {
omb.setOrders(orderList);
omb.setMonth(ShippingDB.normalizeMonth(month));
getPortletConfig().getContext().include("/WEB-INF/jsp/
OrderSummaryView.jsp",request, response);
} else {
//Print error page; omitted
}
}
}
public void actionPerformed (ActionEvent event)
{
DefaultPortletAction action = (DefaultPortletAction) event.getAction();
PortletRequest request = event.getRequest();
if (action.getName().equals(ORDERS_FOR_MONTH)) {
request.getSession().setAttribute(ACTION_NAME, ORDERS_FOR_MONTH);
request.getSession().setAttribute(MONTH, request.getParameter(MONTH));
} else if (action.getName().equals(MONTH_ENTRY)) {
request.getSession().setAttribute(ACTION_NAME, MONTH_ENTRY);
}
} |
Listing 2 : JSP rendering view for Order Summary portlet
<!-- JSP headers and much formatting omitted -->
<%
OrderMonthBean omb = (OrderMonthBean) request.getAttribute
(OrderMonthPortlet.ORDER_MONTH_BEAN);
%>
<table align="center">
<tr>
<td align="center">
Orders for <%= omb.getMonth() %>
</td>
</tr>
<tr>
<td align="center">
Order ID
</td>
<td align="center">
Customer ID
</td>
</tr>
<%
Order[] orders = omb.getOrders();
for (int i = 0; i < orders.length; i++) {
%>
<tr>
<td align="center">
<%= orders[i].getOrderId() %>
</td>
<td align="center">
<%= orders[i].getCustomerId() %>
</td>
</tr>
<% } %>
</table>
<form method="POST" action="<%= omb.getActionURI() %>"
enctype="application/x-www-form-urlencoded"
name="MonthEntry">
<p align="center">
<input name="Back" type="submit" value="Back"/>
</p>
</form> |
Changes to enable user-driven interaction
Enabling on-the-glass interactions such as those described above involves modification to the view generation code in the JSPs, as well as the action-processing code. In the view generation code in the JSPs, you need additional markup and Javascript utilities to facilitate the selection of sources and targets by the user. During action processing, you need additional function to ensure that the selected target receives and processes the action selected by the user. Figure 4 illustrates the steps involved in sending an Order ID from the Order Summary portlet to the Order Details portlet. We choose this specific example for concreteness; enabling other such interactions may follow a similar design pattern.
Figure 4: Steps for enabling on-the-glass integration

The following steps are illustrated in Figure 4:
1. The doView() implementation of the Order Summary portlet creates portlet action URIs corresponding to each order ID. The sample code in Listing 3 below illustrates the process of creating the portlet action URIs. Note that two action URIs are created for each order ID - one action sends a message containing the order ID to the Order Details portlet, and the other sends a message containing the order ID to the Accounts Receivable portlet. These happen to be the two actions on the Order Summary page that can process an order ID. If there are other actions on other portlets which can be triggered off other data, such as the customer ID, they could be treated in a similar manner. Later in this section, we will discuss the details of the actions to send messages containing the order ID to other portlets.
The action URIs are set on the Java bean, from where the JSP to render the view can retrieve them.
Listing 3: Changes to doView in Order Summary portlet
public void doView(PortletRequest request, PortletResponse response)
throws PortletException,IOException
{
String action = (String) request.getSession().getAttribute(ACTION_NAME);
OrderMonthBean omb = new OrderMonthBean();
request.setAttribute(ORDER_MONTH_BEAN, OMB);
if (action == null || action.equals(MONTH_ENTRY)) {
//Show initial page; code omitted
} else if (action.equals(ORDERS_FOR_MONTH)) {
String month = (String) request.getSession().getAttribute(MONTH);
Order orders[] = ShippingDB.getOrders(month);
String actionURI = ShippingUtils.createActionURI(MONTH_ENTRY, response);
omb.setActionURI(actionURI);
if (orders != null) {
//Create ActionURIs for each clickable action
String[] oactionURIs = new String[orders.length];
String[] aactionURIs = new String[orders.length];
//We could add clickable actions for the customer ID as well
for (int i = 0; i < orders.length; i++) {
oactionURIs[i] = ShippingUtils.createActionURI(
SEND_MESSAGE_TO_ORDER_DETAILS,
OrderDetailPortlet.ORDER_ID, orders[i].getOrderId(),
response);
aactionURIs[i] = ShippingUtils.createActionURI(
SEND_MESSAGE_TO_ACCOUNTS,
AccountsPortlet.ACCOUNT_ID, orders[i].getOrderId(),
response);
}
omb.setOrderDetailsActionURIs(oactionURIs);
omb.setAccountsActionURIs(aactionURIs);
omb.setOrders(orders);
omb.setMonth(ShippingDB.normalizeMonth(month));
getPortletConfig().getContext().include("/WEB-INF/
jsp/OrderSummaryView.jsp", request, response);
} else {
//Print error page; code omitted
}
} else if (action.equals(ERROR)) {
//Print error page; code omitted
}
} |
2. Next we add the set of action URIs to the bean and pass them to the JSP responsible for creating the view of the Order Summary portlet. The JSP retrieves the action URIs from the bean. It inserts an image for each source (each order ID in this case), which we'll refer to as a "clickable icon". We associate a Javascript function with the onClick event for the clickable icon. The function displays a menu and associates an action with each menu item. Each action receives the order ID and sends it to a target portlet that can process the order ID. Listing 4 shows the JSP for encoding the action URIs in the menu, with the important portions of the code in bold.
The Javascript functions for the menu are defined in a library that you must include in the page header. This can be inserted by one of the portlets in the application in its beginPage operation.
Listing 4: Changes to Order Summary view JSP
<!--JSP headers and much formatting omitted -->
<%
OrderMonthBean omb = (OrderMonthBean)
request.getAttribute(OrderMonthPortlet.ORDER_MONTH_BEAN);
%>
<table align="center">
<tr>
<td align="center">
Orders for <%= omb.getMonth() %>
</td>
</tr>
<tr>
<td align="center">
Order ID
</td>
<td align="center">
Customer ID
</td>
</tr>
<%
Order[] orders = omb.getOrders();
String[] orderDetailsActionURIs = omb.getOrderDetailsActionURIs();
String[] accountsActionURIs = omb.getAccountsActionURIs();
for (int i = 0; i < orders.length; i++) {
%>
<tr>
<td align="center">
<font color="#0000FF"><!-- A bit of javascript to associate
a menu with an icon;
each item in the menu results in an action -->
<img src="<%= response.encodeURL("/images/arrowp.gif") %>" |
3. The Order Summary portlet now completes its doView(). The portlet container assembles the entire page and sends it back to the browser, which displays it. In particular, it displays the Order ID and Customer ID columns as part of the Order Summary portlet's view. The clickable icon that was inserted by the JSP appears next to the Order ID in each row.
4. If the user clicks on any of the clickable icons, the Javascript function runs to display a menu of matching actions. If the user selects an action from the menu, it invokes the actionPerformed method on the Order Summary portlet. The name of the action specifies to the portlet that it needs to send a message containing the order ID to the target portlet. Listing 5 shows the action handling code for the Order Summary portlet with the additions to handle the sending of the order ID to target portlets highlighted in bold. Note that there are two new actions, one for sending the order ID to the Accounts Receivable portlet, and the other for sending it to the Order Details portlet. Also, a custom MessageData object is used to encapsulate the message data and convert it to a string representation for sending on the message; the application can use any suitable message object for this purpose.
Listing 5: Changes to Order Summary action listener
public void actionPerformed (ActionEvent event)
{
PortletRequest request = event.getRequest();
try {
DefaultPortletAction action = (DefaultPortletAction) event.getAction();
if (action.getName().equals(ORDERS_FOR_MONTH)) {
request.getSession().setAttribute(ACTION_NAME,
ORDERS_FOR_MONTH);
request.getSession().setAttribute(MONTH, request.getParameter
(MONTH));
} else if (action.getName().equals(MONTH_ENTRY)) {
request.getSession().setAttribute(ACTION_NAME, MONTH_ENTRY);
//The remaining actions are used to send messages to other portlets
//to cause them to react to the chosen order ID
} else if (action.getName().equals(SEND_MESSAGE_TO_ORDER_DETAILS)) {
MessageData data = new MessageData(OrderDetailPortlet.
ORDER_DETAIL_MESSAGE);
data.setParameter(OrderDetailPortlet.ORDER_ID, (String)
action.getParameters().get(OrderDetailPortlet.ORDER_ID));
DefaultPortletMessage message =
new DefaultPortletMessage(MessageData.serialize(data));
getPortletConfig().getContext().send(
OrderDetailPortlet.PORTLET_NAME, message);
} else if (action.getName().equals(SEND_MESSAGE_TO_ACCOUNTS)) {
MessageData data = new MessageData(AccountsPortlet.
ACCOUNT_DETAILS_MESSAGE);
data.setParameter(AccountsPortlet.ACCOUNT_ID, (String)
action.getParameters().get(AccountsPortlet.ACCOUNT_ID));
DefaultPortletMessage message =
new DefaultPortletMessage(MessageData.serialize(data));
getPortletConfig().getContext().send(AccountsPortlet.
PORTLET_NAME, message);
}
} catch (Exception e) {
//Error handling omitted
}
} |
5. The message is received by the Order Details portlet, which extracts the message name and order ID from the message, and sets them on the portlet session for retrieval by the subsequent doView() method. This is illustrated in Listing 6.
Listing 6: Order Details message listener
public class OrderDetailPortlet
extends PortletAdapter
implements ActionListener, MessageListener
{
//Other methods omitted
public void messageReceived (MessageEvent event)
throws PortletException
{
DefaultPortletMessage msg = (DefaultPortletMessage) event.getMessage();
String data = msg.getMessage();
MessageData md = MessageData.deserialize(data);
if (md.getName().equals(ORDER_DETAIL_MESSAGE)) {
event.getRequest().getSession().setAttribute(ACTION_NAME,
ORDER_DETAILS);
event.getRequest().getSession().setAttribute(ORDER_ID,
md.getParameter(ORDER_ID));
}
}
} |
6. The portlet container invokes the doView() method on the Order Details portlet, and then retrieves the action and order ID information set in the portlet session by the message listener code. Next it proceeds to retrieve and display the order details. Note that where the information was set in the session is immaterial at this point. In particular, it could also have been set in the action listener instead of the message listener, if the user interacted directly with the Order Details portlet, instead of indirectly through the Order Summary portlet. Listing 7 shows the relevant portions of the Order Details portlet's doView() method.
Listing 7: Order Details doView
public void doView(PortletRequest request, PortletResponse response)
throws PortletException,IOException
{
String action = (String) request.getSession().getAttribute(ACTION_NAME);
OrderDetailBean odb = new OrderDetailBean();
request.setAttribute(ORDER_DETAIL_BEAN, odb);
if (action == null || action.equals(ORDER_ID_ENTRY)) {
//Show initial page; code omitted
} else if (action.equals(ORDER_DETAILS)) {
String orderId = (String) request.getSession().getAttribute(ORDER_ID);
OrderDetail od = ShippingDB.getOrderDetail(orderId);
String actionURI = ShippingUtils.createActionURI(ORDER_ID_ENTRY,
response);
odb.setActionURI(actionURI);
if (OD != null) {
odb.setOrderDetail(OD);
getPortletConfig().getContext().include
("/WEB-INF/jsp/OrderDetailView.jsp", request, response);
} else {
//No match, print error page; code omitted
}
} |
7. The portlet container finishes calling the doView methods on each of the portlets on the page, and assembles and sends the new page back to the browser. The browser's new page contains the details of the specified order ID in the Order Details portlet's view.
In this document, we have discussed a programmatic approach to enabling portlets for on-the-glass integration. The approach is based on identifying matching actions on target portlets, providing message-passing support for invoking such actions from source portlets, and using Javascript to give end-users visual cues for the matching actions in the form of a menu. The approach uses a sample application that is provided with this article, and runs on WebSphere Portal 4.1. The code examples illustrate the design and implementation.
| Name | Size | Download method |
|---|---|---|
| resources.zip | .1 MB | FTP |
Information about download methods
Samar Choudhary is an Advisory Software Engineer at the IBM Pittsburgh Lab. You may contact him at samar@us.ibm.com.
Shankar Ramaswamy is a Senior Software Engineer at the IBM Pittsburgh Lab. You may contact him at shankar4@us.ibm.com.
Sai Rathnam is an Advisory Software Engineer at the IBM Pittsburgh Lab. You may contact him at rathnam@us.ibm.com.
Amber Roy-Chowdhury is a Senior Software Engineer at the IBM Pittsburgh Lab. You may contact him at amberr@us.ibm.com.




