Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Giving Users More for their Clicks in WebSphere Portal

Samar Choudhary, Advisory Software Engineer, IBM Pittsburgh Lab
Samar Choudhary is an Advisory Software Engineer at the IBM Pittsburgh Lab. You may contact him at samar@us.ibm.com.
Shankar Ramaswamy, Senior Software Engineer
Shankar Ramaswamy is a Senior Software Engineer at the IBM Pittsburgh Lab. You may contact him at shankar4@us.ibm.com.
Sai Rathnam, Advisory Software Engineer, IBM Pittsburgh Lab
Sai Rathnam is an Advisory Software Engineer at the IBM Pittsburgh Lab. You may contact him at rathnam@us.ibm.com.
Amber Roy-Chowdhury, Senior Software Engineer, IBM Pittsburgh Lab
Amber Roy-Chowdhury is a Senior Software Engineer at the IBM Pittsburgh Lab. You may contact him at amberr@us.ibm.com.

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

Date:  24 Jul 2002
Level:  Intermediate

Activity:  1708 views
Comments:  

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

NameSizeDownload method
resources.zip.1 MBFTP|HTTP

Information about download methods


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.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

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

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

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

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

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=13580
ArticleTitle=Giving Users More for their Clicks in WebSphere Portal
publish-date=07242002
author1-email=
author1-email-cc=
author2-email=
author2-email-cc=
author3-email=
author3-email-cc=
author4-email=
author4-email-cc=

Tags

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

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

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

Try IBM PureSystems. No charge.

Special offers