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]

The making of MetroSphere, Part 23: Send messages between portlets

Nicholas Chase, President, Chase and Chase, Inc.
Nicholas Chase
Nicholas Chase, a Studio B author, has been involved in Web site development for companies such as Lucent Technologies, Sun Microsystems, Oracle, and the Tampa Bay Buccaneers. Nick has been a high school physics teacher, a low-level radioactive waste facility manager, an online science fiction magazine editor, a multimedia engineer, and an Oracle instructor. More recently, he was the Chief Technology Officer of Site Dynamics Interactive Communications in Clearwater, Florida, and is the author of four books on Web development, including XML Primer Plus (Sams).

Summary:  Sometimes, in the development of a portal, you need two portlets to talk to each other. For example, on MetroSphere, when the user clicks a topic in the Topic List portlet, the team needs the information to be sent not only to the Topic List portlet, but also to the Show Blog portlet. This portlet describes the process for sending messages between portlets and recording the information in the PortletSession object.

Date:  23 Oct 2003
Level:  Introductory

Activity:  1621 views
Comments:  

You'll need to have a working installation of any 4.x version of WebSphere Portal Server to follow along with this tip.

Editor's update

The Web site MetroSphere.com -- the online technical community discussed in this article -- is no longer live. However, the information and screen captures regarding the installation of IBM WebSphere Portal are still accurate and relevant.

What we want to accomplish

The MetroSphere site is a technology-focused community weblog, in which users can post information about interesting sites they've seen and add them to various categories, or topics. In Part 22 of this series, "Set topics -- custom attributes and user sessions," I showed you how to add a link to the topics portlet that would enable a registered user to toggle between showing all available topics and showing just his or her favorite topics. The link did nothing to affect the entries shown in the Show Blog portlet, however, so now we're going to remedy that.

What you're going to do is alter the link action for both the toggle links and the topic links themselves so that in addition to setting session information for the Topic List portlet, the action sends a message to the Show Blog portlet telling it what topic or topics to display. You'll then make changes to the Show Blog portlet to receive the message, decode it, and store the information in a session value.


About sessions

If you've been following along with this series, you may remember that in Part 22 you created a toggle link that set an attribute for the session specifying which set of topics should be shown, as in Listing 1:


Listing 1: Setting a session attribute for the preferred topics
...
  private void setPreftopics(PortletRequest request, String topicState){
      PortletSession session = (PortletSession)request.getSession(false);
      if (topicState.equals("showpref")) {
        session.setAttribute("showtopics",  
                (String)request.getUser().getAttribute("favoriteTopics"));
        session.setAttribute("topicstate", "showpref");
      } else {
        session.setAttribute("showtopics", "-1");
        session.setAttribute("topicstate", "showall");        
      }
  } 
...
  public void actionPerformed (ActionEvent event)  {
      PortletAction portletAction = event.getAction();
      PortletRequest request = event.getRequest();
      
      if (portletAction instanceof DefaultPortletAction)
      {
          DefaultPortletAction action = (DefaultPortletAction)portletAction;
       
          if (action.getName().equals("showpref")){
              setPreftopics(request, "showpref");
              request.getSession().setAttribute("showtopic", "-1");
          } else if (action.getName().equals("showtopic")){
              setPreftopics(request, 
                     (String)action.getParameters().get("showTopicId"));
              request.getSession().setAttribute("showtopic", 
                     (String)action.getParameters().get("showTopicId"));
          } else {
              setPreftopics(request, "showall");
              request.getSession().setAttribute("showtopic", "-1");
          }
      }
  } 
...

Now, it may seem that this should be enough to make the information available to the Show Blog portlet. After all, a session is a session, right? You're even obtaining the session from the request object, so it makes sense that there should be only one.

Let's test that hypothesis. Add a session attribute to both the Topic List and Show Blog portlets. At the top of the doView() method, check the value and add a link to set it:


Listing 2: Displaying a message
...
public class TopicList extends PortletAdapter implements ActionListener {

...
  public void doView (PortletRequest request, PortletResponse response)
     throws PortletException, java.io.IOException
  {            
    PortletSession session = request.getPortletSession(false);

    if (session == null){
       response.getWriter().print("<p>No session yet</p>");
    } else {     
       String clickTime = "";
       if (session.getAttribute("clickTime") == null){
          clickTime = "Variable not set yet";
       } else {
          clickTime = (String)session.getAttribute("clickTime");
       }
       response.getWriter().print("<p>"+clickTime+"</p>");

       PortletURI clickTimeURI = response.createURI();
       DefaultPortletAction clickAction = new DefaultPortletAction("clickTime");
       clickTimeURI.addAction(clickAction);
       response.getWriter().print("<p><a href=\""+
              clickTimeURI.toString()+"\">Set session value</a></p>");
    }

    PortletContext context = getPortletConfig().getContext(); 
    context.include(viewJsp, request, response);    
...
  }

First you're checking to see whether there is a session. If there is, you're checking for a value of the clickTime attribute, and if it doesn't exist yet, you're giving it a value that says so. If it does exist, you can pull it out of the session object and cast it as a String value -- all attributes are retrieved as Objects. In either case, you output the value, and then create a link with an action named clickTime that will automatically set the session attribute to the current time.

To actually set the time, you'll have to make a change to the actionPerformed() method:


Listing 3: Setting the attribute
...
  public void actionPerformed (ActionEvent event)  {
      PortletAction portletAction = event.getAction();
      PortletRequest request = event.getRequest();
      
      if (portletAction instanceof DefaultPortletAction)
      {
          DefaultPortletAction action = (DefaultPortletAction)portletAction;
       
          if (action.getName().equals("clickTime")){
              java.util.Date now = new java.util.Date();
              request.getSession().setAttribute("clickTime", now.toString());
          } else if (action.getName().equals("showpref")){
              setPreftopics(request, "showpref");
              request.getSession().setAttribute("showtopic", "-1");
          } else if (action.getName().equals("showtopic")){
              request.getSession().setAttribute("showtopic", 
                     (String)action.getParameters().get("showTopicId"));
              setPreftopics(request, (String)action.getParameters().get("showTopicId"));
          } else {
              setPreftopics(request, "showall");
              request.getSession().setAttribute("showtopic", "-1");
          }
      }
  } 
    
}

The change itself is actually fairly straightforward; if it's the clickTime action that's been clicked, you create a new Date object and set it as the value of the clickTime attribute.

You can make exactly the same changes in ShowBlog.java, but don't forget to retrieve the session, if you're not doing so already.

Once you've added this test to both portlets, update the application and load the blog page in your browser. Both portlets should show that the variable hasn't been set yet. Click the link in the Topic List portlet and notice that the session variable for the Show Blog portlet still isn't set.


Figure 1. Viewing one variable

At this point some of you may be crying foul. After all, there's no guarantee of the order in which portlets are processed, so maybe the Show Blog portlet was just processed first, even though it's later on the page. Well, no. If you reload the page, you'll find that the information still doesn't show in the Show Blog portlet. If you click the link in the Show Blog portlet, you'll find that each one shows a different time.


Figure 2. Viewing both variables

The question is, why? The answer has to do with the architecture of a portlet application and how it interacts with sessions. While there is only a single HttpSession for a request, there is actually a PortletSession for each concrete portlet instance on the page. In fact, associating the portlet with the user session is what defines a concrete portlet instance. This means that an attribute set on a PortletSession in one portlet will not be available from the PortletSession in another portlet.

So how do you get information from one portlet to another? One way is to use portlet messaging to send information from one portlet to another.


Event processing

Before I move on to the actual creation and receipt of a message, take a look at how messages are processed from an architectural standpoint.

When the portal receives an HTTP request, in most cases there are one or more portlets that need to be processed. In a servlet, all processing is done within the service() method, but with a portlet, processing is broken up into two phases. First, any events such as mouse clicks and messages are processed, and then each of the portlet service() methods (which include doView(), doEdit(), and so on) are processed.

Events are processed in a specific order:

  1. Action events
  2. Message events
  3. Window events

First the action events, generated when the user clicks a link or submits a form, are executed. Because they are processed first, the actionPerformed() method is an ideal place -- and in fact, virtually the only place -- to send a message. Next, the messages are delivered and processed by each recipient. Finally, any window events, such as the minimization of a portlet within the page, are processed.

This order is important. Because window events are processed after actions and messages, you couldn't, say, have a portlet send a message to another portlet when it's minimized. Any messages sent after the message event phase will be discarded. Note, however, that a portlet can send a message when it receives one, because the server is still in the "message event" phase.


Sending a message

Start by creating a message in the Topic List's actionPerformed() method:


Listing 4: Receiving the messages
...
public class TopicList extends PortletAdapter implements ActionListener {

...

  public void actionPerformed (ActionEvent event)  {
      PortletAction portletAction = event.getAction();
      PortletRequest request = event.getRequest();
      
      String messageText = "";
      if (portletAction instanceof DefaultPortletAction)
      {
          DefaultPortletAction action = (DefaultPortletAction)portletAction;
       
          if (action.getName().equals("clickTime")){
              java.util.Date now = new java.util.Date();
              request.getSession().setAttribute("clickTime", now.toString());
          } else if (action.getName().equals("showpref")){
              setPreftopics(request, "showpref");
              request.getSession().setAttribute("showtopic", "-1");
              messageText = "TOPICMSG:showpref:"+getPreftopics(request);
          } else if (action.getName().equals("showtopic")){
              request.getSession().setAttribute("showtopic", 
                    (String)action.getParameters().get("showTopicId"));
              setPreftopics(request, 
                    (String)action.getParameters().get("showTopicId"));
              messageText = "TOPICMSG:showtopic:"+
                    (String)action.getParameters().get("showTopicId");
          } else {
                setPreftopics(request, "showall");
                request.getSession().setAttribute("showtopic", "-1");
                messageText = "TOPICMSG:showall";
          }
      }
      try {
          DefaultPortletMessage dpm = new DefaultPortletMessage(messageText);
          getPortletConfig().getContext().send("Show Blog", dpm);
      } catch ( AccessDeniedException ade ) {
          PortletLog pLog = getPortletLog();
          pLog.error( "MessageSenderPortlet: error sending message");
      }
  }    
}

You start by creating the message text itself. First you indicate that it is, in fact, a topic message. Right now, that's the only kind you're sending, but you might get to the point where you need to distinguish between types. Next, you indicate whether you want to show the preferred topics, all topics, or whether the user has clicked on a specific topic, in which case you also add the particular topic.

The message itself is a DefaultPortletMessage, sent by the PortletContext object's send() method. Notice that you've specified a particular portlet, Show Blog. This is the portlet name, as defined by the portlet-name element within the portlet-app definition in the portlet.xml file:


Listing 5: The portlet.xml file
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE portlet-app-def PUBLIC "-//IBM//DTD Portlet Application 1.1//EN" 
                      "portlet_1.1.dtd">
<portlet-app-def>
   <portlet-app uid="DCE:c334a120-8107-1211-0000-002b7dd7b90c:1">
      <portlet-app-name>BloggingSystem application</portlet-app-name>
...
      <portlet id="Portlet_3" href="WEB-INF/web.xml#Servlet_5" 
                  major-version="1" minor-version="0">
          <portlet-name>Show Blog</portlet-name>
          <cache>
              <expires>0</expires>
              <shared>no</shared>
          </cache>
          <allows>
              <maximized/>
              <minimized/>
          </allows>
          <supports>
             <markup name="html">
                <view/>
             </markup>
          </supports>
      </portlet>
...
   </portlet-app>
   <concrete-portlet-app uid="DCE:c334a120-8107-1211-0000-002b7dd7b90c:1.1">
      <portlet-app-name>BloggingSystem application</portlet-app-name>
...
      <concrete-portlet href="#Portlet_3">
         <portlet-name>Bloggingsystem Show Blog portlet</portlet-name>
         <default-locale>en</default-locale>
         <language locale="en">
             <title>Blog Entries</title>
             <title-short></title-short>
             <description></description>
             <keywords></keywords>
         </language>
      </concrete-portlet>
...
   </concrete-portlet-app>
</portlet-app-def>

The code in Listing 4 sends the message with the topic information to the Show Blog portlet, but in actuality, you want to send the message to any portlet on the page that may need it. To do that, you can just omit the portlet name:


Listing 6: Broadcasting a message
...
      try {
          DefaultPortletMessage dpm = new DefaultPortletMessage(messageText);
          getPortletConfig().getContext().send(null, dpm);
      } catch ( AccessDeniedException ade ) {
          PortletLog pLog = getPortletLog();
          pLog.error( "MessageSenderPortlet: error sending message");
      }
  } 
}

In this case, the message will be received by any portlet that is on the same page as the sending portlet -- as long as they are both part of the same portlet application.


Receiving a message

To receive the message, you need to implement the MessageListener interface and the messageReceived() method:


Listing 7: Receiving the message
package com.metrosphere.blog;

import org.apache.jetspeed.portlet.*;
import org.apache.jetspeed.portlets.*;
import org.apache.jetspeed.portlet.event.*;

public class ShowBlog extends PortletAdapter implements MessageListener, ActionListener{

  public void init(PortletConfig portletConfig) 
    throws UnavailableException
  {
    super.init( portletConfig );
  }
  
  protected static final String viewJsp = "/WEB-INF/html/showblog.jsp";
  protected static final String viewEntryJsp = "/WEB-INF/html/showblogentry.jsp";

  String outMessage = "not set";

  public void messageReceived(MessageEvent event) {

    DefaultPortletMessage message = (DefaultPortletMessage)event.getMessage();
    String messageText = message.getMessage();
   
    outMessage=messageText;        

  }

  public void doView (PortletRequest request, PortletResponse response)
     throws PortletException, java.io.IOException
  {

      response.getWriter().print(outMessage);

      PortletContext context = getPortletConfig().getContext();
      if (request.getAttribute("entrytoshow") != null){
...

Here you're simply retrieving the DefaultPortletMessage object from the message event, and then retrieving the text that makes up the actual message. At this point, you just want to make sure the message has been received, so assign it to a String variable, and then print the variable from within the doView() method. (Note that this is not an ideal way of passing information in the actual application, because only one copy of the portlet runs within the server, so the outMessage variable effectively becomes a class variable. For now, though, you can use it to see what's happening.)

Update the application, and then click a topic or the toggle link. You should see the message displayed at the top of the Show Blog portlet.


Figure 3. Viewing the message


Using the message

Now that you have the message, it's time to put it to good use. Because of the way you structured the BlogBean object, all you need to do is feed the appropriate topicId values to the loadBlog() method once you decode it:


Listing 8: Evaluating the message
...
  public void messageReceived(MessageEvent event) {
    DefaultPortletMessage message = (DefaultPortletMessage)event.getMessage();
    String messageText = message.getMessage();

    PortletRequest request = event.getRequest();
    
    if (messageText.startsWith("TOPICMSG:")){
        messageText = messageText.substring(9, messageText.length());
        if (messageText.startsWith("showtopic:")){
            messageText = messageText.substring(10, messageText.length());
            request.getSession().setAttribute("showtopic", messageText);
            request.getSession().setAttribute("topicState", "showtopic");
        } else if (messageText.startsWith("showpref:")){
            request.getSession().setAttribute("topicState", "showpref");
            messageText = messageText.substring(9, messageText.length());
            request.getSession().setAttribute("showtopic", messageText);
        } else if (messageText.equals("showall")){
            request.getSession().setAttribute("topicState", "showall");
            request.getSession().setAttribute("showtopic", "-1");
        }
    }
  }

  private String getPreftopics(PortletRequest request){
      PortletSession session = (PortletSession)request.getSession(false);
      if (session == null){
          return "-1";
      } else {
          String showtopics = (String)session.getAttribute("showtopic");
          if (showtopics == null){
              return "-1";
          } else {
              return showtopics;
          }
      }
  }

  public void doView (PortletRequest request, PortletResponse response)
     throws PortletException, java.io.IOException
  {

...
      BlogBean blogBean = new BlogBean();
      blogBean.loadBlog(getPreftopics(request));
        
      while (blogBean.next()) {
...

Note that you're using the getPreftopics() method you created for the Topic List portlet as a matter of convenience in terms of checking for an existing session. Because we've now used it twice, you might move it into the com.metrosphere.util package for easier maintenance.

Update the application on the portal and click some of the links in the Topic List portal. You should see changes to the set of entries in the Show Blog portlet.


Summary

The architecture of most sites requires one or more portlets to communicate with each other. Because sessions are individual to each portlet, you can't use them to move information from one portlet to another. Instead, you can use the action phase of one portlet to send a message either to another portlet or to any other portlet within the page that is part of the same portlet application.


Resources

About the author

Nicholas Chase

Nicholas Chase, a Studio B author, has been involved in Web site development for companies such as Lucent Technologies, Sun Microsystems, Oracle, and the Tampa Bay Buccaneers. Nick has been a high school physics teacher, a low-level radioactive waste facility manager, an online science fiction magazine editor, a multimedia engineer, and an Oracle instructor. More recently, he was the Chief Technology Officer of Site Dynamics Interactive Communications in Clearwater, Florida, and is the author of four books on Web development, including XML Primer Plus (Sams).

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=Sample IT projects
ArticleID=10295
ArticleTitle=The making of MetroSphere, Part 23: Send messages between portlets
publish-date=10232003
author1-email=
author1-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).