Application_Scope versus Portlet_Scope: Understanding the JSR 286 PortletSession
Managing state through the PortletSession is a supremely important job for the portal developer, and knowing how to manage the session means understanding the difference between the PortletSession scopes: APPLICATION_SCOPE and PORTLET_SCOPE.
The following tutorial is the second of two about the PortletSession, written by Sal Pece.The first one is called Understanding the PortletSession. This tutorial on the PortletSession is one in a full series of tutorials, CBTs and tips designed to help Java professionals learn the JSR286 Portlet 2.0 API quickly and effectively. For a full listing of learning resources, see the Portlet 2.0 Development Tutorial and Technology Guide.
When a portlet is placed on a portal page, the data stored in the PortletSession of this portlet is not shared with any other portlets on the page, or anywhere else in the portal. This default behavior is known as PORTLET_SCOPE, and has been the way the PortletSession object has behaved for years. However, developers have done nothing but moan and groan about the fact that it is incredibly difficult to share information between portlets that exist within the same portlet application (think WAR file), so with the JSR-168 portlet specification, the Java Gods introduced the concept of a shared, APPLICATION_SCOPE, for the PortletSession, which is a concept that has carried on in to JSR-286 as well. When a portlet shoves an object into the PortletSession, and specifies APPLICATION_SCOPE as the visibility of the data, any portlet that is part of that same portlet application can obtain that data by providing the appropriate key when making a call to the PortletSession's getAttribute method.
TheJSR-268 API defines two constants, or should I say final static variables, in the PortletSession class. These two constants are named APPLICATION_SCOPE and PORTLET_SCOPE, and represent the numbers 0x01 and 0x02 respectively.
PortletSession.APPLICATION_SCOPE //0x01 PortletSession.PORTLET_SCOPE //0x02
Newly Overloaded PortletSession Methods
In a typical Servlet and JSP based application, along with legacy JetSpeed and proprietary Portlet APIs, the setAttribute method is used to save data to the PortletSession. The setAttribute method takes a name/value-object pair as arguments. Data is pulled out of the session with a call to getAttribute, with the name provided as a parameter; the associated value-object is then returned to the calling program. However, in the JSR-268 Portlet API, the setAttribute and getAttribute methods have been overloaded, with an extra parameter added that allows for one of the two new portlet scopes to be specified.
/* default mechanism for adding to a PortletSession */ session.setAttribute(“key”, “value”); /* overloaded mechanism for adding to a PortletSession*/ session.setAttribute(“key”, “value”, PortletSession.PORTLET_SCOPE); /*placing data into the APPLICATION_SCOPE of a PortletSession*/ session.setAttribute(“key”, “value”, PortletSession.APPLICATION_SCOPE); /* default mechanism for pulling from a PortletSession */ session.getAttribute(“key”); /* overloaded mechanism for pulling from a PortletSession*/ session.setAttribute(“key”, PortletSession.PORTLET_SCOPE); /*pulling data from the APPLICATION_SCOPE*/ session.setAttribute(“key”, PortletSession.APPLICATION_SCOPE);
PortletSession and the APPLICATION_SCOPE
When data placed into the PortletSession using the APPLICATION_SCOPE, the data is visible to all other portlets that are packaged as part of the same portlet application. Portlets are part of the same portlet application when they are defined in a common portlet.xml file, and packaged in a common WAR file.
Many developers believe that data stored in the APPLICATION_SCOPE of the PortletSession is available to all of the portlets a users might access through the portal. This is simply not the case. While APPLICATION_SCOPE allows a degree of data sharing, the data is not shared with portlets that are defined in separate WAR files. All portlets sharing APPLICATION_SCOPE data must be defined in a common portlet.xml file.
Class diagram of the PortletSession
"The PortletSession interface provides a way to identify a user across more than one request and to store transient information about that user.
A PortletSession is created per user client per portlet application.
A portlet can bind an object attribute into a PortletSession by name. The PortletSession interface defines two scopes for storing objects:
APPLICATION_SCOPE
PORTLET_SCOPE
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.
The portlet session is based on the HttpSession. Therefore all HttpSession listeners do apply to the portlet session and attributes set in the portlet session are visible in the HttpSession and vice versa."
-Description of the PortletSession from the IBM/Sun Portal API JavaDocs
Reading from the APPLICATION_SCOPE
Our SimpleSessionPortlet, defined in Fig. 5-1, places a hitcount into the session using the key timesvisited. To allow another portlet to access this hitcount, we could change the scope of the timesvisited key to APPLICATION_SCOPE, and have a second portlet, which we will name SessionViewerPortlet, pull the key out of the APPLICATION_SCOPE.
package com.mcnz.portlet; import java.io.*; import javax.portlet.*; public class SessionViewerPortlet extends GenericPortlet { protected void doView (RenderRequest request, RenderResponse response) throws PortletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); PortletSession session = request.getPortletSession(); Object count = session.getAttribute("timesvisited", PortletSession.APPLICATION_SCOPE); if (count != null) { out.print("I found the count in the session: "); out.print(count.toString()); } else { out.print("Couldn't find the count in the session."); } } }
While the SessionViewerPortlet looks for the timesvisited key in the APPLICATION_SCOPE, the lookup will be unsuccessful until the SimpleSessionPortlet explicitly places the timesvisited key in the appropriate scope, thus the conditional check on the count being null.
package com.mcnz.portlet; import java.io.*;import javax.portlet.*; public class SimpleSessionPortlet extends GenericPortlet { protected void doView(RenderRequest request, RenderResponse response) throws PortletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); PortletSession session = request.getPortletSession(); String visitCount = (String)session.getAttribute( "timesvisited", PortletSession.APPLICATION_SCOPE); if (visitCount == null) { /*if this is the first time viewing the portlet, set the timevisited value to 1*/ session.setAttribute("timesvisited", "" + 1, PortletSession.APPLICATION_SCOPE); out.print("Welcome to our little portlet!"); out.print("Click refresh, minimize or maximize!!!"); } else { int newCount = Integer.parseInt(visitCount) + 1; session.setAttribute("timesvisited", "" + newCount, PortletSession.APPLICATION_SCOPE); out.print("Number of times visiting this portlet: "); out.print(newCount); } } }
Now that the key named timesvisited is stored in the scope of the application, both the SessionViewerPortlet and the SimpleSessionPortlet can share information through the PortletSession. But notice how the two portlets are out of sync, with one showing a count of 2, and the other showing a count of 3? Sadly, things do always go as planned when your application relies on the doView methods of various portlets to be processed in a certain order. This very issue will lead us into the next lesson on the action processing phase of a portlet. But for now, you can at least be happy that the two portlets are successfully sharing information through the PortletSession.