Sunday, September 20, 2009

Monitoring EhCache with JMX and Spring

EhCache provides an easy way to manage your caches with JMX. The ManagementService class is the API entry point. You can easily integrate it in your Spring configuration files.

Here's how you can do it :

<ehcache:config configLocation="classpath:config/ehcache.xml" failQuietly="true" />

<ehcache:annotations>
<ehcache:caching id="yourCacheModel" cacheName="yourCache"/>
</ehcache:annotations>

<bean id="mbeanServer" class="org.springframework.jmx.support.MBeanServerFactoryBean">
<property name="locateExistingServerIfPossible" value="true" />
</bean>

<bean class="net.sf.ehcache.management.ManagementService" init-method="init">
<constructor-arg ref="cacheManager" />
<constructor-arg ref="mbeanServer" />
<constructor-arg value="true" />
<constructor-arg value="true" />
<constructor-arg value="true" />
<constructor-arg value="true" />
</bean>

After doing this, you will have access to your caches configuration and operations such as cache flushing. You will also have access to useful statistics like cache hits, cache misses, object count and some more. These statistics will give you critical information on how to configure your caches for an optimal use.

Resources :
- JMX Management and Monitoring from the ehcache official documentation.

Saturday, September 19, 2009

Annotation driven Caching with EhCache and Spring

Caching is the key of fast applications. The Spring modules framework provides us with an easy way to cache the return of our methods using java 5 annotations.

Here's an example :

ehCache.xml :

<ehcache>
<defaultCache
maxElementsInMemory="500"
eternal="true"
overflowToDisk="false"
memoryStoreEvictionPolicy="LFU" />

<cache name="getTestCache"
maxElementsInMemory="50"
eternal="true"
overflowToDisk="false"
memoryStoreEvictionPolicy="LFU" />
</ehcache>


applicationContext.xml :

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ehcache="http://www.springmodules.org/schema/ehcache"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springmodules.org/schema/ehcache
http://www.springmodules.org/schema/cache/springmodules-ehcache.xsd">

<ehcache:config configLocation="classpath:ehcache.xml" />
<ehcache:annotations>
<ehcache:caching id="getTestCacheModel" cacheName="getTestCache" />
<ehcache:flushing id="getTestFlushModel" cacheNames="getTestCache" />
</ehcache:annotations>

<bean id="customerManager" class="services.impl.CustomerManagerImpl"/>

</beans>

The CustomerManager interface :

import org.springmodules.cache.annotations.Cacheable;
import org.springmodules.cache.annotations.CacheFlush;
import vo.Customer;

public interface CustomerManager {

@Cacheable(modelId="getTestCacheModel")
public Customer load(long customerId);

@CacheFlush(modelId="getTestFlushModel")
public void add(Customer customer);
}

In order to do a simple test, we create a simple implementation of the CustomerManager interface :

import services.CustomerManager;
import vo.Customer;

public class CustomerManagerImpl implements CustomerManager {

public Customer load(long customerId) {
//This part should normally call a DAO
return new Customer("Rene", 34);
}

public void add(Customer customer) {
//This part should normally call a DAO
}
}

And the test class :

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import services.CustomerManager;

public class Main {

public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
CustomerManager customerManager = (CustomerManager) context.getBean("customerManager");

System.err.println("Loading customer");
customerManager.load(455L);

System.err.println("Loading customer again");
customerManager.load(455L);

System.err.println("Adding customer");
customerManager.add(new Customer("Jean",34));
}
}

The final step is to add this simple log4j.properties file to the classpath :

# Set root category priority to INFO and its only appender to CONSOLE.
log4j.rootCategory=DEBUG, CONSOLE

# CONSOLE is set to be a ConsoleAppender using a PatternLayout.
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=- %m%n

Here's the log result of the execution of this class :
Loading customer
- Attempt to retrieve a cache entry using key <1187678266|32099260> and cache model
- Retrieved cache element
- Attempt to store the object in the cache using key <1187678266|32099260> and model
- Object was successfully stored in the cache
Loading customer again
- Attempt to retrieve a cache entry using key <1187678266|32099260> and cache model
- Retrieved cache element
Adding customer
- Attempt to flush the cache using model
- Cache has been flushed.

We can see that the first time we call the load method, the retrieved Customer object is stored in the cache. The second time, the object is directly retrieved from the cache. When we call the add method the cache is flushed.

Here's the maven pom file I used :

We'll use the 0.8 version of springmodules, the latest in the maven repo central. This version has dependencies on spring 2.0 and ehcahe 1.1. In order to use the latest library we will add our own version of spring (2.5.6) and ehcache (1.6.2) and excludes the old ones. We have to exclude also all the proprietary libraries which should be optional but are not.

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
<version>2.5.6</version>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>org.springmodules</groupId>
<artifactId>spring-modules-cache</artifactId>
<version>0.8</version>
<exclusions>
<exclusion>
<artifactId>gigaspaces-ce</artifactId>
<groupId>gigaspaces</groupId>
</exclusion>
<exclusion>
<groupId>jini</groupId>
<artifactId>jsk-lib</artifactId>
</exclusion>
<exclusion>
<groupId>jini</groupId>
<artifactId>jsk-platform</artifactId>
</exclusion>
<exclusion>
<groupId>jini</groupId>
<artifactId>mahalo</artifactId>
</exclusion>
<exclusion>
<groupId>jini</groupId>
<artifactId>reggie</artifactId>
</exclusion>
<exclusion>
<groupId>jini</groupId>
<artifactId>start</artifactId>
</exclusion>
<exclusion>
<groupId>jini</groupId>
<artifactId>boot</artifactId>
</exclusion>
<exclusion>
<groupId>jini</groupId>
<artifactId>webster</artifactId>
</exclusion>
<exclusion>
<groupId>jboss</groupId>
<artifactId>jboss-cache</artifactId>
</exclusion>
<exclusion>
<groupId>jboss</groupId>
<artifactId>jboss-common</artifactId>
</exclusion>
<exclusion>
<groupId>jboss</groupId>
<artifactId>jboss-jmx</artifactId>
</exclusion>
<exclusion>
<groupId>jboss</groupId>
<artifactId>jboss-minimal</artifactId>
</exclusion>
<exclusion>
<groupId>jboss</groupId>
<artifactId>jboss-system</artifactId>
</exclusion>
<exclusion>
<groupId>jcs</groupId>
<artifactId>jcs</artifactId>
</exclusion>
<exclusion>
<groupId>xpp3</groupId>
<artifactId>xpp3_min</artifactId>
</exclusion>
<exclusion>
<groupId>ehcache</groupId>
<artifactId>ehcache</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>


In conclusion, annotation driven Caching with EhCache and Spring is very straightforward to implement.

However, be aware that the springmodules framework is in a dead status and have some unresolved issues you should be aware of. You can see those issues listed here. Take a look at it before using it though most of the issues have workarounds. There is also a 0.9 version you can only download because it has never been released in the maven central repository.

A project named Spring modules fork has been created to revive this useful project and to make it evoluate. They've created a 0.10-SNAPSHOT version hosted on their own server.