Unique portlet ID
Sometimes one need to retreive a unique ID for a particular portlet instance. In my case I needed it to implement configuration storage for each portlet instance independent on the user (see another post for comments on this topic).
The catch is, there is no such thing in the portlet specification. The closest you can find is the getNamespace(). The spec says “to ensure they are unique in the context of the portal page”. However, not neccessarily unique through the whole portal. At least, you don’t know until you have investigated it, for example asking the vendor or reading the code. Let’s say the mthod returns a unique ID. Now, here is the catch; you need that ID in processAction() as well!
In most cases, a portlet goes through render mode (doView) and then after the user clicks a button, which invokes processAction(). That means, in render() you can grab the ID and for example inject it into the session.
public void render(RenderRequest req, RenderResponse res) throws ... {
String ID = res.getNamespace();
//... do something with ID ...
req.getPortletSession().setAttribute("portlet.id", ID);
super.render(req, res);
}
And then pull it out again in processAction()
public void processAction(ActionRequest req, ActionResponse res) throws ... {
String ID = req.getPortletSession().getAttribute("portlet.id");
//. . .
}
So far so good, as they say. Now, consider a lazy user, which stares at the portlet page until the session times out and thenafter clicks that darn button - pow! NullPointerException.
The only solution is to figure out how the portal handles portlet IDs internally. For example, the URL might be annotated with a portlet ID after an action request. For the portal I have been working with (SiteVision) I found out that its ID can be found in the session using the following code snippet.
public String getID() {
String id = (String) getSessAttr("portlet.id");
if (id == null) {
String portletID = "" + getReqAttr("sitevision.portlet");
id = "svid" + portletID.replace('.', '_');
setSessAttr("portlet.id", id);
}
return id;
}
I have to emphasize this again: this is not portal independet code.
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.
