Hi,
After my post about a homemade solution of Data caching (Java/Spring: Data caching – Intercept method calls and put their returns in server-side cache with AOP/MBEAN/JCONSOLE), you would introduce a simple use of Ehcache framework.
Presentation
I am also beginner in the use of Ehcache, so, for more information,
http://ehcache.org/:
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.
In this post, I have presented briefly the use of Ehcache and a bit of code to override the declared parameters programmatically.
Source: test_divers-EHCACHE.zip
That’s all!!!
Huseyin OZVEREN
Hi Huseyin OZVEREN, i really like this post, because i’m doing a project of this framework!
i was download your source but i can not run this,
this expeare the error “net.sf.ehcache.CacheException: Error configuring from C:\MyFiles\Development\Java\dev\test_divers\src\com\ho\ehcache\test1\config\ehcache.xml. Initial cause was C:\MyFiles\Development\Java\dev\test_divers\src\com\ho\ehcache\test1\config\ehcache.xml (The system cannot find the path specified)
at net.sf.ehcache.config.ConfigurationFactory.parseConfiguration(ConfigurationFactory.java:80)
at net.sf.ehcache.CacheManager.newInstance(CacheManager.java:872)
at net.sf.ehcache.CacheManager.create(CacheManager.java:852)
at com.ho.ehcache.test1.util.CacheUtil.getCache(CacheUtil.java:54)
at com.ho.ehcache.test1.util.CacheUtil.getListFromCache(CacheUtil.java:27)
at com.ho.ehcache.test1.uses.UseCaseClass.getAllData1(UseCaseClass.java:24)
at com.ho.ehcache.test1.uses.UseCaseClass$3.run(UseCaseClass.java:75)
at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.util.concurrent.FutureTask$Sync.innerRun(Unknown Source)
at java.util.concurrent.FutureTask.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.io.FileNotFoundException: C:\MyFiles\Development\Java\dev\test_divers\src\com\ho\ehcache\test1\config\ehcache.xml (The system cannot find the path specified)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.(Unknown Source)
at net.sf.ehcache.config.ConfigurationFactory.parseConfiguration(ConfigurationFactory.java:77)
… 12 more”!
So, i don’t known how to debug this problem…! Can you help me, please!!!
Hi Tài,
The error is very explicit:
Caused by: java.io.FileNotFoundException: C:\MyFiles\Development\Java\dev\test_divers\src\com\ho\ehcache\test1\config\ehcache.xml (The system cannot find the path specified)
…the “ehcache.xml” file is used in the class “CacheUtil” in order to get the cache instance of Ehcache “CacheManager” in the method “getCache(String cacheName)”:
if(cacheMgr == null){
// We could use an environment or a VM variable
cacheMgr = CacheManager.create(“C:\\MyFiles\\Development\\Java\\dev\\test_divers\\src\\com\\ho\\ehcache\\test1\\config\\ehcache.xml”);
}
Best regards,
thank you for your answer!
but Huseyin OZVEREN , i have the trouble that!
I download your source and extract then put it in the workspace in the Eclipse to run, i think that, just all your source you do that completed and you run successfull, so i can too…
the problem that why i can not run, because the path of file ehcache.xml is in your source, i must to edit the code where to debug…
Please help me Ozveren, thank you so much…
p/s: my English skill is not good so you fogive me ^_^, tks agains…
Hi Tài,
Really, i don’t understand your message, the file “ehcache.xml” is in the folder “test_divers-EHCACHE\src\com\ho\ehcache\test1\config\”
You could change the path of this configuration file in the class “CacheUtil”.
Best regards,
Hi Huseyin OZVEREN, i had edit the path of this configuration file like the image behind (you can see the image i’d up on the link)!
http://www.upanh.com/slider/?e=malihu&s=upload&start=arv45y3d5ej
But it still show the error!
Thank for reading my comment!
Hi Tài,
The submitted image does not reachable.
Best regards,
Dear OZVEREN.
i have done it, and it work good!
thank you so much!
And i have a question, we can create an example to show the effectiveness of ehcache, we just create 2 hello.jsp simple project, and include the ehcache, another is not include the ehcache, so run both them and timing it to see the time of them run!
thank to read my idea ^^!
Best regards,
i don’t know while in the two method getAlldata1&2 you create the list and put it the data-value but for what????????
Hi!,
I am trying to write a test class in java to replicate Ehcaches’ feature of overflow data to disk if in-memory cache size is reached.
I dont want any xml file configuration, so i tried configuring using code . I tried a lot so that cache writes into disk and i read it from disk if it is not present in-memory.
Can anyone please help ?
Hi,
You have done a good job. I liked your blog.
another good example for web page caching I liked is http://blaze-core.com/using-ehcache-for-web-page-caching/
Thanks
Hi,
Thank you for your message, I didn’t post new article from several weeks, but, currently, I don’t have really any time because of personal projects.
I hope that i will have more time next year.
Kind regards,
Huseyin
Hello!
Nice article! The source code link seems broken.
Best Regards!
Hello Luigi,
Thanks for your message,
Could you explain the breaking point of source?
Kind regards,
Great Explanation
You are a saviour. Thanks a ton!
Very nice article.
A more basic quesiton .
To use enchache with tomcat , what do I have to do ?
1. Copy all necessary jars to the lib folder
2. create ehcache.xml and put it on the classpath.
But where should create the cache i.e
where should I put this code
CacheManager.create() ?
So, if I have a web application that needs to use a cache, then should I create this cache in ServletContext class and destroy when the application is brought down ?
Thanks !
Hi,
Thank you for your message,
The creation of cache could be put in a singleton utilitary class (in my example named CacheUtil) in order to put/get values to/from cache via by static method.
In first access to cache, this utilitary class will create the cache.
Indeed, you could make the first call to this utilitary class in ServletContext.
Best regards,