http://www.developer.com/

Back to article

Portal Federation with WebLogic Portal WRSP: Advanced IPC Techniques


May 28, 2008

Managing Session Size

In Part 2 of this series, you created a session object to share data between your remote portlets running on the WebLogic Portal (WLP). Objects in the session are useful when you will need them for an extended period of time; otherwise, you would have to annoy the user repeatedly for the same information. As portals (or any web-based application) grow in size and complexity, it quickly becomes an effort to achieve acceptable performance. One way to improve performance is by keeping the session small, with only the objects that are necessary stored there. This is the time to check your session objects and ask yourself "do I really need this in the session?" Portals that use WSRP can fill the session quickly when objects are placed in session just to share them when they are only used on a request level. Because of your need for speed, take a look at how to orchestrate the way you share your objects using the session just long enough to achieve communication.

The WebLogic Portal includes an Event framework that is ideal for inter-portlet communication (IPC) because it provides APIs to handle such communications in a loosely coupled fashion. For example, say that a user enters a value in portlet A and, on submission, portlet B needs to get that value for the purpose of a single update. Although in the example from Part 2, you used a session object to store the value, this time you will follow the WebLogic best practice of utilizing the Event framework for IPC. Start by firing a custom event in portlet A and then attaching your value to the Event:

public Forward ipcDemoCall1(IpcDemo1FormBean form)
{
   Forward                   forward          = null;
   PortletBackingContext     context          = null;
   HttpServletRequest        outerRequest     = null;

   forward          = new Forward("success");
   outerRequest     = ScopedServletUtils.getOuterRequest
                      (getRequest());
   context          = PortletBackingContext.
                      getPortletBackingContext(outerRequest);

   context.fireCustomEvent("loadIpcPortletB", form);
   return forward;
}

Next, you will have portlet B listen for your custom event:

<netuix:handleEvent event="loadIpcPortletB"
                    eventLabel="handleEvent1"
   fromSelfInstanceOnly="false" onlyIfDisplayed="false"
                                sourceDefinitionWildcard="any">

The values above represent the custom event fired from portlet A, an event label that must be unique to avoid headaches. The two "false" values allow you to listen for the event from other portlets no matter where the listening portlet is displayed, and to listen for this event from any portlet (more on the usefulness of this later).

.portlet files are far easier to configure reliably using the portal IDE (Workshop or Workspace Studio, depending on the WLP version) and the portlet property editor in the Portal perspective. In this article, you have looked at the actual XML both to improve understanding and because it would take way too many screen shots to illustrate the full flow. The values in the XML example above combined with the intuitive Workshop/WorkSpace portal development interface should get you through doing this the easy way.

Were you not using WSRP, the payload parameter of fireCustomEvent (getRequest().getParameter(IPC_VALUE));) would be readily available to the listening portlet. In a WSRP scenario, the firing portlet (A) does not share the same request object as the listening portlet (B), so you need to add a backing file to portlet B that has access to both the Event and the session:

package article.examples.portal.util.wsrp.backing;

import com.bea.netuix.servlets.controls.content.backing.
   AbstractJspBacking;
import com.bea.netuix.servlets.controls.portlet.backing.
   PortletBackingContext;
import org.apache.beehive.netui.pageflow.scoping.
   ScopedServletUtils;
import com.bea.netuix.events.Event;
import com.bea.netuix.events.CustomEvent;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class IpcEventHandler extends AbstractJspBacking
{
   static final long serialVersionUID=1L;
   public static final String IPC_DATA = ".data";
   public void handlePayloadEvent(HttpServletRequest request,
      HttpServletResponse response, Event event)
   {
      CustomEvent customEvent                = null;
      HttpServletRequest        outerRequest = null;
      HttpSession               outerSession = null;
      Object                    payLoad      = null;
      PortletBackingContext     pbc          = null;
      String                    payLoadId    = null;

      customEvent      = (CustomEvent)event;
      outerRequest     = ScopedServletUtils.getOuterRequest
                         (request);
      outerSession     = outerRequest.getSession();
      pbc              = PortletBackingContext.
                         getPortletBackingContext(request);

      if(customEvent.getPayload()!=null)
      {
         payLoad       = customEvent.getPayload();
         payLoadId     = pbc.getInstanceId()+IPC_DATA;
      }
      if(payLoad!=null)
      {
         outerSession.setAttribute(payLoadId, payLoad);
      }
   }
}

This works because the event framework has access to requests from portlet A even though portlet B does not. To help out the portlet, your backing file takes the event payload and places it in the session. This is done using the portlet ID as part of the session key so that this backing file can be reused in any portlet, even by multiple instances of the same portlet.

Admittedly, portlet A could simply have put the value into the session, although doing so would create a tighter coupling between the two portlets. Also, portlet A would put the value in the session regardless of whether there were a portlet that needed it, which would defeat your goal of managing the session.

Once portlet B accesses the data from the session, it can store it in its local scope and safely remove it from the session by calling removeAttribute() on the session.

Abstracting WSRP IPC Session Management

For a small project, having the listener manage the session object directly is both an adequate and acceptable solution. In the case of larger projects (where there will be more developers and/or more IPC over WSRP), it is probably going to be advantageous to have a more generic approach. You may have noticed that your backing file is abstract enough where it can be attached to any portlet listening for any event. You can have this same level of abstraction in the way you retrieve the value. To that end, you will build a light-weight handler to get the value from the on request from the listening portlet, and then remove the value from the session. Such a helper class would look like this:

package article.examples.portal.util.wsrp;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.apache.beehive.netui.pageflow.scoping.
   ScopedServletUtils;
import com.bea.netuix.servlets.controls.portlet.backing.
   PortletBackingContext;

public class IpcDataHandler
{
   public static final String IPC_DATA = ".data";
   public static Object getIpcData(HttpServletRequest request)
   {
      HttpServletRequest        outerRequest     = null;
      HttpSession               outerSession     = null;
      Object                    ipcDataObj       = null;
      PortletBackingContext     pbc              = null;
      outerRequest     = ScopedServletUtils.
                         getOuterRequest(request);
      outerSession     = outerRequest.getSession();
      pbc              = PortletBackingContext.
                         getPortletBackingContext(request);
      ipcDataObj       = outerSession.getAttribute
                         (pbc.getInstanceId()+IPC_DATA);

      if(ipcDataObj!=null)
      {
         outerSession.removeAttribute
            (pbc.getInstanceId()+IPC_DATA);
      }

      return ipcDataObj;
   }
}

You then can use this helper in portlet B as follows:

Forward              forward      = null;
IpcDemo1FormBean     ipcValue     = null;

ipcValue = (IpcDemo1FormBean)IpcDataHandler.
   getIpcData(getRequest());


if(ipcValue!=null)
{
   forward = new Forward("success", ipcValue);
}
else
{
   forward = new Forward("success");
}
return forward;

By using the above approach, the portlet can continue to work on a request level rather than having to access the session directly.

Look, Ma, No Forms!

There are times when a form submission is not the desired UI design, such as tables of data in portlet A where your listening portlet is only interested in one line or a single value (one example of this would be Yahoo! Or Google Finance in a portfolio view). In this case, the Netui tags provide a convenient way to pass these values without a form:

<netui:anchor action="ipcDemoCall2">
   <netui:parameter name="ipcValue" value="Example 1"/>
      IPC Link Example 1
</netui:anchor>

The ipcDemoCall2 action in portlet A then puts the value into an event as follows:

import com.bea.netuix.servlets.controls.portlet.backing.
   PortletBackingContext;
   private static final String IPC_VALUE = "ipcValue";

   @Jpf.Action(forwards = { @Jpf.Forward(name = "success",
      path = "index.jsp") })
   public Forward ipcDemoCall2()
   {
      Forward forward = new Forward("success");
      PortletBackingContext     context          = null;
      HttpServletRequest        outerRequest     = null;
      outerRequest = ScopedServletUtils.
                     getOuterRequest(getRequest());
      context      = PortletBackingContext.
                     getPortletBackingContext(outerRequest);
      context.fireCustomEvent("loadIpcExample2B",
         getRequest().getParameter(IPC_VALUE));
      return forward;
   }

In this case, the value is simply a String rather than an object. Fortunately, you have created your generic helper to not care what kind of object is packed into the payload, so you can continue to pick up the value in the same way you handled it earlier.

Page Switching with WSRP

Earlier, you set up your event listening so that it would not matter whether the listening portlet was displayed or not:

<netuix:handleEvent event=
   "loadIpcPortletB" eventLabel="handleEvent1"

fromSelfInstanceOnly="false" onlyIfDisplayed="false"
   sourceDefinitionWildcard="any">

The advantage to this is if you want to have the listening portlet on a different page and have the user taken to that page as part of the interaction (a handy user experience design to keep from cluttering the page). Although in a file-based portal you can create this type of interaction by directly calling the page where the listening portlet resides, in a WSRP interaction you don't even know where that page is. A direct link won't work with WSRP both because it is administered in a separate portal application and because the instance ID of all WSRP portlets, pages, and books are generated at the time they are integrated into the consumer portal (I am purposely avoiding the debate of whether that should be done even without WSRP, in case you were wondering). Instead of generating links in code, you add a step to your event handling that will dynamically load the page in which the listening portlet resides. This is achieved by going to the portlet properties as you did before and adding the Activate Page action (this time with the screen shot because one picture is adequate to explain):



Click here for a larger image.

Figure 1: Adding Activate Page to Event Handling

Conclusion

There are many ways to share data between portlets. Oftentimes, portal developers never explore the IPC tools provided by the WebLogic Portal, either because they haven't needed to or did not have the time explore the documentation thoroughly enough. Those with more time on their hands have been able to discover that there are many robust APIs that facilitate developing a loosely coupled portal application. Even though the advantages to doing so are very clear when demonstrated in the WSRP context, they are of benefit even without WSRP.

There are other approaches to achieving the same results, as demonstrated in this series. Many of those approaches are provided in the documentation. The goal here was to share approaches that are not as easily found and provide some real-world context around the value of learning the advanced features of WLP. Although you will certainly be able to get by with what you have covered, you will be able to make the best choice for your project by reviewing the documentation in addition to these articles.

About the Author

Scott Nelson is a Senior Principal Consultant who strives to leverage portal technologies to provide businesses an improved ROI realized from a better user experience and reduced developer maintenance. In other words, he likes to do things better, faster, and cheaper.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date