Thursday, May 8, 2008

Pragmatic Caching - a simple Cache Configuration Model for Spring

Caching is a widely used instrument when it comes to performance tuning a given application (of course not until you’ve measured the real bottlenecks). For example, you may want to cache objects that are expensive to fetch (i.e. an object graph from database) or to calculate. Whereas most of us have used a kind of Map to ‘cache’ some data on ad hoc basis in the past, there are a couple of mature caching solutions at the market in the meantime that offer sophisticated features for caching, which goes beyond simply holding objects in memory (e.g. Thread pool controls, Element grouping, Remote server chaining (or clustering), failover, …) Deciding to use such a cache solution always comes with the question of the resulting complexity impact when applying ‘cache logic’ to your application.

Caching is an Aspect

One could rightly say, that caching is a cross cutting concern, that shouldn’t be intermingled with the rest of your business or application logic. Especially the Spring framework provides different options to cope with cross cutting concerns. Spring Modules - a sub project of the Spring framework - offers Support for declarative caching that uses Springs AOP capabilities. It provides a generic ‘API’ while supporting a bunch of different cache solutions (e.g. EHCache, JCS, OSCache, GigaSpaces, …) under the hood.

Pragmatic Caching

While its very easy to declare caching models (resp. flushing models) in a declarative (and more abstract) style with Spring Modules Caching, you sometimes may want to do it in a more programatic way due to some specific caching behaviour that’s difficult to reflect by using generic, declarative expressions. For example you may want to build a more complex cache key or decide which object to cache due to runtime conditions. In this case it may be better (and simpler) to handle caching in a more programmatic way, still treating caching as a cross cutting concern. The following solution is by far not comparable with Spring Modules Caching, yet provides a slightly different model for declaring and using a Cache. Only the core configuration and instantiation of the Cache will be managed by Spring. Caching and Flushing can be managed programmatically, giving you the full power of expressing ‘caching logic’ by the underlying language.

Pragmatic Desicions

We will use only a particular Cache solution - for example the Java Caching System (JCS), so we don’t have to cope with a variety of different Cache APIs. While Spring Modules Caching have to provide a more abstract API which is able to handle different Cache solutions, focussing on a specific solution allows us to manage cache configuration with far less code (We could of course come up with a similar abstraction of caches which allows us to write Adapters for different Cache solutions - but that’s not in the focus of this post).

Programmatic Interception

The basic concept of AOP (remember - caching is an Aspect) is method interception. In our case we want to intercept calls to service methods that provide expensive to fetch or expensive to calculate objects (and thus separating business or application logic from caching logic).
Interception is done in order to deliver objects that were cached due to prior calls (once delegated to the intercepted service).

Let’s imagine a CustomerDAO interface, which is implemented by
HibernateCustomerDAO. In order to intercept arbitrary calls to HibernateCustomerDAO, we’ll define CustomerDAOCacheInterceptor for which we’ll provide a readily configured Cache instance:

public class CustomerDAOCacheInterceptor implements CustomerDAO{

    private static final String CACHE_GROUP_VIP = "VIP";

    private JCS customerCache = null;
    private CustomerDAO target = null;

    public void setTarget( CustomerDAO target ){ = target;

    public void setCustomerCache( JCS cache ){
        this.customerCache = cache;

    public Customer loadCustomerByLogin( String loginId ) {

        String cacheKey = calculateCacheKey( loginId );
        String cacheGroup = getCacheGroup( loginId );

        Customer customer = (Customer)
        customerCache.getFromGroup( cacheKey, cacheGroup );

        if( customer == null ){
            customer = target.loadCustomerByLogin( loginId );

            if( customer != null ){
                    customerCache.putInGroup( cacheKey, cacheGroup, customer );
                catch ( CacheException e ){
                    throw new RuntimeException(
                        "exception while trying to store Customer to cache (JCS)", e );
        return customer;

    public void update( Customer customer ) {

        target.update( Customer );

        if( someCondtionsMetOn( customer ) ){
                invalidateGroup( CACHE_GROUP_VIP );
            catch ( CacheException e ){
                throw new RuntimeException( "exception while invalidating VIP cache (JCS)", e );

As you’ve seen, the Interceptor is provided with a target - the CustomerDAO implementation which is asked for a Customer in case the affected Customer isn’t cached yet (normally our HibernateCustomerDAO).
Further on, a readily configured JCS Cache instance is injected (as said before, we could easily come up a cache abstraction by shielding the Intercepor from the concrete cache instance with an appropriate cache interface), so the Interceptor doesn’t have to cope with Cache configuration and cache creation itself (maybe there are other DAOs that will use the same cache instance, so we have to come up with a central cache configuration and instantiation mechanism anyway).

Let’s say that the cache key is calculated by a more or less complex logic, that’s better described within Java. Same goes for using cache groups within the ‘customer cache’. Handling different cache groups and detecting the right cache group for a certain customer may be better placed inside the Interceptor using the full power of the language (again, the same might be true for determining conditions under which the cache or a certain cache group have to be flushed as shown in method update() ).
Note, that the demonstration doesn’t include logic to handle concurrent access - this ‘exercise’ is left to you since we only want to take a look at the caching logic.

Basic cache configuration

JCS (like most of the other cache solutions) provides a way to configure caches by using a cache configuration file (*.ccf). In it, we’ll describe the different caches which are supposed to be used within our application (for further information, please refer to the documentation):




We’ve defined a default configuration and a cache coniguration for our customer cache. We could of course could have defined some more cache regions in order to cache other objects or object categories within our application (e.g. a particular region for caching Articles or calculated Prices).
That’s all we need to start. The last question (and most interesting for this post) is who’s responsible for instantiating and managing the different ‘caches’ (resp. cache regions)?

Say it with a FactoryBean

Within an application context, all we want to do is pointing to the underlying cache configuration file and choose a particular cache region (defined within the cache configuration file) by its name to bring the different cache instances to life.
This is easily done by leveraging a FactoryBean, which is responsible for the creation and management of our caches. By Injecting the path and name of the cache configuration file and the name of the cache region the FactoryBean should deliver the corresponding cache instance.

package org.common.cache;

import java.util.HashMap;
import java.util.Map;

import org.apache.jcs.JCS;
import org.apache.jcs.access.exception.CacheException;
import org.springframework.beans.factory.FactoryBean;

public class JCSCacheFactoryBean implements FactoryBean {

    private Map<String,JCS> caches = new HashMap<String,JCS>();

    private String configLocation = null;
    private String region = null;

    public void setConfigLocation(String configLocation) {
        this.configLocation = configLocation;

    public void setRegion(String region) {
        this.region = region;

    public Object getObject() throws Exception {
            String cacheRegionKey =
                new StringBuffer( configLocation )
                    .append( "." ).append( "region" ).toString();

            JCS cache = null;

            if( caches.containsKey( cacheRegionKey ) ){
                cache = caches.get( cacheRegionKey );
                JCS.setConfigFilename( configLocation );
                cache = JCS.getInstance( region );
                caches.put( cacheRegionKey, cache );

            return cache;
        catch ( CacheException e ){
            throw new RuntimeException( "exception while initializing cache (JCS)", e );

    public Class getObjectType() {
        return JCS.class;

    public boolean isSingleton() {
        return true;

As you’ve seen, the implementation is very straightforward. Every time a cache instance is requested, the FactoryBean will look for an appropriate cache which is already created and in memory or is creating a new instance according to the current property values configLocation and region.

A cache bean

With the given FactoryBean, it’s easy to define a ‘cache bean’ simply by providing the cache configuration file and the name of a defined cache region.
Now you can wire up dependend beans (e.g. CustomerDAOInterceptor) simply by injecting the cache bean.

    <bean id="customerDAO" class="org.sample.intercept.<span><span>CustomerDAOCacheInterceptor</span></span>">
        <property name="target">
            <bean class="org.sample.CustomerDAO">
                <property name="sessionFactory"><ref local="hibernateSessionFactory"/></property>
        <property name="customerCache"><ref bean="customerCache"/></property>

    <bean id="customerCache" class="org.sample.cache.JCSCacheFactoryBean">
        <property name="configLocation"><value>/cache.ccf</value></property>
        <property name="region"><value>customerCache</value></property>

    <bean id="priceCache" class="org.sample.cache.JCSCacheFactoryBean">
        <property name="configLocation"><value>/cache.ccf</value></property>
        <property name="region"><value>priceCache</value></property>

It’s of course possible to wire up more than one bean with a needed cache bean just as well as you can define arbitrary cache beans which represent different cache regions.


We’ve come up with a very pragmatic solution with a declarative style for cache configuration and a more programmatic style for handling caching behaviour. As always, the usefulness of such a solution depends on the given problem space and the surrounding forces.
While Spring Modules Caching allows for a more generic style for all aspects of cache confiuration and cache processing (with less code to write!) the given solution is only half-declarative (only for the configuration part) providing more power in expressing the cache processing part.

As always, its a kind of trade off - you’ll have to choose the right tool for the right situation …

by Mario Gleichmann

No comments: