|
|
DEVELOPMENT
Inter-Portlet Messaging with WebSphere Portal
WebSphere Portal lets your portlets communicate with each other, a function that can greatly improve the user experience. Here's how to use it.
By Sunil Hiranniah, Software Engineer, IBM Developer Relations; and Sukumar Konduru, Advisory Software Engineer, IBM Developer Relations
One of the important features of WebSphere Portal is the ability for your portlets to communicate with each other. Portal developers generally face the difficult task of integrating applications into a common interface. WebSphere Portal provides a framework with built-in support for portlet communication or messaging among portlets in the application. This provides a better personal experience for portal users. For example, in a banking application, users could see their account summary in one portlet and account transaction activity in another portlet. When a transaction occurs, it's reflected in the account summary portlet immediately.
 |
| Figure 1: MessageSendingPortlet and MessageReceivingPortlet - Here's what you should see after you've deployed the example portlets to a page. |
 |
| Figure 2: Account numbers sent to all the portlets - In this scenario, we decided to send the message to all portlets. |
 |
| Figure 3: Message sent to only one portlet on the page - Here, we've entered AccountReceiverPortlet1 as the name of the portlet to receive the message. |
Is it complicated to get this kind of functionality? Does it involve a lot of coding? The answer is no. In this article, we'll explain how portlet messaging works in WebSphere Portal 4.1 using example code.
How portlet messaging works
Here are the basics. One portlet sends the message (the portlet's action listener), and that message is received by one or more portlets in the application (he portlet's message listener). The user performs an action in one portlet; the action event is captured and processed, and the message is relayed to other portlets. A single portlet can both send and receive messages. However, there are some limitations with portlet messaging in WebSphere Portal you need to know:
- To perform messaging among portlets, those portlets have to be on the same page. This limitation is due to performance issues.
- Portlets that are deployed as Web services can't send or receive messages.
- If your portlet implements the PortletMessage Interface, portlet messaging must occur within the same portlet application (one or more portlets packaged in the same .WAR file). If your portlets extend the Default PortletMessage interface, they can interact with other portlet applications.
Example scenario
In this paper, we'll demonstrate how portlet messaging works using a simple banking application as an example. Let's start building the code.
The application consists of three portlets: AccountSenderPortlet, AccountReceiverPortlet1, and AccountReceiverPortlet2. AccountSenderPortlet displays a form for users to enter a bank account number, and an option to submit to all portlets on the page or to a specific portlet. After the user submits an account number, the actionPerformed method of AccountSenderPortlet is invoked. This method will use the send method of PorletContext to send the message either to all portlets on the page or to a specific portlet, depending on the user request. The message contains the account number, and other portlets will display the result.
We have a Java Server Page (JSP) page, which will have the provision to input the account number, and an option to send to all the portlets or to any particular portlet with that portlet number as an input value (MessageSending Portlet). Action performed on this portlet will be reflected in the MessageReceiving portlet. Listing 1 shows the sample JSP (MessageSendingView.jsp) we use for our application.
Listing 1: MessageSendingView.jsp.
<%@ taglib uri='/WEB-INF/tld/portlet.tld' prefix='portletAPI'%>
<portletAPI:init/>
<jsp:useBean id="sendAccountNumberURI" class="java.lang.String" scope="request" />
<SCRIPT language="JavaScript">
function AutoSelectRadioButton(name, value)
{
var mainForm = <%=portletResponse.encodeNamespace("message_form")%>
// Now check the radiobutton
for (var i=0; i < mainForm.elements.length; i++)
{
var e = mainForm.elements[i];
if ( (e.type=='radio') && e.name == name && (e.value==value) )
{
e.checked = true;
}
}
}
</SCRIPT>
<DIV CLASS="wpsPortletBack">
<SPAN CLASS="wpsPortletHead">
Bank of XYZ<BR>
</SPAN>
<SPAN CLASS="wpsPortletText">
Enter your account number to demonstrate displaying
it in other portlet<BR>
</SPAN>
<form name= "<%=portletResponse.encodeNamespace("message_form")%>" action="<%=sendAccountNumberURI%>" method="POST">
<TABLE>
<TR>
<TD>
<SPAN CLASS="wpsLabelText">Account Number:</SPAN>
</TD>
<TD>
<input CLASS="wpsEditField" type="text" name="<%=portletResponse.encodeNamespace("accNumber")%>" value=""><br>
</TD>
</TR>
<TR>
<TD>
<INPUT TYPE ="RADIO" NAME="<%=portletResponse.encodeNamespace("sendAllPortlets")%>" VALUE ="yes" checked>
<SPAN class="wpsPortletTinyText">Send all portlets</SPAN>
</TD>
</TR>
<TR>
<TD>
<INPUT TYPE ="RADIO" NAME="<%=portletResponse.encodeNamespace("sendAllPortlets")%>" VALUE ="no">
<SPAN class="wpsPortletTinyText">Send to portlet</SPAN><br>
</TD>
</TR>
<TR>
<TD>
<SPAN CLASS="wpsLabelText">Portlet Name:</SPAN>
</TD>
<TD>
<input CLASS="wpsEditField" type="text" name="<%=portletResponse.encodeNamespace("portletName")%>" value=""
onkeydown="javascript:AutoSelectRadioButton('<%=portletResponse.encodeNamespace("sendAllPortlets")%>','no')"><BR>
</TD>
</TR>
<TR>
<TD colspan="2" align="center">
<input CLASS="wpsButtonText" type="submit" name="Save_Button" value="submit" size=35>
</TD>
</TR>
</TABLE>
</form>
</DIV>
When the portal submits an event to the portal server, the portal aggregation process begins. The message is sent from a portlet's action listener and received by another portlet's message listener. The structure of our MessageSendingPortlet is simple (listing 2). When the portal receives an event from a client, it decodes the action URI sent by the client and propagates an action event to the appropriate portlet. Typically, this is a single event to a single portlet.
Listing 2: MessageSendingPortlet.
package samplepkg;
import com.ibm.wps.portlets.*;
import org.apache.jetspeed.portlet.*;
import org.apache.jetspeed.portlets.*;
import org.apache.jetspeed.portlet.event.*;
import java.io.*;
import java.util.*;
public class MessageSendingPortlet extends PortletAdapter implements ActionListener{
/***************************************************************
* Displays a form which has account number as text field
*/
public void doView( PortletRequest portletRequest,
PortletResponse portletResponse) throws PortletException,
IOException{
DefaultPortletAction action = new DefaultPortletAction("SEND_ACCOUNT_NUMBER");
PortletURI sendAccountNumberURI = portletResponse.createReturnURI();
sendAccountNumberURI.addAction(action);
portletRequest.setAttribute("sendAccountNumberURI", sendAccountNumberURI.toString());
getPortletConfig().getContext().include("/WEB-INF/jsp/MessageSendingView.jsp", portletRequest, portletResponse);
}
public void actionPerformed(ActionEvent event) throws PortletException {
PortletRequest request = event.getRequest();
String accNumber = request.getParameter("accNumber");
String sendAllPortlets = request.getParameter("sendAllPortlets").trim();
String portletName = request.getParameter("portletName").trim();
if ( accNumber != null && accNumber.trim().length() > 0 ) {
DefaultPortletMessage message = new DefaultPortletMessage(accNumber);
try {
// To send message to all portlets on the page.
if(sendAllPortlets.equals("yes") )
getPortletConfig().getContext().send(null, message);
else
getPortletConfig().getContext().send(portletName, message);
} catch ( AccessDeniedException ade ) {
System.out.println("Could not send Message" + ade);
}
}
}
}
As you'll notice from the code, a portlet URI is constructed. The action from the submit button is propagated back to the portlet's action listener through this URI. After the URI is constructed, the portlet's content is rendered to the MessageSendingView JSP page via the getContext().include method.
The portlet displays the account number and the portlet name fields. You provide the bank number and the portlet name for the changes to reflect and submit the action. JavaScript is used to automatically select the "Send to Portlet" button, where users type the portlet name in the text field. The event is presented to the actionPeformed method. This method sends the account number as a message to all portlets that reside on the same portlet or to a particular portlet based on the user request.
Based on the results of that action, the portlet can send a message to other portlets using the send() method of the PortletContext object. The send() method takes the following arguments:
- portletName: The name of the portlet that receives the request. The value can be "null" or "name of the portlet." Null means the message is sent to all the portlets on the same page, and portletName is used to relay the message to a particular portlet defined by the <portlet-name> tag in the portlet deployment descriptor. We've used both of these values, as the users can send the message to one portlet or multiple portlets.
- Message: The message to be sent. The message must be a PortletMessage object or any subclass that implements that interface. In our prior example, the message is instantiated in a DefaultPortletMessage object containing the url string from the action (SEND_ACCOUNT_NUMBER) that was performed.
As we explained earlier, portlets can send multiple messages. The same process loops until all message events are processed. The catch statement is included in the code in case there's any failure in sending messages.
As listing 3 shows, the receiving portlet is defined to display the account number.
Listing 3: MessageReceivingView.jsp.
<jsp:useBean id="accNumber" class="java.lang.String" scope="request" />
<DIV CLASS="wpsPortletBack">
<SPAN CLASS="wpsPortletHead">
Bank of XYZ<BR> </SPAN>
<SPAN CLASS="wpsPortletText">
Account Number: <%=accNumber%> </SPAN>
</DIV>
The receiving portlet implements the MessageListener interface that retrieves the message using the getMessage() method of the message event. Listing 4 shows the structure of our MessageReceivingPortlet.
Listing 4: MessageReceivingPortlet.
package samplepkg;
import java.io.*;
import org.apache.jetspeed.portlet.*;
import com.ibm.wps.portlets.*;
import org.apache.jetspeed.portlet.event.MessageListener;
import org.apache.jetspeed.portlet.event.MessageEvent;
public class MessageReceivingPortlet extends PortletAdapter implements MessageListener {
public static final String ACCOUNT_NUMBER = "accNumber";
public void doView(PortletRequest request, PortletResponse response) throws PortletException, IOException {
String accNumber = (String)request.getSession().getAttribute(ACCOUNT_NUMBER );
if ( accNumber== null ) accNumber= "";
request.setAttribute(ACCOUNT_NUMBER, accNumber);
// Invoke the JSP to render
getPortletConfig().getContext().include("/WEB-INF/jsp/MessageReceivingView.jsp", request, response);
request.getSession().removeAttribute(ACCOUNT_NUMBER);
}
public void messageReceived(MessageEvent event) {
DefaultPortletMessage message = (DefaultPortletMessage) event.getMessage();
PortletRequest request = event.getRequest();
request.getSession().setAttribute(ACCOUNT_NUMBER, message.getMessage());
}
}
MessageReceivingPortlet contains two methods:
- The doView() method Retrieves the account number out of session and displays the result. This method is responsible for rendering the portlet's content. The value of the account number is placed in the portlet request object for subsequent use by the JSP page.
- The messageReceived() method extracts the account number from the message and stores the value in session.
Notice the portlets AccountReceiverPortlet1, AccountReceiverPortlet2, and AccountSenderPortlet in the portlet.xml file shown in listing 5.
Listing 5: portlet.xml.
<portlet-app-def>
<portlet-app uid="samplepkg.MessageSendingReceivingPortletApp" major-version="41" minor-version="0">
<portlet-app-name>Message Sharing app(Sample code)</portlet-app-name>
<portlet id="Portlet_1" href="WEB-INF/web.xml#Servlet_Sender_Portlet">
<portlet-name>AccountSenderPortlet</portlet-name>
<cache>
<expires>0</expires>
<shared>NO</shared>
</cache>
<allows>
<maximized/>
<minimized/>
</allows>
<supports>
<markup name="html">
<view output="fragment"/>
</markup>
</supports>
</portlet>
<portlet id="Portlet_2" href="WEB-INF/web.xml#Servlet_Receiver_Portlet1">
<portlet-name>AccountReceiverPortlet1</portlet-name>
<cache>
<expires>0</expires>
<shared>NO</shared>
</cache>
<allows>
<maximized/>
<minimized/>
</allows>
<supports>
<markup name="html">
<view output="fragment"/>
</markup>
</supports>
</portlet>
<portlet id="Portlet_3" href="WEB-INF/web.xml#Servlet_Receiver_Portlet2">
<portlet-name>AccountReceiverPortlet2</portlet-name>
<cache>
<expires>0</expires>
<shared>NO</shared>
</cache>
<allows>
<maximized/>
<minimized/>
</allows>
<supports>
<markup name="html">
<view output="fragment"/>
</markup>
</supports>
</portlet>
</portlet-app>
<concrete-portlet-app uid="samplepkg.MessageSendingReceivingPortletApp.concrete">
<portlet-app-name>Message Sharing concrete app (Sample code)</portlet-app-name>
<concrete-portlet href="#Portlet_1">
<portlet-name>Accoun SenderPortlet1</portlet-name>
<default-locale>en</default-locale>
<!-- Begin translation: -->
<language locale="en">
<title>Account Sender Portlet</title>
<title-short>Sender</title-short>
<description>Portlet demonstrates how to send message</description>
<keywords>sender</keywords>
</language>
<!-- End translation: -->
</concrete-portlet>
<concrete-portlet href="#Portlet_2">
<portlet-name>Account ReceiverPortlet1</portlet-name>
<default-locale>en</default-locale>
<!-- Begin translation: -->
<language locale="en">
<title>Account Receiver Portlet1</title>
<title-short>Receiver1</title-short>
<description>Portlet demonstrates how to receive message</description>
<keywords>receiver</keywords>
</language>
<!-- End translation: -->
</concrete-portlet>
<concrete-portlet href="#Portlet_3">
<portlet-name>Account ReceiverPortlet 2</portlet-name>
<default-locale>en</default-locale>
<!-- Begin translation: -->
<language locale="en">
<title>AccountReceiver Portlet2</title>
<title-short>Receiver2</title-short>
<description>Portlet demonstrates how to receive message</description>
<keywords>receiver</keywords>
</language>
<!-- End translation: -->
</concrete-portlet>
</concrete-portlet-app>
</portlet-app-def>
In the web.xml files (listing 6), you'll specify the class files for your message sending (MessageSendingPortlet) and messaging receiving portlet (MessageReceivingPortlet).
Listing 6: web.xml.
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
"http://java.sun.com/j2ee/dtds/web-app_2.2.dtd">
<web-app id="WebApp_1_1">
<display-name>Message sharing portlets</display-name>
<servlet id="Servlet_Sender_Portlet">
<servlet-name>MessageSendingPortlet</servlet-name>
<servlet-class>samplepkg.MessageSendingPortlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet id="Servlet_Receiver_Portlet1">
<servlet-name>MessageReceivingPortlet1</servlet-name>
<servlet-class>samplepkg.MessageReceivingPortlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet id="Servlet_Receiver_Portlet2">
<servlet-name>MessageReceivingPortlet2</servlet-name>
<servlet-class>samplepkg.MessageReceivingPortlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping id="ServletMapping_1_1">
<servlet-name>MessageSendingPortlet</servlet-name>
<url-pattern>/MessageSendingPortlet/*</url-pattern>
</servlet-mapping>
<servlet-mapping id="ServletMapping_1_2">
<servlet-name>MessageReceivingPortlet1</servlet-name>
<url-pattern>/MessageReceivingPortlet1/*</url-pattern>
</servlet-mapping>
<servlet-mapping id="ServletMapping_1_3">
<servlet-name>MessageReceivingPortlet2</servlet-name>
<url-pattern>/MessageReceivingPortlet2/*</url-pattern>
</servlet-mapping>
</web-app>
Package MessageSendingPortlet, MessageReceivingPortlet, and the deployment descriptors as a .WAR file for deployment within the WebSphere Portal environment. You can refer to the November/December 2002 issue of WebSphere Advisor magazine (http://WebSphereAdvisorMagazine.com) to learn how to package a portlet as a .WAR file, which is the requirement for deploying in the WebSphere Portal environment, along with the details about deployment descriptors. The article also includes instructions for deploying the portlet to a page.
After you've deployed the above portlets to a page, you should see the screen shown in figure 1.
In the first scenario, you'll enter the account number as 12345 and send it to all portlets. Notice the changes as shown in figure 2.
You can also choose a single portlet for sending the message (in this case, the account number). Enter the name of the portlet as AccountReceiverPortlet1 or AccountReceiverPortlet2. In our example, we enter AccountReceiverPortlet1 as the name of the portlet. You should see the changes as shown in figure 3.
I hope this article helps get you started on developing inter-portlet messaging in WebSphere Portal. Remember: This technique can greatly improve the personal experience for your portal users, and it's not that hard!
Sunil Hiranniah works for IBM Developer Relations Technical Support Center in Dallas, Texas. He has worked as a software engineer/developer in various commercial projects since 1997. He has ample experience and has published extensively on the WebSphere family of products, J2EE, databases, and security. He is the co-author for the IBM WebSphere Portal 4.1 Handbook (IBM, 2002). His current and most interesting job involves supporting customers with WebSphere Portal issues. sunilh@us.ibm.com.
Sukumar Konduru is an Advisory Software Engineer at the IBM Dallas, Texas, Developer Technical Support Center. He holds a master's degree in computer science from the University of Houston. You can reach Sukumar at konduru@us.ibm.com.
ARTICLE INFO
FREE ACCESS
Keyword Tags: Application Development, Business Software, Data Integration, Development, IBM, IBM Lotus, IBM WebSphere, IBM WebSphere Portal, Integration, Java, Messaging, Portals, Programming, Security, Tech: Development, Web Development, XML
|
|