Ehcache is an open source, standards-based cache for boosting performance, offloading your database, and simplifying scalability. It’s the most widely-used Java-based cache because it’s robust, proven, and full-featured. Ehcache scales from in-process, with one or more nodes, all the way to mixed in-process/out-of-process configurations with terabyte-sized caches. For applications needing a coherent distributed cache, Ehcache uses the open source Terracotta Sever Array.
Download
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’.
<?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>
Use / Utilization
To prove the effectiveness of Ehcache, we need create several classes.
- 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. More, to specify the ehcache configuration file, we could use an environment or a VM variable:
·
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.
This method could be synchronized. In anticipation of its use, this method is
waiting a threadName argument.
@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:
private static final String CACHE_NAME =
"myCache1";
public List<String> getAllData1(final String
threadName){
return
CacheUtil.getListFromCache(threadName, CACHE_NAME, "data1", new
CacheCreation<String>(){
@Override
public
List<String> getAll(){
System.out.println(threadName+"
: UseCaseClass.getAllData1() : the target original method is called to get the
values.");
List<String>
list = new ArrayList<String>();
list.add("data1-value1");
list.add("data1-value2");
list.add("data1-value3");
list.add("data1-value4");
return
list;
}
});
}
public List<String> getAllData2(final String
threadName){
return
CacheUtil.getListFromCache(threadName, CACHE_NAME, "data2", new
CacheCreation<String>(){
@Override
public
List<String> getAll(){
System.out.println(threadName+"
: UseCaseClass.getAllData2() : the target original method is called to get the
values.");
List<String>
list = new ArrayList<String>();
list.add("data2-value1");
list.add("data2-value2");
list.add("data2-value3");
list.add("data2-value4");
return
list;
}
});
}
…So,
if we execute the main method:
- with the following parameters in ehcache.xml (the
cache will expire an element in memory if it is idle for more than 5
minutes and lives for more than 10 minutes):
·
<cache
name="myCache1"
·
maxEntriesLocalHeap="10000"
·
maxEntriesLocalDisk="1000"
·
eternal="false"
·
diskSpoolBufferSizeMB="20"
·
timeToIdleSeconds="300"
·
timeToLiveSeconds="600"
·
memoryStoreEvictionPolicy="LFU"
·
transactionalMode="off">
·
<persistence
strategy="localTempSwap"/>
·
</cache>
…we obtain the below results – at the end, the
elements ‘data1′ and ‘data2′ have been found in the cache-:
thread_0 : CacheUtil.getListFromCache() : the
element 'data1' has not been found in the cache ---> get the original data.
thread_0 : UseCaseClass.getAllData1() : the target
original method is called to get the values.
thread_1 : CacheUtil.getListFromCache() : the
element 'data1' has been found in the cache.
thread_1 will sleep during 3820ms.
thread_0 : CacheUtil.getListFromCache() : the
original data for the element 'data1' has been added in the cache.
thread_0 will sleep during 3252ms.
thread_2 : CacheUtil.getListFromCache() : the
element 'data1' has been found in the cache.
thread_2 will sleep during 2626ms.
thread_2 wakes up
thread_2 : CacheUtil.getListFromCache() : the
element 'data2' has not been found in the cache ---> get the original data.
thread_2 : UseCaseClass.getAllData2() : the target
original method is called to get the values.
thread_2 : CacheUtil.getListFromCache() : the
original data for the element 'data2' has been added in the cache.
thread_2 will sleep during 3622ms.
thread_0 wakes up
thread_0 : CacheUtil.getListFromCache() : the
element 'data2' has been found in the cache.
thread_0 will sleep during 1956ms.
thread_1 wakes up
thread_1 : CacheUtil.getListFromCache() : the
element 'data2' has been found in the cache.
thread_1 will sleep during 2747ms.
thread_0 wakes up
thread_0 : CacheUtil.getListFromCache() : the
element 'data1' has been found in the cache.
thread_0 : CacheUtil.getListFromCache() : the
element 'data2' has been found in the cache.
thread_0 : CacheUtil.getListFromCache() : the
element 'data1' has been found in the cache.
thread_0 : CacheUtil.getListFromCache() : the
element 'data2' has been found in the cache.
thread_2 wakes up
thread_2 : CacheUtil.getListFromCache() : the
element 'data1' has been found in the cache.
thread_2 : CacheUtil.getListFromCache() : the
element 'data2' has been found in the cache.
thread_2 : CacheUtil.getListFromCache() : the
element 'data1' has been found in the cache.
thread_2 : CacheUtil.getListFromCache() : the
element 'data2' has been found in the cache.
thread_1 wakes up
thread_1 : CacheUtil.getListFromCache() : the
element 'data1' has been found in the cache.
thread_1 : CacheUtil.getListFromCache() : the
element 'data2' has been found in the cache.
thread_1 : CacheUtil.getListFromCache() : the
element 'data1' has been found in the cache.
thread_1 : CacheUtil.getListFromCache() : the
element 'data2' has been found in the cache.
- with the following parameters in ehcache.xml (the
cache will expire an element in memory if it is idle for more than 1
seconds and lives for more than 2 seconds):
·
<cache
name="myCache1"
·
maxEntriesLocalHeap="10000"
·
maxEntriesLocalDisk="1000"
·
eternal="false"
·
diskSpoolBufferSizeMB="20"
·
timeToIdleSeconds="1"
·
timeToLiveSeconds="2"
·
memoryStoreEvictionPolicy="LFU"
·
transactionalMode="off">
·
<persistence
strategy="localTempSwap"/>
·
</cache>
…we obtain the below results – at the end, the
elements ‘data1′ and ‘data2′ have been found in the cache, but they expire
frequently-:
thread_2 : CacheUtil.getListFromCache() : the
element 'data1' has not been found in the cache ---> get the original data.
thread_2 : UseCaseClass.getAllData1() : the target
original method is called to get the values.
thread_1 : CacheUtil.getListFromCache() : the
element 'data1' has not been found in the cache ---> get the original data.
thread_1 : UseCaseClass.getAllData1() : the target
original method is called to get the values.
thread_0 : CacheUtil.getListFromCache() : the
element 'data1' has not been found in the cache ---> get the original data.
thread_0 : UseCaseClass.getAllData1() : the target
original method is called to get the values.
thread_2 : CacheUtil.getListFromCache() : the
original data for the element 'data1' has been added in the cache.
thread_2 will sleep during 3449ms.
thread_1 : CacheUtil.getListFromCache() : the
original data for the element 'data1' has been added in the cache.
thread_1 will sleep during 1335ms.
thread_0 : CacheUtil.getListFromCache() : the
original data for the element 'data1' has been added in the cache.
thread_0 will sleep during 2558ms.
thread_1 wakes up
thread_1 : CacheUtil.getListFromCache() : the
element 'data2' has not been found in the cache ---> get the original data.
thread_1 : UseCaseClass.getAllData2() : the target
original method is called to get the values.
thread_1 : CacheUtil.getListFromCache() : the
original data for the element 'data2' has been added in the cache.
thread_1 will sleep during 3509ms.
thread_0 wakes up
thread_0 : CacheUtil.getListFromCache() : the
element 'data2' has not been found in the cache ---> get the original data.
thread_0 : UseCaseClass.getAllData2() : the target
original method is called to get the values.
thread_0 : CacheUtil.getListFromCache() : the
original data for the element 'data2' has been added in the cache.
thread_0 will sleep during 1451ms.
thread_2 wakes up
thread_2 : CacheUtil.getListFromCache() : the
element 'data2' has been found in the cache.
thread_2 will sleep during 3111ms.
thread_0 wakes up
thread_0 : CacheUtil.getListFromCache() : the
element 'data1' has not been found in the cache ---> get the original data.
thread_0 : UseCaseClass.getAllData1() : the target
original method is called to get the values.
thread_0 : CacheUtil.getListFromCache() : the
original data for the element 'data1' has been added in the cache.
thread_0 : CacheUtil.getListFromCache() : the
element 'data2' has been found in the cache.
thread_0 : CacheUtil.getListFromCache() : the
element 'data1' has been found in the cache.
thread_0 : CacheUtil.getListFromCache() : the
element 'data2' has been found in the cache.
thread_1 wakes up
thread_1 : CacheUtil.getListFromCache() : the
element 'data1' has been found in the cache.
thread_1 : CacheUtil.getListFromCache() : the
element 'data2' has not been found in the cache ---> get the original data.
thread_1 : UseCaseClass.getAllData2() : the target
original method is called to get the values.
thread_1 : CacheUtil.getListFromCache() : the
original data for the element 'data2' has been added in the cache.
thread_1 : CacheUtil.getListFromCache() : the
element 'data1' has been found in the cache.
thread_1 : CacheUtil.getListFromCache() : the
element 'data2' has been found in the cache.
thread_2 wakes up
thread_2 : CacheUtil.getListFromCache() : the
element 'data1' has not been found in the cache ---> get the original data.
thread_2 : UseCaseClass.getAllData1() : the target
original method is called to get the values.
thread_2 : CacheUtil.getListFromCache() : the
original data for the element 'data1' has been added in the cache.
thread_2 : CacheUtil.getListFromCache() : the
element 'data2' has not been found in the cache ---> get the original data.
thread_2 : UseCaseClass.getAllData2() : the target
original method is called to get the values.
thread_2 : CacheUtil.getListFromCache() : the
original data for the element 'data2' has been added in the cache.
thread_2 : CacheUtil.getListFromCache() : the
element 'data1' has been found in the cache.
thread_2 : CacheUtil.getListFromCache() : the
element 'data2' has been found in the cache.
We
change the parameters in ehcache.xml (the
cache will expire an element in memory if it is idle for more than 5 minutes
and lives for more than 10 minutes):
<cache
name="myCache1"
maxEntriesLocalHeap="10000"
maxEntriesLocalDisk="1000"
eternal="false"
diskSpoolBufferSizeMB="20"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU"
transactionalMode="off">
<persistence strategy="localTempSwap"/>
</cache>
…
and we change the method getCache to
override the parameters from ehcache.xml:
Ehcache cache = null;
if(cacheMgr!=null){
//cache
= cacheMgr.addCacheIfAbsent(name);
cache
= cacheMgr.getEhcache(cacheName);
//It
is possible to override the parameters from ehcache.xml
cache.getCacheConfiguration().setTimeToIdleSeconds(1);
cache.getCacheConfiguration().setTimeToLiveSeconds(2);
}
…we
obtain the below results – at the end, the elements ‘data1′ and ‘data2′ have
been found in the cache, but they expire frequently-:
thread_1 : CacheUtil.getListFromCache() : the
element 'data1' has not been found in the cache ---> get the original data.
thread_1 : UseCaseClass.getAllData1() : the target
original method is called to get the values.
thread_2 : CacheUtil.getListFromCache() : the
element 'data1' has not been found in the cache ---> get the original data.
thread_2 : UseCaseClass.getAllData1() : the target
original method is called to get the values.
thread_0 : CacheUtil.getListFromCache() : the
element 'data1' has not been found in the cache ---> get the original data.
thread_0 : UseCaseClass.getAllData1() : the target
original method is called to get the values.
thread_0 : CacheUtil.getListFromCache() : the
original data for the element 'data1' has been added in the cache.
thread_0 will sleep during 3295ms.
thread_1 : CacheUtil.getListFromCache() : the
original data for the element 'data1' has been added in the cache.
thread_1 will sleep during 1603ms.
thread_2 : CacheUtil.getListFromCache() : the
original data for the element 'data1' has been added in the cache.
thread_2 will sleep during 4515ms.
thread_1 wakes up
thread_1 : CacheUtil.getListFromCache() : the
element 'data2' has not been found in the cache ---> get the original data.
thread_1 : UseCaseClass.getAllData2() : the target
original method is called to get the values.
thread_1 : CacheUtil.getListFromCache() : the
original data for the element 'data2' has been added in the cache.
thread_1 will sleep during 4186ms.
thread_0 wakes up
thread_0 : CacheUtil.getListFromCache() : the
element 'data2' has not been found in the cache ---> get the original data.
thread_0 : UseCaseClass.getAllData2() : the target
original method is called to get the values.
thread_0 : CacheUtil.getListFromCache() : the
original data for the element 'data2' has been added in the cache.
thread_0 will sleep during 1049ms.
thread_0 wakes up
thread_0 : CacheUtil.getListFromCache() : the
element 'data1' has not been found in the cache ---> get the original data.
thread_0 : UseCaseClass.getAllData1() : the target
original method is called to get the values.
thread_0 : CacheUtil.getListFromCache() : the
original data for the element 'data1' has been added in the cache.
thread_0 : CacheUtil.getListFromCache() : the
element 'data2' has not been found in the cache ---> get the original data.
thread_0 : UseCaseClass.getAllData2() : the target
original method is called to get the values.
thread_0 : CacheUtil.getListFromCache() : the
original data for the element 'data2' has been added in the cache.
thread_0 : CacheUtil.getListFromCache() : the
element 'data1' has been found in the cache.
thread_0 : CacheUtil.getListFromCache() : the
element 'data2' has been found in the cache.
thread_2 wakes up
thread_2 : CacheUtil.getListFromCache() : the
element 'data2' has been found in the cache.
thread_2 will sleep during 4864ms.
thread_1 wakes up
thread_1 : CacheUtil.getListFromCache() : the
element 'data1' has not been found in the cache ---> get the original data.
thread_1 : UseCaseClass.getAllData1() : the target
original method is called to get the values.
thread_1 : CacheUtil.getListFromCache() : the original
data for the element 'data1' has been added in the cache.
thread_1 : CacheUtil.getListFromCache() : the
element 'data2' has not been found in the cache ---> get the original data.
thread_1 : UseCaseClass.getAllData2() : the target
original method is called to get the values.
thread_1 : CacheUtil.getListFromCache() : the
original data for the element 'data2' has been added in the cache.
thread_1 : CacheUtil.getListFromCache() : the
element 'data1' has been found in the cache.
thread_1 : CacheUtil.getListFromCache() : the
element 'data2' has been found in the cache.
thread_2 wakes up
thread_2 : CacheUtil.getListFromCache() : the
element 'data1' has not been found in the cache ---> get the original data.
thread_2 : UseCaseClass.getAllData1() : the target
original method is called to get the values.
thread_2 : CacheUtil.getListFromCache() : the
original data for the element 'data1' has been added in the cache.
thread_2 : CacheUtil.getListFromCache() : the
element 'data2' has not been found in the cache ---> get the original data.
thread_2 : UseCaseClass.getAllData2() : the target
original method is called to get the values.
thread_2 : CacheUtil.getListFromCache() : the
original data for the element 'data2' has been added in the cache.
thread_2 : CacheUtil.getListFromCache() : the
element 'data1' has been found in the cache.
thread_2 : CacheUtil.getListFromCache() : the
element 'data2' has been found in the cache.