Friday, November 16, 2007

How to scope Portlet data per Window

I have been often asked how it is possible to scope data in a Portlet for a specific window. One obvious way would be to generate some unique ID and store it in the Portlet preferences, however this means that we start to use the preferences of the Portlet as a database which is not advised by the specification. So it would be neat to be able to retrieve the window ID provided by the portal to the portlet container.



Since Portlet 2.0 it is possible to retrieve the window ID using the PortletRequest.getWindowID(). Still there is a way to have this window id value using the Portlet 1.0 specification, it may sound like an hack but it respects the specification. The trick is to use the fact that the Portlet session attributes are scoped using the window id value, for instance if the Portlet put the value foo in the Portlet session, it will be stored as javax.portlet.p.XYZ?foo where the XYZ is the window id.



Here is how to do it in practice:




public class WindowIDPortlet extends GenericPortlet
{

protected void doView(RenderRequest request, RenderResponse response) throws PortletException, PortletSecurityException, IOException
{
PortletSession session = request.getPortletSession();
WindowIDRetriever retriever = (WindowIDRetriever)session.getAttribute("retriever");
if (retriever == null)
{
retriever = new WindowIDRetriever();
session.setAttribute("retriever", retriever);
}
String windowID = retriever.getWindowID();

//
response.setContentType("text/html");
PrintWriter writer = response.getWriter();
writer.print("Window ID is equals to " + windowID);
}

public static class WindowIDRetriever implements HttpSessionBindingListener
{

/** . */
private String windowID;

public void valueBound(HttpSessionBindingEvent event)
{
String name = event.getName();
windowID = name.substring("javax.portlet.p.".length(), name.indexOf('?'));
}

public void valueUnbound(HttpSessionBindingEvent event)
{
}

public String getWindowID()
{
return windowID;
}
}
}




What you need to pay attention to is the fact that if you use the window id value as a key in a cache then it will be fine, however if you start to persist data using the window id as a key then as you will not be aware of the associated window destruction and you will not be able to remove associated data in the database when the portal destroys the window. In that case the best advice is probably to implement a purge mechanism that would remove the out dated entries in the database (which suppose that you associate with the records, the date at which it was inserted).

2 comments:

Unknown said...

I've tried your code with the HelloWorldPortlet example. Unfortunately, the reported window ID doesn't change for the different windows, but it is always the same: "HelloWorldPortletInstance.HelloWorldPortletInstance". It seems to be an instance Id, rather than a window Id.
What did I get wrong?

Julien Viet said...

We are aware of that and it has been fixed in our code base and will be in the 2.6.3 release which should be done next week.