Home Coding Wiki
RSS
 

Ehcache event listeners as Spring beans

02 Feb

When working with Ehcache and Spring, one might get to the point where he/she needs to implement cache event listeners: objects that are observing the cache and want to be notified by the cache when an element is put, removed or expires in/from the cache. However, Ehcache has a requirement that for each such listener you need to write 2 classes: a cache listener factory and the actual cache listener. Moreover, Ehcache takes care by itself of the instantiations of the factory and the listeners, so if one wants to have the listeners as Spring beans and have some other beans autowired in one’s listeners, then there is certainly a problem. Here is a solution to this problem. In the next posts I will show a solution that contains a generic cache event listener factory, that can be configured to return custom beans when Ehcache asks for the listeners. This way, one gets the following benefits:

  • no need to implement one factory for each listener. Reuse this generic factory for all the listeners
  • implement the listeners as full Spring beans, with all the DI support that comes with the framework
  • for each listener one needs one single line of configuration code (and the implementation of the listeners, of course)

Here is the generic listener factory:

public class GenericCacheEventListenerFactory extends CacheEventListenerFactory {

    @Override
    public CacheEventListener createCacheEventListener( Properties properties ) {
        String beanName = properties.getProperty( "bean" );
        if ( beanName == null ) {
            throw new IllegalArgumentException( "The cache event listener factory must be configured with 'bean' " +
                                                        "property pointing to the Spring bean to return as cache event listener" );
        }

        return (CacheEventListener) ApplicationContextProvider.getContext().getBean( beanName );
    }
}

As the code presents it, I am accessing the Spring context statically, using the simple pattern with the ApplicationContextProvider that I presented in a previous post. Ehcache creates an instance of the factory, and when it asks for the listener, the factory fetches the bean from the Spring application context and returns it to the cache manager.

Next step is to configure this factory in the Ehcache configuration file:

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false"
         monitoring="off">

    <defaultCache maxElementsInMemory="100000" eternal="false" timeToIdleSeconds="300" timeToLiveSeconds="500" memoryStoreEvictionPolicy="LRU" overflowToDisk="false"/>

    <cache name="testCache" maxElementsInMemory="5000000" eternal="false" timeToIdleSeconds="3600" overflowToDisk="false" memoryStoreEvictionPolicy="LRU">
        <cacheEventListenerFactory class="zendo.playground.spring.cache.GenericCacheEventListenerFactory"
                                   properties="bean=myCacheEventListener"/>
    </cache>
</ehcache>

When configuring, one must supply the name of the bean that should be returned as cache listener. If the property is not supplied, the factory will crash with an IllegalArgumentException to make the use aware of the slip.

Finally, here is the Spring application context, that defines the static application context provider, the cache manager and the actual cache listener (which is a very simple implementation of CacheEventListener) :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

    <bean id="applicationContextProvider" class="zendo.playground.spring.ApplicationContextProvider"/>

    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation" value="/zendo/playground/spring/cache/TestEhcacheConfig.xml"/>
    </bean>

    <bean id="testCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
        <property name="cacheManager" ref="cacheManager"/>
    </bean>

    <bean id="myCacheEventListener" class="zendo.playground.spring.cache.MyCacheEventListener"/>
</beans>

The drawback of this solution is the following:

  • one needs to use the ApplicationContextProvider for static access to the application context. This means that if one’s application has more than one application context loaded (which, however, should not be the case, in most situations) then this solution will not work as easy as it is presented. One must craft a more complex ApplicationContextProvider, that keeps track of multiple contexts (each one with a distinct name).

What do you think about this solution? Do you know if the problem can be solved in a better way? Please take the time to leave a comment and give me your opinion.

 
No Comments

Posted in Spring

 

Leave a Reply