Ehcache provides a preview implementation of JSR107 via the net.sf.cache.jcache package.
WARNING: JSR107 is still being drafted with the ehcache maintainer as Co Spec Lead. This package will continue to change until JSR107 is finalised. No attempt will be made to maintain backward compatiblity between versions of the package. It is therefore recommended to use Ehcache's proprietary API directly.
JCaches can be created in two ways:
manager in the following sample is an net.sf.ehcache.CacheManager
net.sf.jsr107cache.Cache cache = new JCache(manager.getCache("sampleCacheNoIdle"), null);
This is the recommended way of using JCache. Caches can be configured in ehcache.xml and wrapped as JCaches with the getJCache method of CacheManager.
manager in the following sample is an net.sf.ehcache.CacheManager
net.sf.jsr107cache.Cache cache = manager.getJCache("sampleCacheNoIdle");
manager in the following sample is an net.sf.ehcache.CacheManager
Ehcache ehcache = new net.sf.ehcache.Cache(...);
net.sf.jsr107cache.Cache cache = new JCache(ehcache);
manager.addJCache(cache);
Warning: The JCache CacheManager is unworkable and will very likely be dropped in the final JCache as a Class. It will likely be replaced with a CacheManager interface.
The JCache CacheManager only works as a singleton. You obtain it with getInstance
The CacheManager uses a CacheFactory to create Caches. The CacheFactory is specified using the Service Provider Interface mechanism introduced in JDK1.3.
The factory is specified in the META-INF/services/net.sf.jsr107cache.CacheFactory resource file. This would normally be packaged in a jar. The default value for the ehcache implementation is net.sf.ehcache.jcache.JCacheFactory
The configuration for a cache is assembled as a map of properties. Valid properties can be found in the JavaDoc for the JCacheFactory.createCache() method.
See the following full example.
CacheManager singletonManager = CacheManager.getInstance();
CacheFactory cacheFactory = singletonManager.getCacheFactory();
assertNotNull(cacheFactory);
Map config = new HashMap();
config.put("name", "test");
config.put("maxElementsInMemory", "10");
config.put("memoryStoreEvictionPolicy", "LFU");
config.put("overflowToDisk", "true");
config.put("eternal", "false");
config.put("timeToLiveSeconds", "5");
config.put("timeToIdleSeconds", "5");
config.put("diskPersistent", "false");
config.put("diskExpiryThreadIntervalSeconds", "120");
Cache cache = cacheFactory.createCache(config);
singletonManager.registerCache("test", cache);
Once a cache is registered in CacheManager, you get it from there.
The following example shows how to get a Cache.
manager = CacheManager.getInstance();
Ehcache ehcache = new net.sf.ehcache.Cache("UseCache", 10,
MemoryStoreEvictionPolicy.LFU,
false, null, false, 10, 10, false, 60, null);
manager.registerCache("test", new JCache(ehcache, null));
Cache cache = manager.getCache("test");
The JavaDoc is the best place to learn how to use a JCache.
The main point to remember is that JCache implements Map and that is the best way to think about it.
JCache also has some interesting asynchronous methods such as load and loadAll which can be used to preload the JCache.
If you are used to the richer API that ehcache provides, you need to be aware of some problems and limitations in the draft specification.
You can generally work around these by getting the Ehcache backing cache. You can then access the extra features available in ehcache.
Of course the biggest limitation is that JSR107 (as of Augut 2007) is a long way from final.
/**
* Gets the backing Ehcache
*/
public Ehcache getBackingCache() {
return cache;
}
The following is both a critique of JCache and notes on the Ehcache implementation. As a member of the JSR107 Expert Group these notes are also intended to be used to improve the specification.
CacheManager does not have the following features:
A property is specified in the resource services/net.sf.jsr107cache.CacheFactory for a CacheFactory.
The factory then resolves the CacheManager which must be a singleton.
A singleton CacheManager works in simple scenarios. But there are many where you want multiple CacheManagers in an application. Ehcache supports both singleton creation semantics and instances and defines the way both can coexist.
The singleton CacheManager is a limitation of the specification.
(Alternatives: Some form of annotation and injection scheme)
Pending a final JSR107 implementation, the ehcache configuration mechanism is used to create JCaches from ehcache.xml config.
cache.put(null, null);
assertNull(cache.get(null));
cache.put(null, "value");
assertEquals("value", cache.get(null));
cache.put("key", null);
assertEquals(null, cache.get("key"));
null is effectively a valid key. However because null id not an instance of Serializable null-keyed entries will be limited to in-process memory.
To avoid running out of threads, these load requests need to be queued and use a finite number of threads. The ehcache implementation does that. However, due to the lack of lifecycle management, there is no immediate way to free resources such as thread pools.
/**
* Returns a collection view of the values contained in this map. The
* collection is backed by the map, so changes to the map are reflected in
* the collection, and vice-versa. If the map is modified while an
* iteration over the collection is in progress (except through the
* iterator's own <tt>remove</tt> operation), the results of the
* iteration are undefined. The collection supports element removal,
* which removes the corresponding mapping from the map, via the
* <tt>Iterator.remove</tt>, <tt>Collection.remove</tt>,
* <tt>removeAll</tt>, <tt>retainAll</tt> and <tt>clear</tt> operations.
* It does not support the add or <tt>addAll</tt> operations.
* <p/>
*
* @return a collection view of the values contained in this map.
*/
public Collection values() {
It is not practical or desirable to support this contract. Ehcache has multiple maps for storage of elements so there is no single backing map. Allowing changes to propagate from a change in the collection maps would break the public interface of the cache and introduce subtle threading issues.
The ehcache implementation returns a new collection which is not connected to internal structures in ehcache.
Once you get to Integer.MAX_VALUE the counter rolls over. See the following test:
public void testIntOverflow() {
long value = Integer.MAX_VALUE;
value += Integer.MAX_VALUE;
value += 5;
LOG.info("" + value);
int valueAsInt = (int) value;
LOG.info("" + valueAsInt);
assertEquals(3, valueAsInt);
}
public interface CacheStatistics {
public static final int STATISTICS_ACCURACY_NONE = 0;
public static final int STATISTICS_ACCURACY_BEST_EFFORT = 1;
public static final int STATISTICS_ACCURACY_GUARANTEED = 2;
public int getStatisticsAccuracy();
public int getObjectCount();
public int getCacheHits();
public int getCacheMisses();
public void clearStatistics();
Ehcache supports this behaviour.
But what if you are really using it as a value object and have serialized it? The ehcache implementation marks the Cache reference as transient . If clearStatistics() is called when the cache reference is no longer there, an IllegalStateException is thrown.
A much better solution would be to move clearStatistics() to Cache.
/**
* Interface describing various events that can happen as elements are added to
* or removed from a cache
*/
public interface CacheListener {
/** Triggered when a cache mapping is created due to the cache loader being consulted */
public void onLoad(Object key);
/** Triggered when a cache mapping is created due to calling Cache.put() */
public void onPut(Object key);
/** Triggered when a cache mapping is removed due to eviction */
public void onEvict(Object key);
/** Triggered when a cache mapping is removed due to calling Cache.remove() */
public void onRemove(Object key);
public void onClear();
}
JSR107 is silent on JMX which has been included in the JDK since 1.5.