08/10/2013

[Java] Store session attributes in portlet controller and retrieve them from JSP

I was coding a portlet following the JSR-168 specification and Spring 2 and I noticed an inconsistent behaviour on some pages. Passing some Map objects, needed to initialize the JSP drop-down menus, by storing them in session in the controller and retrieving them in the JSP, would work always except when the page was reloaded after a failed form validation during the submission phase.

Note: this issue could not be solved by simply using the ModelAndView addObject("name", value) method in the controller and then retrieving the object in the JSP with ${name} outside of a Java block, since I needed to perform some actions on those values directly from the JSP, therefore inside a Java block, without using EL.
The first thing I tried was to store these objects in session in the render method of my controller and get them back from inside my JSP. In the controller I would do:

PortletSession session = request.getPortletSession();
session.setAttribute("name", value);

and in the JSP:

Map myMap = (Map)request.getAttribute("name");

This works but not in my precise case: reloading the page after a failed form validation on submission. What happens after reloading is that request.getAttribute("name") returns null. With this exact setting, there was no way to get back that same object from the JSP, even though it was still there.
I discovered that, in fact, my object was still being stored in session but not under the name name, it was instead named something like javax.portlet.p.5_8_11D?name. I could see this by simply doing:

Enumeration enuma = session.getAttributeNames();
while (enuma .hasMoreElements()) {
    System.out.println(enuma .nextElement());
}

And I couldn't understand the reason for that strange name. Turns out, there are two scopes for session attributes inside a PortletSession: APPLICATION_SCOPE and PORTLET_SCOPE; described as
 All objects stored in the session using the APPLICATION_SCOPE must be available to all the portlets, servlets and JSPs that belongs to the same portlet application and that handles a request identified as being a part of the same session. Objects stored in the session using the PORTLET_SCOPE must be available to the portlet during requests for the same portlet window that the objects where stored from. Attributes stored in the PORTLET_SCOPE are not protected from other web components of the portlet application. They are just conveniently namespaced.

Furthermore, the default scope when calling the setAttibute("name", value) method is PORTLET_SCOPE.

To solve the issue, I converted my calls to:

session.setAttribute("name",value,PortletSession.APPLICATION_SCOPE);

inside the controllers, and to:

Map myMap = (Map)request.getSession().getAttribute("name");

in the JSPs.

This works, but introduces a little caveat in the controllers since now, for the objects stored in session that way, the correct getAttribute call requires the scope to be explicitly declared:

Map myMap = (Map) session.getAttribute("name",PortletSession.APPLICATION_SCOPE);

No comments:

Post a Comment

With great power comes great responsibility