EHCache : Implementation of Java in-memory object cache

The in-memory caching system is designed to increase application performance by holding frequently-requested data in memory, reducing the need for database queries to get that data. For One of application requirement, I need a Java in-memory object caching that can replace HashMap based caching system. I need to extend the cache to include basic features like max size and time to live but don't bother about caching server or persistence.

Example of Cache

I have investigated following options:
  • Guava CacheBuilder - active development. See this presentation.
  • LRUMap - Config via API. No TTL. Not purpose built for caching.
  • whirlycache - XML config. Mailing list. Last updated 2006.
  • cache4j - XML config. Documentation in Russian. Last updated 2006.
I found that  EHCache is very nice where I can create an in memory cache. I have checked out their code samples for an example of creating an in memory cache. I can specify a max size, and a time to live, it is really awesome. EHCache does offer some advanced features, but if your not interested in using them - don't. But it's nice to know they are there if your requirements ever change.

Ehcache is an open source, standards-based cache that boosts performance, offloads your database, and simplifies scalability. It's the most widely-used Java-based cache because it's robust, proven, full-featured, and integrates with other popular libraries and frameworks.

 So let discuss the implementation part of EHCache in your application. First, we need to download the ehcache-core-2.6.2.jar, slf4j-api-1.6.1.jar and slf4j-jdk14-1.6.1.jar in ehcache-core-2.6.2-distribution.tar.gz from http://ehcache.org/downloads. Then, add this jar in the librairies folder of your project \war\WEB-INF\ or in your pom.xml (MAVEN).

Configuration ehcache.xml
Create (this file could be copied from downloaded package) an ehcache.xml file in the classpath of your project. This file contains a default Cache configuration with an implicit name “default” which is a reserved cache name. This cache will be applied to caches created programmatically (using CacheManager.add(String cacheName)).
So, we will add a sample cache named “myCache1” wich will contain a maximum in memory of 10000 elements, and will expire an element if it is idle for more than 5 minutes (300 sec) and lives for more than 10 minutes (600 sec). If there are more than 10000 elements it will overflow to the disk cache, which in this configuration will go to wherever java.io.tmp is defined on your system. On a standard Linux system this will be ‘/tmp’, for Windows7 it could be ‘C:\Users\username\AppData\Local\Temp’.

Find the sample configuarion code:

<?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"
         dynamicConfig="true">


    <diskStore path="java.io.tmpdir"/>

    <defaultCache
            maxEntriesLocalHeap="10000"
            eternal="false"
            timeToIdleSeconds="120"
            timeToLiveSeconds="120"
            diskSpoolBufferSizeMB="30"
            maxEntriesLocalDisk="10000000"
            diskExpiryThreadIntervalSeconds="120"
            memoryStoreEvictionPolicy="LRU"
            statistics="false">
        <persistence strategy="localTempSwap"/>
    </defaultCache>

    <cache name="myCache1"
           maxEntriesLocalHeap="10000"
           maxEntriesLocalDisk="1000"
           eternal="false"
           diskSpoolBufferSizeMB="20"
           timeToIdleSeconds="300"
           timeToLiveSeconds="600"
           memoryStoreEvictionPolicy="LFU"
           transactionalMode="off">
        <persistence strategy="localTempSwap"/>
    </cache>
    
</ehcache>
 How to Implement in Application?
  •  First, we will create an utilitary class named CacheUtil in order to manipulate and sollicit the Ehcache
  • Get the cache instance of Ehcache via the method getCache. This method could be synchronized.

public static CacheManager cacheMgr = null;

private static Ehcache getCache(String cacheName){
 if(cacheMgr == null){
  // We could use an environment or a VM variable
  cacheMgr = CacheManager.create("...\\config\\ehcache.xml");
 }
  
 Ehcache cache = null;
 if(cacheMgr!=null){
  //cache = cacheMgr.addCacheIfAbsent(name);
  cache = cacheMgr.getEhcache(cacheName);
 }
 
 return cache;
}
  •  Get data from the cache via the method getListFromCache.
@SuppressWarnings("unchecked")
public static <T> List<T> getListFromCache(String threadName, String cacheName, String key, CacheCreation<T> cacheCreation){
 List<T> all = new ArrayList<T>();
  
 Ehcache cache = getCache(cacheName);
 Element element = null;
 if(cache!=null){
  element = cache.get(key);
 }
  
 if(element==null){
  System.out.println(threadName+" : CacheUtil.getListFromCache() : the element '"+key+"' has not been found in the cache ---> get the original data.");

  all = cacheCreation.getAll();
  cache.put(new Element(key, all));
  System.out.println(threadName+" : CacheUtil.getListFromCache() : the original data for the element '"+key+"' has been added in the cache.");


 }else{
  System.out.println(threadName+" : CacheUtil.getListFromCache() : the element '"+key+"' has been found in the cache.");

  //all = (List<T>) element.getValue();
  all = (List<T>) element.getObjectValue();
 }
 return all;

} 

  •  We have also created an abstract class CacheCreation to in anticipation of the use of cache: 

public abstract class CacheCreation<T> { 
 public abstract List<T> getAll(); 
}
  • Then, we will create an class UseCaseClass to use and check the Ehcache containing main method to create several threads soliciting the Ehcache: 
public static void main(String[] args) {
 int nbThreads = 3;
 ExecutorService execService = Executors.newFixedThreadPool(nbThreads);
  
 // Create several threads which solicit the Ehcache
 for (int i = 0; i < nbThreads; i++) {
  final int indexFinal = i;

  execService.submit(new Runnable(){
   String threadName= null;
   UseCaseClass useCaseClass = null;
    
   public void run(){
    try {
     useCaseClass = new UseCaseClass();
     threadName = "thread_"+indexFinal;
     useCaseClass.getAllData1(threadName);
     {
      int sleepTime = getRandomSleepTime(1000, 5000);
      System.out.println(threadName+" will sleep during "+sleepTime+"ms.");
      Thread.currentThread().sleep(sleepTime);
      System.out.println(threadName+" wakes up");
     }
     useCaseClass.getAllData2(threadName);
     {
      int sleepTime = getRandomSleepTime(1000, 5000);
      System.out.println(threadName+" will sleep during "+sleepTime+"ms.");
      Thread.currentThread().sleep(sleepTime);
      System.out.println(threadName+" wakes up");
     }
     useCaseClass.getAllData1(threadName);
     useCaseClass.getAllData2(threadName);
     useCaseClass.getAllData1(threadName);
     useCaseClass.getAllData2(threadName);
    } catch (Throwable e) {
     e.printStackTrace();
    }
     
   }//end-run
    
   private int getRandomSleepTime(int min, int max){
    return min + (int)(Math.random() * ((max - min) + 1));
   }

  }//end-runnable

  );//end-submit

 }//end-for
}

  • The Ehcache will be sollicited by 2 methods getAllData1 and getAllData2:
          
 public static void main(String[] args) {
 int nbThreads = 3;
 ExecutorService execService = Executors.newFixedThreadPool(nbThreads);
  
 // Create several threads which solicit the Ehcache
 for (int i = 0; i < nbThreads; i++) {
  final int indexFinal = i;

  execService.submit(new Runnable(){
   String threadName= null;
   UseCaseClass useCaseClass = null;
    
   public void run(){
    try {
     useCaseClass = new UseCaseClass();
     threadName = "thread_"+indexFinal;
     useCaseClass.getAllData1(threadName);
     {
      int sleepTime = getRandomSleepTime(1000, 5000);
      System.out.println(threadName+" will sleep during "+sleepTime+"ms.");
      Thread.currentThread().sleep(sleepTime);
      System.out.println(threadName+" wakes up");
     }
     useCaseClass.getAllData2(threadName);
     {
      int sleepTime = getRandomSleepTime(1000, 5000);
      System.out.println(threadName+" will sleep during "+sleepTime+"ms.");
      Thread.currentThread().sleep(sleepTime);
      System.out.println(threadName+" wakes up");
     }
     useCaseClass.getAllData1(threadName);
     useCaseClass.getAllData2(threadName);
     useCaseClass.getAllData1(threadName);
     useCaseClass.getAllData2(threadName);
    } catch (Throwable e) {
     e.printStackTrace();
    }
     
   }//end-run
    
   private int getRandomSleepTime(int min, int max){
    return min + (int)(Math.random() * ((max - min) + 1));
   }

  }//end-runnable

  );//end-submit

 }//end-for
}