7/20/2015

Ehcache Tutorial: Dynamically configuration and Cache Abstraction(Since Spring 3.1)

There are basically two ways to config Ehcache in java project.

1. Dynamically configuration
2. Through XML

Before getting started, we need to add the dependency of Ehcache. For example, if use maven, add below dependency into pom.xml:

<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>2.10.0</version>
</dependency>

1. Dynamically configuration

More flexible than XML configuration. But instead, you need to write and manage more code.

Cache Initialization:

public abstract class CommonDao{

    @Value("${cache.enable:true}")
    protected boolean cacheEnable;

    @Value("${cache.timetolive:21600}")
    private int cacheTimeToLive;
    
    @Value("${cache.timetoidle:21600}")
    private int cacheTimeToIdle;
    
    protected CacheManager cacheManager;
    
    protected static final String userCacheName = "userCache";
    protected static final String orgCacheName = "orgCache";

    @PostConstruct
    void initCache(){
        Configuration config = new Configuration();
        CacheConfiguration defaultCacheConfiguration = new CacheConfiguration();
        defaultCacheConfiguration.setEternal(false);
        defaultCacheConfiguration.setTimeToIdleSeconds(cacheTimeToIdle);
        defaultCacheConfiguration.setTimeToLiveSeconds(cacheTimeToLive);
        config.addDefaultCache(defaultCacheConfiguration);
        cacheManager = CacheManager.create(config);
        cacheManager.addCacheIfAbsent(userCacheName);
        cacheManager.addCacheIfAbsent(orgCacheName);
    }
    
    protected void putCacheElement(String cacheName, String key, Object value){
        
        Cache cache = null;
        if(BooleanUtils.isTrue(cacheEnable)){
            cache = cacheManager.getCache(cacheName);
            if(cache != null){
                cache.put(new Element(key, value));
            }
        }
    }
    
    protected void putCacheElement(Cache cache, String key, Object value){
        
        if(BooleanUtils.isTrue(cacheEnable)){
            if(cache != null){
                cache.put(new Element(key, value));
            }
        }
    }
    
    protected Object getCacheElement(String cacheName, String key){
        
        Cache cache = null;
        if(BooleanUtils.isTrue(cacheEnable)){
            cache = cacheManager.getCache(cacheName);
            if(cache != null){
                Element element = cache.get(key);
                return element == null ? null : element.getObjectValue();
            }
        }
        return null;
    }
    
    protected Object getCacheElement(Cache cache, String key){
        
        if(BooleanUtils.isTrue(cacheEnable)){
            if(cache != null){
                Element element = cache.get(key);
                return element == null ? null : element.getObjectValue();
            }
        }
        return null;
    }
    
    protected void removeCacheElement(String cacheName, String key){
        
        Cache cache = null;
        if(BooleanUtils.isTrue(cacheEnable)){
            cache = cacheManager.getCache(cacheName);
            if(cache != null){
                cache.remove(key);
            }
        }
    }
    
    protected void removeCacheElement(Cache cache, String key){
        
        if(BooleanUtils.isTrue(cacheEnable)){
            if(cache != null){
                cache.remove(key);
            }
        }
    }
    
    protected CacheManager getCacheManager(){
        return cacheManager;
    }
}

Dynamically update:

If in the system, you have the requirement to run time update the caching configuration:

Cache cache = dao.getCacheManager().getCache("userCache"); 
CacheConfiguration config = cache.getCacheConfiguration(); 
config.setTimeToIdleSeconds(60); 
config.setTimeToLiveSeconds(120); 
config.setmaxEntriesLocalHeap(10000); 
config.setmaxEntriesLocalDisk(1000000);

Reference:
http://ehcache.org/generated/2.10.0/html/ehc-all/index.html#page/Ehcache_Documentation_Set/co-cfgbasics_dynamically_changing_cache_config.html#wwconnect_header

2. XML Configuration

a.  Config the ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true" monitoring="autodetect">
    <defaultCache
        eternal="false"
        maxElementsInMemory="1000"
        overflowToDisk="false"
        diskPersistent="false"
        timeToIdleSeconds="3600"
        timeToLiveSeconds="3600"
        memoryStoreEvictionPolicy="LRU" />
    <cache
        name="userCache"
        eternal="false"
        maxElementsInMemory="200"
        overflowToDisk="false"
        diskPersistent="false"
        timeToIdleSeconds="3600"
        timeToLiveSeconds="3600"
        memoryStoreEvictionPolicy="LRU" />
    <cache
        name="orgCache"
        eternal="false"
        maxElementsInMemory="200"
        overflowToDisk="false"
        diskPersistent="false"
        timeToIdleSeconds="3600"
        timeToLiveSeconds="3600"
        memoryStoreEvictionPolicy="LRU" />
</ehcache>

In above example, we configured two cache named "userCache" and "orgCache".
The file can be put in classpath or any location.


Tip:

If the system is developed using Spring, there is another way to configure the cache, which is more preferable. Example below.


b. Create spring context file for Ehcache and added it into spring context.

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

    <cache:annotation-driven cache-manager="cacheManager" />

    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cache-manager-ref="ehcache" />

    <bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:config-location="classpath:ehcache.xml" />
        
    <bean id="realCacheManager" factory-bean="cacheManager" factory-method="getCacheManager" />
    
    <!-- If cache is not configured in ehcache.xml, can also be created here -->
    <bean id="userCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
        <property name="cacheManager" ref="realCacheManager" />
        <property name="eternal" value="false" />
        <property name="maxElementsInMemory" value="${user_cache_max_elements_in_memory}" />
        <property name="overflowToDisk" value="false" />
        <property name="diskPersistent" value="false" />
        <property name="timeToIdle" value="${user_cache_time_to_idle}" />
        <property name="timeToLive" value="${user_cache_time_to_live}" />
        <property name="memoryStoreEvictionPolicy" value="LRU" />
    </bean>

    <bean id="orgCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
        <property name="cacheManager" ref="realCacheManager" />
        <property name="eternal" value="false" />
        <property name="maxElementsInMemory" value="${org_cache_max_elements_in_memory}" />
        <property name="overflowToDisk" value="false" />
        <property name="diskPersistent" value="false" />
        <property name="timeToIdle" value="${org_cache_time_to_idle}" />
        <property name="timeToLive" value="${org_cache_time_to_live}" />
        <property name="memoryStoreEvictionPolicy" value="LRU" />
    </bean>
</beans>

Reference:
http://ehcache.org/generated/2.10.0/html/ehc-all/#page/Ehcache_Documentation_Set%2Fco-cfgbasics_xml_configuration.html%23

c. Cache Abstraction (Since Spring 3.1)

Since 3.1, Spring added support for transparently adding caching into an existing Spring application. 
Similar to other feature in Spring, it supports annotation-based and xml-based configuration. Here we just include example for annotation-based configuration.

@Repository
public interface UserDao extends CrudRepository<User, String> {

    @Cacheable(value="userCache", key="#p0", unless="#result == null")
    Channel findByUserName(String userName);
}
@Repository
public interface OrganizationDao extends CrudRepository<Organization, String> {

    @Cacheable(value="orgCache", key="#p0", unless="#result == null")
    Organization findByName(String name);

    @CacheEvict(value="orgCache", key="#p0")
    Long deleteByName(String name);
}
Tips:

1. The key in above example is using p0 to represent the first param of the method. This is in case:

"Name of any of the method argument. If for some reason the names are not available (ex: no debug information), the argument names are also available under the a<#arg> where #arg stands for the argument index (starting from 0)."

Details on how to customize the configuration will not be included here in this tutorial, because you could almost find everything in the official document. Just do a little bit research based on your own requirement.

Spring expression language can also be used in the configuration.

Reference:


Good luck!