1. Based on sensitivity of data and type of transaction, this cache need to be refreshed to get fesh data. This can be achieved by writing code with timer which handles flushing data from memory every x time period.
2. Application need some data to be refreshed frequently, some need to be refreshed once a day. So cache should be able to load different type of data based on configurable time.
There are lot of open source cache solution available in market, I found this link very interesting. I decided to use ehCache for our application, because it uses SelfPopulating design pattern, to load data automatically once it expires.
Below steps explains step by step coding steps,
Business problem: Category table values changes less frequently in application, so application can keep this in memory for operations and this need to be refreshed every five minutes to avoid stale data.
- Define a class which implements CacheEntryFactory (refer FactoryPattern – CacheFactory.java) – this is the class which loads data from Database
- Write a Singleton to initialize and get the values from cache CacheLoader – Add logic to use SelfPopulatingCache. This class will load new values whenever the object expires and added back again.
- Configure ecache.xml to specify the cache name and time period
- Test program (how to get the values – TestCache.java )
Step 1: Download ehcache code from repository . Keep ehcache-x.x.x.jar and commons-logging.jar as part of application lib folder
Step 2: Create java class CacheFactory.java which defines code to connect to java and get the result
package com.test.cache;
import java.util.HashSet;
import java.util.Hashtable;
import net.sf.ehcache.constructs.blocking.CacheEntryFactory;
public class CacheFactory implements CacheEntryFactory {
public Object createEntry(Object key) throws Exception {
if("catagory".equals(key))
return loadCatagory();
else if("country".equals(key))
return loadCountry();
return null;
}
public static HashSet loadCatagory(){
System.out.println("Calling and populating Catagory");
HashSet catagoryHashSet = new HashSet();
//Your DB code goes here
catagoryHashSet.add("veg");
catagoryHashSet.add("fruit");
return catagoryHashSet;
}
private static Hashtable loadCountry(){
System.out.println("Calling and populating Country");
Hashtable countryHashtable = new Hashtable();
//Your DB code goes here
countryHashtable.put("01", "USA");
countryHashtable.put("91", "India");
return countryHashtable;
}
}
Step 3: Create java class CacheLoader.java singleton which loads object using cache loader
package com.test.cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.constructs.blocking.SelfPopulatingCache;
public class CacheLoader {
private static CacheLoader cacheLoader = null;
protected CacheManager manager;
protected SelfPopulatingCache selfPopulatingCache;
protected Ehcache cache;
private CacheLoader(){
manager = new CacheManager();
cache = manager.getCache("CACHETEST");
selfPopulatingCache = new SelfPopulatingCache(cache, new CacheFactory());
}
public static CacheLoader getInstance(){
synchronized(CacheLoader.class){
if(cacheLoader == null){
cacheLoader = new CacheLoader();
}
}
return cacheLoader;
}
public Object get(Object key){
return selfPopulatingCache.get(key);
}
public void refresh(){
selfPopulatingCache.refresh();
}
}
Step 4: Place ecahce.xml under classpath and define cache name and time to live, here i have set 4 seconds
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd">
<diskStore path="java.io.tmpdir"/>
<cacheManagerEventListenerFactory class="" properties=""/>
<!--
Mandatory Default Cache configuration. These settings will be applied to caches
created programmtically using CacheManager.add(String cacheName).
The defaultCache has an implicit name "default" which is a reserved cache name.
-->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
diskSpoolBufferSizeMB="30"
maxElementsOnDisk="10000000"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU"
/>
<cache name="CACHETEST"
maxElementsInMemory="100"
timeToIdleSeconds="4"
timeToLiveSeconds="4"
eternal="false"
overflowToDisk="false"
memoryStoreEvictionPolicy="FIFO"
/>
</ehcache>
Step 5: Write Test class TestCache.java which calls CacheLoader class to get values with 1 sec delay. Method gets executed only after 4 secs, check "Calling and populating Catagory" message.
package com.test.cache;
package com.test.cache;
public class TestCache {
public static void main(String[] arg) throws Exception{
for(int i = 0 ; i < 10 ; i++ ){
System.out.println(CacheLoader.getInstance().get("catagory"));
System.out.println(CacheLoader.getInstance().get("country"));
Thread.sleep(1000);
}
}
}
Result:
Calling and populating Catagory
[ key = catagory, value=[veg, fruit], version=1, hitCount=0, CreationTime = 1192975672420, LastAccessTime = 0 ]
Calling and populating Country
[ key = country, value={01=USA, 91=India}, version=1, hitCount=0, CreationTime = 1192975672420, LastAccessTime = 0 ]
[ key = catagory, value=[veg, fruit], version=1, hitCount=1, CreationTime = 1192975672420, LastAccessTime = 1192975673421 ]
[ key = country, value={01=USA, 91=India}, version=1, hitCount=1, CreationTime = 1192975672420, LastAccessTime = 1192975673421 ]
[ key = catagory, value=[veg, fruit], version=1, hitCount=2, CreationTime = 1192975672420, LastAccessTime = 1192975674412 ]
[ key = country, value={01=USA, 91=India}, version=1, hitCount=2, CreationTime = 1192975672420, LastAccessTime = 1192975674412 ]
[ key = catagory, value=[veg, fruit], version=1, hitCount=3, CreationTime = 1192975672420, LastAccessTime = 1192975675414 ]
[ key = country, value={01=USA, 91=India}, version=1, hitCount=3, CreationTime = 1192975672420, LastAccessTime = 1192975675414 ]
[ key = catagory, value=[veg, fruit], version=1, hitCount=4, CreationTime = 1192975672420, LastAccessTime = 1192975676415 ]
[ key = country, value={01=USA, 91=India}, version=1, hitCount=4, CreationTime = 1192975672420, LastAccessTime = 1192975676415 ]
Calling and populating Catagory
[ key = catagory, value=[veg, fruit], version=1, hitCount=0, CreationTime = 1192975677417, LastAccessTime = 0 ]
Calling and populating Country
[ key = country, value={01=USA, 91=India}, version=1, hitCount=0, CreationTime = 1192975677427, LastAccessTime = 0 ]
[ key = catagory, value=[veg, fruit], version=1, hitCount=1, CreationTime = 1192975677417, LastAccessTime = 1192975678428 ]
[ key = country, value={01=USA, 91=India}, version=1, hitCount=1, CreationTime = 1192975677427, LastAccessTime = 1192975678428 ]
[ key = catagory, value=[veg, fruit], version=1, hitCount=2, CreationTime = 1192975677417, LastAccessTime = 1192975679430 ]
[ key = country, value={01=USA, 91=India}, version=1, hitCount=2, CreationTime = 1192975677427, LastAccessTime = 1192975679430 ]
[ key = catagory, value=[veg, fruit], version=1, hitCount=3, CreationTime = 1192975677417, LastAccessTime = 1192975680431 ]
[ key = country, value={01=USA, 91=India}, version=1, hitCount=3, CreationTime = 1192975677427, LastAccessTime = 1192975680431 ]
Calling and populating Catagory
[ key = catagory, value=[veg, fruit], version=1, hitCount=0, CreationTime = 1192975681433, LastAccessTime = 0 ]
Calling and populating Country
[ key = country, value={01=USA, 91=India}, version=1, hitCount=0, CreationTime = 1192975681433, LastAccessTime = 0 ]
Conclusion:
Multiple cache can be defined in ecache.xml and can be used easily. This keeps cache code clearly separated from main business logic in application.
1 comment:
Hey Pandi,
Take a look at this also http://www.danga.com/memcached/
Post a Comment