TUTORIAL

Quickstart

All you have to do is to implement the CacheableAction api interface in your actions. Alternatively you can subclass AbstractCachableAction api which provides additional support for expiration based caching.

A CacheableAction must provide a cacheKey that identifies its output. Besides the classname all parameters and attributes that are used to compute the result should (well, must) be included in the key, for example com.foo.BarAction[id=23] (as the key isn't interpreted in any way the format can be whatever you want).

The second information a cacheable action has to provide is the lastModified date. The cache needs to know when the underlying data has changed in order to refresh the cached item. There a basically three different ways to achieve this:

Modification based caching

Lets imagine an action that retrieves articles from a database and displays them as list of teasers. To determine whether the action needs to be performed we need to know whether any of the articles has been modified, was deleted or a new one has been added. We therefore need a column in the article table that contains a timestamp with the date of the last modification. Additionally we need a flag that indicates whether an article has been deleted. We could then use a query like SELECT max(last_modified) FROM article WHERE id_category=id in our Action's getLastModified method.

Expiration based caching

If your datasource doesn't provide last modified information or it would take as long to retrieve this information as it would take to actually perform the action you can still use expiration based caching. Even very short expiration times (e.g. 5s) can help to decrease the response time of a heavily loaded server. To use expiration based caching simply extend the AbstractCacheableAction and override the getTimeToLive method.

Notification based caching

When the content to be displayed isn't determined by a query (like in our index example) and the action already knows which primary key(s) shall be retrieved you can use an eventing mechanism and let your backend notify the struts application whenever some data changes. You could for example send a JMS message to your application containing a unique object identifier and a timestamp as soon as an object is modified. You could then put this information into a Hashtable that is used by your actions to perform last-modified lookups.

Combined stragtegies

In some cases it may be very useful to combine the methods mentioned above. Note that if you use expiration based caching the getLastModified method isn't called as long as the cache item hasn't expired.

Example

Below you will find a very basic example that demonstrates how to implement a cacheable action. The action displays the current time using the locale specified by the request parameter locale or the user's default locale if the parameter is omitted. The timeToLive is set to 5 seconds.

public class ExampleAction extends AbstractCacheableAction {
    
    /**
     * Returns the cacheKey containing the acttion's class name and
     * the requested locale.
     */

    public String getCacheKey(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) {
            
        return ExampleAction.class.getName() + ':' + getLocale(request);
    }
    
    /**
     * Returns <code>System.currentTimeMillis()</code> to force a refresh
     * when the <code>timeToLive</code> has elapsed.
     */

    public long getLastModified(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) 
            throws IOException, ServletException {
        
        return System.currentTimeMillis();
    }
    
    /**
     * Returns <code>5000</code> which means that result will be cached
     * for 5 seconds.
     */

    public int getTimeToLive(ActionMapping mapping, ActionForm form,
            HttpServletRequest request, HttpServletResponse response) {
            
        return 5000;
    }
    
    /**
     * Writes the current time formatted for the requested locale to the
     * output. For simplicity's sake no forward is returned.
     */

    public ActionForward execute(ActionMapping mapping, 
            ActionForm form, HttpServletRequest request, 
            HttpServletResponse response)
            throws IOException, ServletException {
        
        Locale locale = getLocale(request);
        DateFormat df = DateFormat.getDateTimeInstance(
                DateFormat.SHORT, DateFormat.LONG, locale);
                
        response.getWriter().write(df.format(new Date()));
        return null;
    }
    
    /**
     * Returns the locale specified by the request parameter <code>locale</code>
     * or the user's default locale in case the parameter is omitted.
     */
    protected Locale getLocale(HttpServletRequest request) {
        String s = request.getParameter("locale");
        if (s != null) {
            return new Locale(s);
        }
        return super.getLocale(request);    
    }
}

Note: Since release 1.1 this example is included in the actioncache distribution.


digital handcraft made in hamburg/berlin