Ehcache Example (original) (raw)

Below is a cache example that uses the popular Java Ehcache system.

Replace the code inside ServiceHelper.CreateCache with initialization of the Ehcache object as shown in the example.

// Create Ehcache manager final CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() .withCache("preConfigured", CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, Object.class, ResourcePoolsBuilder.newResourcePoolsBuilder().heap(1, MemoryUnit.GB))) .build(); cacheManager.init(); // Cache event listener for cleaning up resources final CacheEventListener<String, Object> cacheEventListener = new CacheEventListener<String, Object>() { @Override public void onEvent(CacheEvent<? extends String, ? extends Object> event) { // event.getType will only be removed/evicted/expired/updated, so dispose old value EhcacheObjectCache.tryDispose(event.getOldValue()); } }; // Create a new cache object Cache<String, Object> cache = cacheManager.getCache("preConfigured", String.class, Object.class); // Enable trying to auto-disposing cache objects that holds unmanaged memory when items are removed/evicted/expired/updated // from the cache. cache.getRuntimeConfiguration().registerCacheEventListener(cacheEventListener, EventOrdering.ORDERED, EventFiring.SYNCHRONOUS, EnumSet.of(EventType.EVICTED, EventType.REMOVED, EventType.EXPIRED, EventType.UPDATED)); // Create a leadtools.document.ObjectCache wrapper for our Ehcache object ObjectCache objectCache = new EhcacheObjectCache(cache);

Set objectCache into the global _cache object in the service. Below is the implementation of EhcacheObjectCache

import java.net.URI; import java.util.EnumSet; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; import org.ehcache.Cache; import leadtools.caching.CacheItem; import leadtools.caching.CacheItemExpiredEvent; import leadtools.caching.CacheItemExpiringEvent; import leadtools.caching.CacheItemPolicy; import leadtools.caching.CacheSerializationMode; import leadtools.caching.CacheStatistics; import leadtools.caching.DefaultCacheCapabilities; import leadtools.caching.EnumerateCacheEntriesCallback; import leadtools.caching.ObjectCache; /* Implementation of leadtools.caching.ObjectCache that wraps an org.ehcache.Cache object 1. Create or load an instance Cache. 2. Create an instance of EhcacheObjectCache to wrap it 3. Pass it to a LEADTOOLS Document toolkit that expects a CacheObject. For instance, DocumentFactory.loadFromUrl or DocumentFactory.loadFromCache. Then everything should work as expected 4. To delete a document from the cache, use DocumentFactory.deleteFromCache This implementation does not support individual expiration policies for each item since this is not supported by Ehcache. Instead, it will use the policies already set up in the original Ehcache object passed. The document toolkit will call the following methods of ObjectCache: - getDefaultCacheCapabilities to get the kind of support of this cache. We support minimal requirement of get/set only (no regions, external URLs, disk access nor auto-serialization). - addOrGetExisting: To add items to the cache use the following parameters: item.regionName: Will always be the document ID (leadtools.document.Document.getDocumentId) item.keyName: Unique identifier for this item, such as page1_image or document_metadata. The implementation uses regionName + "_" + keyName to create a unique identifier in the cache since true grouping is not supported by Ehcache. item.Value: The value to store, this could be: - Array of bytes: In case of original document data and annotation data - Strings: In case of raster codecs options, meta data, etc. - RasterImage and ISvgDocument objects: If DocumentCacheOptions.PAGE_IMAGE, PAGE_SVG, PAGE_SVG_BACK_IMAGE or PAGE_THUMBNAIL_IMAGE was used to cache images. If this occurs, it is recommended that the implementer calls the static tryDispose method from a CacheEventListender when items are removed, evicted, expired or updates. Refer to the example code for more information. - getCacheItem: to get items from the cache. Refer to the parameters above. - remove: to remove an item from the cache while returning the original value. - deleteItem: to delete an item from the cache quickly. - deleteAll: to delete multiple items from the cache quickly. This is called when the document is removed from the cache using Document.setAutoDeleteFromCache(true) or DocumentFactory.deleteFromCache. */ public class EhcacheObjectCache extends ObjectCache { // Helper method to call when items are removed/evicted or updated in the cache // See the sample program. This is optional and the GC can usually handle these but for optimal // implementation, try to always dispose (close) objects when they are not needed anymore public static void tryDispose(Object value) { if (value == null) return; // If the document was creating with any of the following DocumentCacheOptions.PAGE_IMAGE, PAGE_SVG, PAGE_SVG_BACK_IMAGE or PAGE_THUMBNAIL_IMAGE, then // The toolkit might add RasterImage or ISvgDocument objects to the cache. These objects must be disposed when they are no longer use // to free system resources and memory. // Note that this is optional and the GC can handle this operation automatically if needed. However, it is strongly recommended to delete // these objects as soon as they are not needed anymore. if (value instanceof leadtools.RasterImage) { ((leadtools.RasterImage)value).dispose(); } else if (value instanceof leadtools.ISvgDocument) { ((leadtools.ISvgDocument)value).dispose(); } } // Configuration options for this class public static class Configuration { // Regions support // The document toolkit will always call add, get, set, remove on the items with a region (the document ID) and a key (the item itself). The // supportsRegion field affects whether CacheObject.deleteRegion or CacheObject.deleteAll is called when the document toolkit tries to remove all // the items added for a region. Usually, as a result of calling Document.dispose with autoDeleteFromCache is true. // When the value is false (default) will instruct the toolkit to call deleteAll when the above occurs. // When the value is true, the toolkit will call deleteRegion when the above occurs. Use this mode if you are planning to use a single region per // document and add your own implementation. For example, by deleting the whole Ehcache object. This implementation is not used in this class public boolean useRegions = false; } // The Ehcache object we are wrapping private Cache<String, Object> _cache; public Cache<String, Object> cache() { return _cache; } private boolean _useRegions; public EhcacheObjectCache(Cache<String, Object> cache) { super(); init(cache, null); } public EhcacheObjectCache(Cache<String, Object> cache, EhcacheObjectCache.Configuration config) { super(); init(cache, config); } private void init(Cache<String, Object> cache, EhcacheObjectCache.Configuration config) { if (cache == null) { throw new IllegalArgumentException("Cache cannot be null"); } this._cache = cache; if (config != null) { this._useRegions = config.useRegions; } } // Helper method to resolve region and key pair. The document will refer to each item by a region (the document ID, not unique in the cache) and a key // (the value itself, unique for the document ID). Ehcache does not support that, so simply concatenate both values to generate a unique key private static String resolveKey(String key, String region) { if (region == null || region.length() == 0) { // We do not support this throw new UnsupportedOperationException("Cache region must be provided"); } return region + "_" + key; } // Not used in this implementation @Override public CacheItemExpiringEvent cacheItemExpiring() { return null; } // Not used in this implementation @Override public CacheItemExpiredEvent cacheItemExpired() { return null; } @Override public String getName() { return "Ehcache ObjectCache"; } // We only support binary serialization @Override public CacheSerializationMode getPolicySerializationMode() { return CacheSerializationMode.BINARY; } @Override public void setPolicySerializationMode(CacheSerializationMode value) { // This is never called from the document toolkit throw new UnsupportedOperationException(); } @Override public CacheSerializationMode getDataSerializationMode() { return CacheSerializationMode.BINARY; } @Override public void setDataSerializationMode(CacheSerializationMode value) { // This is never called from the document toolkit throw new UnsupportedOperationException(); } @Override public boolean contains(String key, String regionName) throws Exception { if (key == null) throw new IllegalArgumentException("Key cannot be null"); return _cache.containsKey(resolveKey(key, regionName)); } @Override public <T> T get(String key, Class<?> classOfT) throws Exception { // This is never called from the document toolkit throw new UnsupportedOperationException(); } @Override public <T> void set(String key, T value, Class<?> classOfT) throws Exception { // This is never called from the document toolkit throw new UnsupportedOperationException(); } @Override public void enumerateRegions(EnumerateCacheEntriesCallback callback) throws Exception { // This is never called from the document toolkit throw new UnsupportedOperationException(); } @Override public void enumerateKeys(String region, EnumerateCacheEntriesCallback callback) throws Exception { // This is never called from the document toolkit throw new UnsupportedOperationException(); } @Override public Map<String, Object> getValues(Iterator<String> keys, String regionName) throws Exception { // This is never called from the document toolkit throw new UnsupportedOperationException(); } @SuppressWarnings("unchecked") @Override public <T> CacheItem<T> addOrGetExisting(CacheItem<T> item, Class<?> classOfT, CacheItemPolicy policy) throws Exception { if (item == null) { throw new IllegalArgumentException("item cannot be null"); } // Get the unique key // Get the current item (if exists) // Put the new item // Return the old item // Note: policy is not used String resolvedKey = resolveKey(item.getKey(), item.getRegionName()); CacheItem<T> oldItem = null; if (_cache.containsKey(resolvedKey)) { try { Object oldValue = _cache.get(resolvedKey); oldItem = new CacheItem<T>(item.getKey(), (T) oldValue, item.getRegionName()); } catch (Exception ex) { oldItem = null; } } _cache.put(resolvedKey, item.getValue()); return oldItem; } @SuppressWarnings("unchecked") @Override public <T> CacheItem<T> getCacheItem(String key, Class<?> classOfT, String regionName) throws Exception { // Get the unique key // Get the item String resolvedKey = resolveKey(key, regionName); Object value = _cache.get(resolvedKey); if (value != null) { return new CacheItem<T>(key, (T) value, regionName); } else { return null; } } @SuppressWarnings("unchecked") @Override public <T> T remove(String key, Class<?> classOfT, String regionName) throws Exception { // Get the unique key // Get the old value // Remove the item // Return the old value String resolvedKey = resolveKey(key, regionName); Object obj = _cache.get(resolvedKey); if (obj != null) { _cache.remove(resolvedKey); } return (T) obj; } @Override public void deleteItem(String key, String regionName) throws Exception { // Get the unique key // Remove the item String resolvedKey = resolveKey(key, regionName); if (_cache.containsKey(resolvedKey)) { _cache.remove(resolvedKey); } } @Override public long getCount(String regionName) { // This is never called from the document toolkit throw new UnsupportedOperationException(); } @Override public EnumSet<DefaultCacheCapabilities> getDefaultCacheCapabilities() { EnumSet<DefaultCacheCapabilities> caps = EnumSet.of(DefaultCacheCapabilities.NONE); if (_useRegions) { // We support regions, this will cause the toolkit to call deleteRegion instead of deleteAll when // a document is deleted from the cache. caps.add(DefaultCacheCapabilities.CACHE_REGIONS); } return caps; } @Override public CacheStatistics getStatistics(String keyName, String regionName) throws Exception { // This is never called from the document toolkit throw new UnsupportedOperationException(); } @Override public CacheStatistics getStatistics() throws Exception { // This is never called from the document toolkit throw new UnsupportedOperationException(); } @Override public void deleteRegion(String regionName) throws Exception { if (!this._useRegions) { throw new UnsupportedOperationException("Method not supported. Use deleteAll"); } // One cache per document implementation. Try to delete _cache from the manage if not needed } @Override public URI getItemExternalResource(String key, String regionName, boolean readWrite) throws Exception { /// This is never called from the document toolkit since we do not support DefaultCacheCapabilities.EXTERNAL_RESOURCES throw new UnsupportedOperationException(); } @Override public void removeItemExternalResource(String key, String regionName) throws Exception { /// This is never called from the document toolkit since we do not support DefaultCacheCapabilities.EXTERNAL_RESOURCES throw new UnsupportedOperationException(); } @Override public URI beginAddExternalResource(String key, String regionName, boolean readWrite) throws Exception { /// This is never called from the document toolkit since we do not support DefaultCacheCapabilities.EXTERNAL_RESOURCES throw new UnsupportedOperationException(); } @Override public <T> void endAddExternalResource(boolean commit, String key, T value, Class<?> classOfT, CacheItemPolicy policy, String regionName) throws Exception { /// This is never called from the document toolkit since we do not support DefaultCacheCapabilities.EXTERNAL_RESOURCES throw new UnsupportedOperationException(); } @Override public <T> void updatePolicy(String key, CacheItemPolicy policy, String regionName) throws Exception { // Method not supported. Policies are set on the Cache object passed to the constructor. // This will be called from the document toolkit. But we can safely ignore it and rely on the // expiration policies always set up in the Ehcache object from outside. } @Override public <T> boolean updateCacheItem(CacheItem<T> item, Class<?> classOfT) throws Exception { // Get the unique key // If item found, update it // return status String resolvedKey = resolveKey(item.getKey(), item.getRegionName()); if (_cache.containsKey(resolvedKey)) { _cache.put(resolvedKey, item.getValue()); return true; } else { return false; } } @Override public void deleteAll(String regionName, Set<String> keys) throws Exception { if (keys == null) { return; } // Remove all the keys // Note that the document toolkit will call this method with keys for items that may not exist // in the cache, Ehcache does not have a problem with that so just call removeAll instead // of trying to iterate through the keys to check if they exist first. // Of course, convert to our key format first Set<String> resolvedKeys = new HashSet<String>(); for (String key: keys) { resolvedKeys.add(resolveKey(key, regionName)); } _cache.removeAll(resolvedKeys); } }