Portlet Caching

by Gerald Nunn
01/10/2005

Abstract

WebLogic Portal 8.1 includes a number of features that enable developers to maximize the performance of their site. One of these features is the caching of portlet content through the use of the Render Cacheable property. While a basic description of the property can be found in the help documentation, this feature is often misunderstood. The purpose of this article is to provide a detailed examination of this property and discuss when it should and should not be used.

Background

The Render Cacheable property is part of the portlet definition. Clicking on a portlet definition and examining the property editor shows this property along with the Cache Expires property that is closely related. Since the Render Cacheable property is part of the general portlet definition and not a particular portlet instance, this means that caching applies to all instances of the portlet within the portal. You cannot specify caching for some instances and not for others.

Even though the caching is controlled on a global basis, different instances of the same portlet use different caches. For illustrative purposes, let us examine a scenario where a portlet called GenericDisplayFilePortlet has been created by a developer. This portlet displays a static HTML file from the file system based on a property passed to the portlet through personalization. If this portlet had two separate instances on the portal home page, each displaying a separate file, the caching for these two instances would not collide; each portlet instance maintains its own cache.

The Cache Expires property is the number of seconds the cache is kept alive before the cache content is expired. If the Cache Expires is set to –1, then no caching takes place regardless of whether Render Cacheable is set to true.

Cache Scope

The first misconception many developers have about portlet caching is that the cache is application scoped. This is incorrect. The portlet cache is scoped to the session level, and this means that each individual user will have individual versions of the portlet cached.

This makes the portlet caching mechanism well suited to caching personalized content, however portlet caching is not well suited to caching static content that is presented identically to all users and that rarely expires. For example, a portlet displaying a monthly company newsletter should not use the Render Cacheable property; instead it should use an alternative mechanism that provides caching at the application level.

If you did set the Render Cacheable property for the newsletter, then you would, in effect, be caching this content at session scope for each individual session. This needlessly duplicates the content in multiple caches and reduces performance.

One alternative is simply to move the caching one layer lower and have the JSP or page flow the portlet is fronting perform the caching at that level. In this case we can use the wl:cache tag that is included with WebLogic to cache content at the application level. Detailed documentation on the wl:cache tag is referenced below.

Another alternative is to cache information within the portal caches; this enables administrators to easily tune these caches through the Portal Administrator tool included with WebLogic Portal. You can opt to work with the portal cache in a variety of ways including:

  • Using the portal cache API directly, you can store and retrieve information for display as needed. See the References section for more information.

  • Alternatively, you can create a custom tag alternative to wl:cache that caches information in the portal cache for better control by portal administrators. The appendix below contains a simple example showing how this can be done.

Cache Lifecycle

The next misconception about the portlet cache that we will discuss involves the lifecycle of the cache. Developers tend to think the cache is always used or not used depending on whether the portlet has been previously invoked and has not yet surpassed the expiration time. In fact, there are several reasons why the cache will not be used when the portlet is rendered. Having an understanding of the cache lifecycle is very important to ensuring that maximum benefit is being achieved from the cache.

An interesting exercise to perform is to create a simple portlet that displays the date and time using a small JSP as follows:

<%@ page language="java" contentType="text/html;charset=UTF-8"%>

<%@ taglib uri="netui-tags-databinding.tld" prefix="netui-data"%>

<%@ taglib uri="netui-tags-html.tld" prefix="netui"%>

<%@ taglib uri="netui-tags-template.tld" prefix="netui-template"%>

<p>

  Date and time is: <%=new Date(System.currentTimeMillis())%>

</p>

Set the portlet's Render Cacheable property to true, and set Cache Expires to 30 seconds. Drop the portlet on a test portal, and run the portal from WebLogic Workshop. You should see the portal displaying the date and time. Now press the browser's Refresh button and notice the date and time has changed; the portlet cache was not used. At this point you might be forgiven for thinking the portlet cache is broken, but in fact it is working as we will see next.

To expand this example further, next create a new portlet with a simple page flow consisting of a single JSP page. The JSP contains a simple form and Submit button as follows:

<%@ page language="java" contentType="text/html;charset=UTF-8"%>

<%@ taglib uri="netui-tags-databinding.tld" prefix="netui-data"%>

<%@ taglib uri="netui-tags-html.tld" prefix="netui"%>

<%@ taglib uri="netui-tags-template.tld" prefix="netui-template"%>

<p>Postback example</p>

<netui:form action="refresh">

  <netui:button>Refresh</netui:button>

</netui:form>

This portlet simply initiates a postback operation to the server when the Refresh button is clicked. When setting up this example, make sure to tie the refresh action back to the JSP page in the page flow editor. Now drop this portlet on the same portal page as the previous date and time portlet that we are caching.

If you now run the portal from Workshop, two portlets should be visible: the date and time portlet that is being cached, and the postback portlet that we just created. If you refresh the browser, the same behavior previously noted continues, namely the cache is not being used. Now try clicking the Refresh button in the postback portlet and notice that when a postback is performed the cache is used as expected.

The point of this example is to demonstrate that the cache is only used in certain parts of the portlet lifecycle; specifically, while not explicitly documented, the cache is only used if the portlet loads state. Operations that do not invoke this part of the portlet lifecycle will not use the cache, and therefore the cache and portlet lifecycles are intimately linked. How various portal interactions affect the cache is summarized in the table below.

Interaction Cache Used
Portal is accessed through external link No
Cached portlet initiates postback No
Portlet on same page as cached portlet initiates postback Yes
Navigating between books and pages in the portal Yes
_nfls=false parameter is passed to portal in URL No

In summary, to achieve maximum effectiveness of the cache, it is important to understand the way the cache lifecycle is handled relative to portal interactions. If a situation in which the cache is being utilized involves a lot of interactions where the cache will not be invoked, alternatives to the portlet cache should be strongly considered.

Conclusion

This concludes our discussion of the portlet caching mechanism. As you can see there is a lot more to the portlet caching mechanism than appears on the surface, and developers should carefully consider their options for each situation in order to maximize the performance benefits of caching.

The ideal use case of the portlet cache is for content that is personalized for each user and expires often. Other use cases should look to more appropriate caching alternatives such as the use of the wl:cache tag or the portal cache.

Appendix. Cache Tag

Example 1. Custom Tag

This is a simple example showing how to cache content in a portal cache using a custom tag. It is not meant to be a full-fledged caching solution, rather it illustrates how we can use portal caches to cache information as an alternative to Render Cacheable.

package com.bea.ps.portal.tags.cache;



import com.bea.p13n.cache.Cache;

import com.bea.p13n.cache.CacheFactory;

import java.io.IOException;

import javax.servlet.jsp.JspException;

import javax.servlet.jsp.tagext.BodyTagSupport;



/**

 * Caches content in body of tag in a portal cache called

 * 'tagContentCache'. While not as full featured as wl:cache,

 * caching content in the portal cache does have an advantage of

 * allowing administrators to tune and control the cache from the

 * portal administration tool.

 */

public class CacheContentTag extends BodyTagSupport {

  private static final String CACHE_NAME = "tagContentCache";

  private String name;



  /**

  * Name to use to uniquely identify content within the cache

  */

  public String getName() {

    return name;

  }



  public void setName(String value) {

    name = value;

  }



  public int doStartTag() throws JspException {



    Cache cache = CacheFactory.getCache(CACHE_NAME);

    //If the request parameter _cache_refresh is set to true,

    //we invalidate the cache for this entry. This is identical

    //to the way wl:cache behaves.

    if ("true".equals(pageContext.getRequest().getParameter("_cache_refresh"))) {

      cache.remove(name);

    }

    if (cache.get(name)!=null)

      return BodyTagSupport.SKIP_BODY;

     else

      return BodyTagSupport.EVAL_BODY_BUFFERED;

    }



  public int doEndTag() throws JspException {

    Cache cache = CacheFactory.getCache(CACHE_NAME);

    String content = (String)cache.get(name);

    //If content is null then it has not been previously cached

    if (content==null) {

      //Get content of tag body

      content = getBodyContent().getString();

      //Put content in portal cache

      cache.put(name,content);

    } try {

      //Out  put content to page

      pageContext.getOut().print(content);

    } catch (IOException e) {

      throw new JspException(e);

    }

    return BodyTagSupport.EVAL_PAGE;

  }



  public void release() {

    super.release();

    name = null;

  }

}

Example 2. TLD File

This is the accompanying JSP tag descriptor file.

<?xml version="1.0" encoding="ISO-8859-1"?>

<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"

"http://bit.ly/10lWUaT">

<taglib>

  <tlib-version>1.0</tlib-version>

  <jsp-version>1.2</jsp-version>

  <short-name>example</short-name>

  <display-name>Cache Tag</display-name>

  <description>

    A tag for caching content in a portal cache

  </description>

  <tag>

  <name>cache</name>

  <tag-class>com.bea.ps.portal.tags.cache.CacheContentTag</tag-class>

  <body-content>jsp</body-content>

  <display-name>Cache</display-name>

  <description>Caches content in the portal cache tagContentCache</description>

  <attribute>

    <name>name</name>

    <required>true</required>

    <rtexprvalue>true</rtexprvalue>

    <description>The name to use to uniquely identify the content</description>

  </attribute>

  </tag>

</taglib>

Example 3. Example Cache Tag Usage

This is an example showing how the cache tag could be used in a JSP.

<%@ taglib uri="/WEB-INF/tld/example.tld" prefix="example" %>



<example:cache name="newsletter">

    <jsp:include page="newsletter.html">

</example:cache>

Gerald Nunn is a business principal consultant with BEA Systems Professional Services.