Portlets are Java-based Web components that process requests and generate dynamic content. This portlet generated content is called a fragment, which is a piece of markup (such as HTML, XHTML, WML) adhering to certain rules. The fragment can be aggregated with other fragments to form a complete document, called a portal page.
In order to generate this markup, a very common pattern calls for portlets accessing backend systems in order to retrieve data, such as customer information or stock quotes. The data is presented to the end user through the portlet markup. Often the access to the backend system has some latency and does not change frequently. In these cases, caching the backend data in portlets can provide better response times for the end user and better scalability of the entire portal.
The JSR 168 Java Portlet Specification V1.0 does not define any caching APIs; the expert group considered caching to be a broader J2EE topic and not a portlet-specific issue. J2EE currently does not define such a caching API; therefore, you must use vendor specific APIs for caching.
In this article, we explain how you can use the IBM® WebSphere® Application Server dynacache caching infrastructure and how to generate caching IDs for two use cases:
- Caching data which is user session specific and could be shared between portlets
- Caching data which is portlet window specific and should not be shared
Finally, we use both caching strategies in a bookmark sample portlet. This article and the samples apply to IBM WebSphere Portal V5.1 (or higher) and to IBM WebSphere Application server V5.1 (or higher).
You can download the sample code for the bookmark example, and refer to it as you read the rest of this article. For a detailed description of the sample, see Example: Bookmark portlet with caching.
Comparing caching to the portlet session
With no caching API available in the JSR 168 Portlet API, many portlet developers use the portlet session in order to cache data. At first, this might seem to be a good fit, because the Portlet API provides you with two different scopes: an application wide scope and a portlet window scope. So, you would think that using the session could satisfy the two use cases described in the Introduction.
However, using the session for storing cache related data has some severe drawbacks. Session and cache data differ in the following ways:
- Session data is that which has a lifetime of the user session, and is created based on actions the user performed that translate to processAction calls on the portlet. The portlet should only modify session data in the action phase; the render phase should be idempotent; that is, it should not change the state of any data. The session data also needs to be replicated to other cluster nodes, in case the original cluster node goes down (failover scenario). An example scenario when session data is appropriate is the shopping cart on an internet site. You add items to your shopping cart while logged in to the Web site, and then you confirm the content and submit it for processing.
- Cache data is that which can be re-created at any point in time. This data is stored in order to optimize performance, because the backend system that stores the original data might be slow, or there is a slow or unstable connection to that backend system. An example when cache data would be appropriate is a customer record, including the address of the customer, stored in a backend system.
Therefore, implementing a cache store that does nothing is a valid implementation, because the client of the cache is always able (and always needs to expect) to re-fetch the data from the backend system. This is not true for the session implemention. Your shopping experience would be quite frustrating (but might save you some money) if none of the selected items were stored.
These fundamental differences also have implications for using these different data stores. If you use the session for storing cache data, you need to take the following disadvantages into account:
- Memory consumption cannot be managed.
Session data needs to be kept, so the system cannot discard any data when running low on memory. If you were to store the data in a real cache, then the system could discard parts of the cache to gain memory for other tasks because the client can always re-create the cached data.
- Lifetime of the data is bound to the session lifetime.
Cache data often has a shorter lifetime that could be configured if the data were stored in a real cache.
So by now, we have hopefully convinced you that using a real cache API for cache data, instead of the session, is something that gives you real benefits in performance and scalability. Now, let’s take a look at how you can access the cache APIs provided with WebSphere Application Server.
Leveraging the WebSphere dynacache infrastructure
There are different flavors of the dynacache infrastructure within WebSphere Application Server. For this scenario, we are interested in the dynamic caching service. You can use it to store objects in a distributed fashion across a cluster. We will use it to store objects that might have previously been stored in the session.
First, you need to enable the dynamic cache service. In the administrative console for Websphere Application Server:
- Navigate to Servers => Application servers => server_name => Container services => Dynamic cache service.
- Enable the service by selecting Enable service at startup.
- Apply the changes.
- Restart the server.
Figure 1 shows how this screen would look like after you have enabled the dynamic cache service.
Figure 1: Enabled Dynamic Cache Service
The Java APIs that WebSphere Application Server provides to store objects in a distributed fashion are called DistributedMap and DistributedObjectCache. You use these interfaces to cache and share Java objects by storing a reference to the object in the cache in any J2EE application or system component.
When you enable the dynamic cache and restart the server (as described above) WebSphere Application Server creates a default dynamic cache instance. This default instance is bound to the global Java Naming and Directory Interface (JNDI) namespace using the name
services/cache/distributedmap.
One big advantage of the distributed map is that the J2EE application developer can decide to create new instances on the fly. This capability of multiple instance enables you to separately configure cache instances as needed. Each instance of the DistributedMap interface has its own properties that you can set using Object cache instance settings. You can make the settings either through the programmatic API or through the administrative console at Resources > Cache instances > Object cache instances. For more flexibility, use the programmatic API which we use in this article to create the cache instance. See the WebSphere Application Server Information Center for more information.
To use the programmatic API, create a properties file called cachinstances.properties in the classpath of your J2EE application; for example,
/classes/cacheinstance.properties.
Listing 1 shows the format of the file for the bookmark portlet example.
Listing 1. Bookmark portlet cachinstances.properties file
cache.instance.0=/services/cache/bookmark/statistics cache.instance.0.cacheSize=1000 cache.instance.0.enableDiskOffload=false cache.instance.0.flushToDiskOnStop=false cache.instance.0.useListenerContext=true cache.instance.0.enableCacheReplication=true cache.instance.0.replicationDomain=DynaCacheCluster cache.instance.0.disableDependencyId=true |
Then, in your Java code, you can access the distributed map as shown below.
InitialContext ic = new InitialContext();
DistributedMap map = (DistributedMap)ic.lookup
("services/cache/bookmark/statistics");
|
Now that you know how to cache data using the dynacache infrastructure, let's look at how you can generate the correct cache key in a portlet. In this section, you see how to generate keys that let you store data for each user session. In this case, the data is shared among all components with which the user interacts.
There are numerous use cases for caching by user session, and they are often based on different components operating on the same backend data. Therefore, it is useful to get the data only once, cache it, and have all other components use the cached data, instead of accessing the backend system again. This sharing of cached data does not only apply to portlets within one portlet application, but also includes other portlet applications, and to themes and skins. This sharing is possible because WebSphere Application Server assigns the same session ID to all Web application sessions connected to a specific user.
In order to make the generation of the cache keys reusable for individual projects, separate the key generation into its own class. In this example, the class is called PortletObjectCacheHelper and consists of a method getKey() that you can call with the two portlet session scopes: application or portlet. Just as you would use the application scope to store data in the portlet session so that it is visible to other components in the portlet application, you would also use application scope to generate a key that is shared across components for the current user. Listing 1 shows how to implement key generation for session-wide keys.
Lising 2: Generation of a session specific caching key based on the session id.
public static String getKey(String key, PortletSession session, int scope) {
if (scope == PortletSession.APPLICATION_SCOPE) {
return getKey(key, session.getId());
} else ...
|
The getKey method constructs a cache key which includes the custom application key and the session id. Listing 2 shows a sample algorithm which creates such a key by string concatenation.
Listing 3: String concatenation for producing a single cache key out of the diffferent keys and IDs.
public static String getKey(String key, String sessionId) {
return getKey(key, sessionId, null);
}
public static String getKey(String key, String sessionId,
String windowId) {
final StringBuffer _key = new StringBuffer(key.length() + 50);
_key.append(sessionId);
_key.append('.'); // not required, added to show the key contruction
if (windowId != null) {
_key.append(windowId);
_key.append('.'); // not required
}
_key.append(key);
return _key.toString();
}
|
Now that you know how to create a session-based cache key, let's look into generating a cache that is unique for a portlet window in the next section.
Caching data by portlet window
The second use case involves caching data which is only relevant for one portlet instance, and the data is specific to a portlet window. So, you might put a stock quote portlet twice on a page to display different stock quotes, and these two different instances of the same portlet would create two separate cache entries, so that one does not overwrite the cache entry of the other.
However, creating a cache ID that is unique for the portlet window is not as easy as using the session ID in the first use case because the Java Portlet Specification does not provide any direct means to access this ID. There are two ways you could get a portlet window cache ID in the JSR 168 API:
- getNamespace() on the RenderResponse
- session namespacing
The getNamespace() method provides you with a unique ID for each portlet window, and, therefore, seems to be the perfect solution. However, there are two issues with this solution:
- The getNamespace() method is only available in the render phase.
You cannot access the ID in the action phase or in any session listeners.
- The Java Portlet Specification only guarantees that this value stays constant for one request.
WebSphere Portal goes beyond this requirement and guarantees that the getNamespace value stays constant for the current user session.
The advantage of using the getNamespace value is that it is a simple API call and it is very efficient. If you can live with accessing your cached data only in the render phase, we suggest to you use the getNamespace method. From a programming model point of view, it is the prefered solution. You should normally only write to the backend system in the action phase, and read from the backend system in the render phase. This pattern also helps to avoid any deadlocks in case the backend system uses transactions.
The other alternative is to leverage the defined namespacing mechanism of the Java Portlet Specification for keys that are stored in the portlet session in the portlet scope. The specification requires that you prefix the key by a unique ID for a specific portlet window.
The key generation sample in Listing 4 extracts the prefixed ID by writing a well-known key into the portlet scope, and then reads all values from the portlet session in the un-prefixed application scope and searches for the well-known key. Because the specification describes how the prefixing should be done, we can extract the prefix again and have the unique portlet window ID. You can get to this ID only by having access to the portlet session, so you can access it in the action phase, render phase, and session listeners. The drawback of this approach is that it needs to write something to the session, so you must have a session. This write operation also has some performance penalty. This ID could be different from the getNamespace value mentioned above.
Listing 4: Generating a portlet window specific cache key, using the portlet scope session prefixing defined in the Java Portlet Specification
final private static String CACHE_CONST = "__cache__";
final private static int len1 = "javax.portlet.".length();
final private static int len2 = CACHE_CONST.length();
public static String getKey(String key, PortletSession session,
int scope) {
if ...
} else if (scope == PortletSession.PORTLET_SCOPE) {
synchronized (session) {
String id = null;
session.setAttribute(CACHE_CONST, "");
final Enumeration e = session.getAttributeNames(PortletSession.APPLICATION_SCOPE);
while (e.hasMoreElements()) {
final Object object = e.nextElement();
if (object instanceof java.lang.String) {
final String name = (String)object;
if (name.indexOf(CACHE_CONST)>=0) {
id = name.substring(len1, name.length()-len1-len2);
break;
}
}
}
session.removeAttribute(CACHE_CONST);
return getKey(key, session.getId(), id);
}
}
|
Now you can generate the different cache keys for the session and portlet window scope. Next, let's look at an example that uses both key generation mechanisms and stores the data using the dynacache infrastructure.
Example: Bookmark portlet with caching
Our example bookmark portlet in the download shows how to use the caching functionality within a portlet. Even though it is a very simple portlet, there are some parts of an application that always make sense to put into a cache, such as re-creatable information or non-persistent information that does not need to persist forever.
In this example we created a Statistics class to show the user how often he or she added, modified, or deleted a bookmark. This information is available for both the private session and application session. The statistics sample is not a perfect fit for caching because the data is not re-creatable, which is required for cached data. However, we chose this example to keep the sample code simple and to concentrate on how to use caching. Better samples for real-world applications would be to cache the title or icon of the page targeted by the bookmark. The portlet adds the statistics section at the bottom of the view as shown in Figure 2.
Figure 2. Bookmark portlet includes user statistics
The Statistics class consists of two parts. The first part defines a couple of static methods which are general access methods to handle the cache. One retrieves the statistic scoped by HTTP session, and the other one is scoped by portlet session. The following listing shows the code:
LIisting 5: Statistics class defining access methods
public static Statistics getApplicationStatistics(PortletSession session)
{
return getStatistics(PortletObjectCacheHelper.getKey(
STATISTICS,session,PortletSession.APPLICATION_SCOPE));
}
public static Statistics getPrivateStatistics(PortletSession session)
{
return getStatistics(PortletObjectCacheHelper.getKey(
STATISTICS,session,PortletSession.PORTLET_SCOPE));
}
private synchronized static Statistics getStatistics(String key)
{
Statistics stat = (Statistics)cache.get(key);
if (stat==null)
{
stat = new Statistics();
cache.put(key,stat);
}
return stat;
}
|
The second part is a very simple statistics implementation which increases counters for additions, modifications, and deletions.
LIisting 6: Statistics class implementing simple statistics
private int countModifications = 0;
private int countAdditions = 0;
private int countDeletions = 0;
public Statistics()
{
}
public void incModifications()
{
countModifications++;
}
public void incAdditions()
{
countAdditions++;
}
public void incDeletions()
{
countDeletions++;
}
public int getModifications()
{
return countModifications;
}
public int getAdditions()
{
return countAdditions;
}
public int getDeletions()
{
return countDeletions;
}
|
Caching of data is not defined in Java Portlet Specification V1.0; therefore, you need to leverage vendor extensions for this functionaltity. We showed how to use the WebSphere Application Server dynacache infrastructure in order to cache data in portlets. We also explained how to generate cache keys for the two use cases session scope and portlet window scope.
Generating session scope cache keys is simple and cheap because the session provides you with a specific ID that can be leveraged. WebSphere Application Server makes this session ID the same across Web applications for a single user; therefore, cache content can be shared across applications.
Cache keys that are private to a portlet, and thus scoped to the portlet window, are not that easy to generate because there is no explicit portlet window ID in the Portlet API. If you only need to access the cache in the render phase, the easiest method is to use the getNamespace method on the response. For all other cases, you can use the helper class we provided to generate the ID, which uses session prefixing defined to entries stored in the portlet scope in the portlet session.
The example bookmark portlet shows how to use the different cache key scopes, and how to store the data with the WebSphere dynacache infrastructure.
| Description | Name | Size | Download method |
|---|---|---|---|
| Code samples | bookmarkportlet.zip | 11 KB | FTP |
Information about download methods
Learn
- Java Portlet Specification V1.0, JSR 168: Defines the Java Community Process project for the portlet API.
-
Portlet development guide: Helps you get started developing portlets in Websphere Portal V5.x.
-
Best practices: Developing portlets using JSR 168 and WebSphere Portal V5.02: Provides guidance for designing and developing portlets that conform to the JSR-168 standard, and which leverage the IBM WebSphere Portal infrastructure for JSR 168.
-
Comparing the Java Portlet Specification JSR 168 with the IBM Portlet API: Explains the basic concepts and features of JSR 168, compares them to the IBM Portlet API, and provides an example portlet of each of the two portlet programming interfaces.
-
IBM WebSphere Developer Technical Journal: Static and dynamic caching in WebSphere Application Server V5
: Explains how different types of caching can be used to maximize the performance and minimize the workload of each layer of a distributed Web application in WebSphere Application Server.
-
Page-to-page communication between JSR 168 portlets in WebSphere Portal V5.1.0.1: Shows how to enable a JSR 168 portlet on one page to pass information to a JSR 168 portlet on another page, using cross-page wiring, which was introduced in WebSphere Portal V5.1.0.1.
-
WebSphere Application Server Information Center: Shows how to enable a JSR 168 portlet on one page to pass information to a JSR 168 portlet on another page, using cross-page wiring, which was introduced in WebSphere Portal V5.1.0.1.
-
WebSphere Portal production documentation: Provides access to all current product documentation including InfoCenters, release notes, and readmes for all releases of WebSphere Portal.
-
developerWorks WebSphere Portal zone: Find more resources to help you develop portals and portlets.
Get products and technologies
-
Rational Application Developer V6: Download trial software from developerWorks. Includes the portal tools and a test runtime copy of portal that you can use to develop a prototype.

Stefan Hepper is the architect responsible for the WebSphere Portal programming model and public APIs. He was co-leader of the Java Portlet Specification JSR 168. He also started the Pluto project at Apache, which provides the reference implementation of JSR 168. Stefan has delivered a number of lectures at international conferences, such as JavaOne, published various papers, and is co-author of the book Pervasive Computing (Addison-Wesley 2001). His research interests are component-based software architectures, pervasive infrastructures, and, of course, portals and portlets. Stefan received a Diploma of Computer Science from the University of Karlsruhe, Germany, and in 1998 he joined the IBM Böblingen Development Laboratory.

Stephan Hesmer is the architect responsible for the Portlet Runtime in WebSphere Portal. He is also responsible for the integration of WebSphere Portal with its base product WebSphere Application Server. Stephan worked on the JSR 168 Java Portlet Specification, and designed and implemented the initial version of the JSR 168 Reference Implementation, Pluto. Stephan received a Diploma of Information Technology from the University of Cooperative Education Stuttgart, Germany, in 2000. After graduating, he joined the IBM Böblingen Development Laboratory to work in the WebSphere Portal Team.




