Inter portlet communication
Writing a portlet is like writing a web application, although the scope is smaller, page rendering limited to a minor area of a web site and you are usually in the dark of the semantics of the portal you will be deploying to. Although not all is lost, there are some fun as well.
I recently completed a portlet project, which involved three different portlets with specific roles. The first assembled search criteria, the second viewed query resuls using a table and the third is intended to view details of one found object. No rocket science here.
The problem pops up when these portlets needs to communicate. Surprisingly, there is currently no standard mechanism for this. The Portlet 1.0 specification (JSR 168) from October 2003, leaves some to desire for practical portlet development, enabling various portal server implementors to fill in the blanks or just leave it to the poor developer to find out. This summer (June 2008) version 2.0 of the spec (JSR 286) was finalized and it’s primary feature is portlet events. It remains to see how long it will take before the spec will be implemented on major plattforms.
Back to the problem; at the time I still had to solve it using a less world-wide known portal called SiteVision. It is a CMS with a bare-bone portlet container implementation.
A portlet is either rendering (render) or serving a form request (action) and at most one portlet per request can serve an action. Rendering means a portlet emits HTML - somehow. The idea is that one portlet on a page receives request parameters, processes them and then renders, followed by all other portlets rendering. This works as long as just the first portlet need to perform actions.
The key to the solution is to use the session. A portlet has access to two types of sessions; one private to the portlet instance (and user) called PORTLET_SCOPE and one common to all portlets called APPLICATION_SCOPE. Behind the scene both are part of the HttpSession using various key prefix trixs. For our problem, the app session is clearly the one to use.
The solution is straight forward, put an object in the app session at the sending side and pick it up at the receipment side. If you only have one pair of portlets communicting, choose a common session key name and there you go. On the other hand, if you need to tie two arbitrary portlets together, perhaps via user configuration, you quickly find you need a helper class that takes care of the details. The class PortletMessenger below wraps a session object and provides put() and get() methods.
public class PortletMessenger {
private PortletSession session;
private String topicName = PortletMessenger.class.getName();
public PortletMessenger(PortletRequest request) {
this.session = request.getPortletSession();
}
public PortletMessenger withTopic(String name) {
if (name != null && name.length() > 0) topicName = name;
return this;
}
public PortletMessenger put(String messageName, Object value) {
if (!(value instanceof Serializable)) throw new RuntimeException(...);
this.session.setAttribute(createKey(messageName), value, PortletSession.APPLICATION_SCOPE);
return this;
}
public Object get(String messageName) {
String name = createKey(messageName);
Object value = session.getAttribute(name, PortletSession.APPLICATION_SCOPE);
session.removeAttribute(name, PortletSession.APPLICATION_SCOPE);
return value;
}
protected String createKey(String msgName) {
return topicName + ':' + msgName;
}
}
You send a portlet message like this
PortletMessenger msgr = new PortletMessenger( actionRequest ).withTopic("myTopic");
msgr.put("myMessageName", myMsg);
And receives the message somewhere else, like this
PortletMessenger msgr = new PortletMessenger( renderRequest ).withTopic("myTopic");
Object msg = msgr.get("myMessageName");
The topic name serves as a namespace - strictly not necessary, but convenient in a large application. The message name is the identifier and must be known on both sides. An alternative implementation might use a (short) queue, instead of the single slot queue I’m using. However, that is only needed if several portlets might send to a single destination. All action and rendering per request, happens on the same thread and as long a message is produced and consumed within the same request, queueing is not needed.
There is one catch to understand and that is the message is sent inside one processAction() at the sending portlet and received within doView() at the receiving portlet.
