 | Level: Intermediate Samar Choudhary, Advisory Software Engineer, IBM Pittsburgh Lab Shankar Ramaswamy, Senior Software Engineer Sai Rathnam, Advisory Software Engineer, IBM Pittsburgh Lab Amber Roy-Chowdhury, Senior Software Engineer, IBM Pittsburgh Lab
24 Jul 2002 This article shows how information displayed on one portlet can be transferred to and acted upon by another portlet on the same page, using WebSphere Portal Server 4.1.
© Copyright International Business Machines Corporation
2002. All rights reserved. Introduction 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

Design and
implementation Basic application design 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") %>" onclick="var orderIdMenu = new PopUpMenu(80);
createMenu(orderIdMenu,
'Order Details', '<%= orderDetailsActionURIs[i] %>',
'Accounts', '<%= accountsActionURIs[i] %>');
orderIdMenu.open(this.getBoundingClientRect().left,
this.getBoundingClientRect().bottom +
(document.getElementsByTagName('BODY'))[0].scrollTop);" />
<%= orders[i].getOrderId() %>
</td>
<td align="center">
<!-- If the customer ID could be processed by other portlets,
similar javascript would be added here -->
<%= orders[i].getCustomerId() %>
</td>
</tr>
<% } %>
</table>
<form action="<%= omb.getActionURI() %>"
enctype="application/x-www-form-urlencoded"
name="MonthEntry">
<p align="center">
<input name="Back" type="submit" value="Back"/>
</p>
</form> |
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.
Conclusion 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.
Download | Name | Size | Download method |
|---|
| resources.zip | .1 MB | FTP | HTTP |
Resources
About the authors  | |  | 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. |
Rate this page
|  |