diff --git a/src/java/com/samskivert/depot/CacheAdapter.java b/src/java/com/samskivert/depot/CacheAdapter.java index 609ba21..3d01776 100644 --- a/src/java/com/samskivert/depot/CacheAdapter.java +++ b/src/java/com/samskivert/depot/CacheAdapter.java @@ -3,7 +3,7 @@ // // Depot library - a Java relational persistence library // Copyright (C) 2006-2008 Michael Bayne and Pär Winzell -// +// // This library is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published // by the Free Software Foundation; either version 2.1 of the License, or @@ -22,14 +22,31 @@ package com.samskivert.depot; import java.io.Serializable; +import com.samskivert.depot.impl.FindAllQuery; +import com.samskivert.util.Tuple; + /** * Implementations of this interface are responsible for all the caching needs of Depot. * - * The cache consists of many {@link CacheBin}s. Each bin its own key space. Look-ups and storage - * occur on a per-bin per-key basis. + * From the point of view of this interface, there are a potentially very large number of + * caches available, each idenfided by a unique cacheId. Currently Depot creates up to three + * caches for each record type: + * + * Any record type with a primary key has a {@link CacheCategory#RECORD} cache, for storing + * record instances by primary key. + * + * Record types with primary keys may also have a {@link CacheCategory#KEYSET} cache wherein + * {@link KeySet} instances are stored, identified by query strings. See {@link FindAllQuery} + * for more on this. + * + * Finally, clients may request {@link CacheCategory#RESULT} caching of entire result sets of + * some record type -- which does not need to have a primary key, in contrast to the other two + * categories. These are also identified by query strings, and end up in a third cache. */ public interface CacheAdapter { + public enum CacheCategory { RECORD, KEYSET, RESULT }; + /** The encapsulated result of a cache lookup. */ public interface CachedValue { @@ -38,37 +55,25 @@ public interface CacheAdapter } /** - * A reference to a specific bin within the cache; this is the type where most of the actual - * caching functionality occurs. + * Searches the given cache using the given key and returns the resulting {@link CachedValue}, + * or null if nothing exists in the cache for this key. */ - public interface CacheBin - { - /** - * Searches this bin using the given key and returns the resulting {@link CachedValue}, or - * null if nothing exists in the cache for this key. - */ - public CachedValue lookup (Serializable key); - - /** - * Stores a new value in this cache bin under the given key. - */ - public void store (Serializable key, T value); - - /** - * Removes the cache entry, if any, associated with the given key. - */ - public void remove (Serializable key); - - /** - * Provides a way to enumerate the currently cached entries in this bin. - */ - public Iterable enumerateKeys (); - } + public CachedValue lookup (String cacheId, Serializable key); /** - * Fetch the {@link CacheBin} associated with the given ID, creating one on the fly if needed. + * Stores a new value in the given cache under the given key. */ - public CacheBin getCache (String id); + public void store (CacheCategory category, String cacheId, Serializable key, T value); + + /** + * Removes the cache entry, if any, associated with the given key. + */ + public void remove (String cacheId, Serializable key); + + /** + * Provides a way to enumerate the currently cached entries for the given cache. + */ + public Iterable>> enumerate (String cacheId); /** * Shut down all operations, e.g. persisting memory contents to disk. diff --git a/src/java/com/samskivert/depot/EHCacheAdapter.java b/src/java/com/samskivert/depot/EHCacheAdapter.java index a68f850..58322a3 100644 --- a/src/java/com/samskivert/depot/EHCacheAdapter.java +++ b/src/java/com/samskivert/depot/EHCacheAdapter.java @@ -21,124 +21,108 @@ package com.samskivert.depot; import java.io.Serializable; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import com.google.common.base.Function; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import com.samskivert.util.Tuple; -import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; +import net.sf.ehcache.Ehcache; import net.sf.ehcache.Element; -import net.sf.ehcache.distribution.CacheManagerPeerListener; -import net.sf.ehcache.distribution.CacheManagerPeerProvider; -import net.sf.ehcache.distribution.RMIAsynchronousCacheReplicator; +import net.sf.ehcache.event.CacheEventListener; import static com.samskivert.depot.Log.log; /** - * An implementation of {@link CacheAdapter} for ehcache. + * An implementation of {@link CacheAdapter} for ehcache where each {@link CacheCategory} results + * in one {@link Ehcache}. All (cacheId, key) combinations within one category is stuffed into the + * same {@link Ehcache}, and all elements are cached under {@link EHCacheKey}, which basically + * wraps just such a tuple. + * + * Thus there are currently only three Ehcaches in play, called 'depotRecord', 'depotKeyset', and + * 'depotResult'. These must be defined in your ehcache.xml configuration. If you use distributed + * replication/invalidation, you should replicate updates and removes but not puts nor + * updates-via-copy. */ public class EHCacheAdapter implements CacheAdapter { + protected static final String EHCACHE_RECORD_CACHE = "depotRecord"; + protected static final String EHCACHE_KEYSET_CACHE = "depotKeyset"; + protected static final String EHCACHE_RESULT_CACHE = "depotResult"; + /** * Creates an adapter using the supplied cache manager. Note: this adapter does not shut down * the supplied manager when it is shutdown. The caller is responsible for shutting down the * cache manager when it knows that Depot and any other clients no longer need it. */ - public EHCacheAdapter (EHCacheConfig config, CacheManager cachemgr) + public EHCacheAdapter (CacheManager cachemgr) { - _config = config; - _cachemgr = cachemgr; + bindEHCache(cachemgr, CacheCategory.RECORD, EHCACHE_RECORD_CACHE); + bindEHCache(cachemgr, CacheCategory.KEYSET, EHCACHE_KEYSET_CACHE); + bindEHCache(cachemgr, CacheCategory.RESULT, EHCACHE_RESULT_CACHE); + } - if (config.distributed) { - CacheManagerPeerListener listener = _cachemgr.getCachePeerListener(); - CacheManagerPeerProvider provider = _cachemgr.getCachePeerProvider(); - if (provider == null || listener == null) { - log.warning("Distributed mode disabled. Please set a CacheManagerPeerListener " + - " and a CacheManagerPeerProvider in your ehcache.xml configuration", - "listener", listener, "provider", provider); - _config.distributed = false; - } + // from CacheAdapter + public CachedValue lookup (String cacheId, Serializable key) + { + @SuppressWarnings("unchecked") + EHCacheBin bin = (EHCacheBin) _bins.get(cacheId); + if (bin == null) { + return null; + } + return lookup(bin.getCache(), cacheId, key); + } + + // from CacheAdapter + public void store (CacheCategory category, String cacheId, Serializable key, T value) + { + Ehcache cache = _categories.get(category); + if (cache == null) { + throw new IllegalArgumentException("Unknown category: " + category); + } + @SuppressWarnings("unchecked") + EHCacheBin bin = (EHCacheBin) _bins.get(cacheId); + if (bin == null) { + bin = new EHCacheBin(cache, cacheId); + _bins.put(cacheId, bin); + } + bin.getCache().put(new Element(new EHCacheKey(cacheId, key), value != null ? value : NULL)); + } + + // from CacheAdapter + public void remove (String cacheId, Serializable key) + { + EHCacheBin bin = _bins.get(cacheId); + if (bin != null) { + bin.getCache().remove(new EHCacheKey(cacheId, key)); } } - public CacheBin getCache (String id) + // from CacheAdapter + public Iterable>> enumerate (final String cacheId) { - return new EHCacheBin(id); - } + EHCacheBin bin = _bins.get(cacheId); + if (bin == null) { + return Collections.emptySet(); + } - /** - * The main ehcache-bridging class, a {@link CacheBin} interface against {@link Cache}. - */ - protected class EHCacheBin implements CacheBin - { - // from CacheBin - public CachedValue lookup (Serializable key) - { - Element hit = _cache.get(key); - if (hit == null) { - return null; - } - - Serializable rawValue = hit.getValue(); - @SuppressWarnings("unchecked") - final T value = (T) (rawValue instanceof NullValue ? null : rawValue); - return new CachedValue() { - public T getValue () { - return value; + final Ehcache cache = bin.getCache(); + Iterable>> tuples = Iterables.transform( + bin.getKeys(), new Function>> () { + public Tuple> apply (Serializable key) { + CachedValue value = lookup(cache, cacheId, key); + return (value != null) ? Tuple.newTuple(key, value) : null; } - @Override public String toString () { - return String.valueOf(value); - } - }; - } - - // from CacheBin - public void store (Serializable key, T value) - { - _cache.put(new Element(key, value != null ? value : NULL)); - } - - // from CacheBin - public void remove (Serializable key) - { - _cache.remove(key); - } - - // from CacheBin - public Iterable enumerateKeys () - { - @SuppressWarnings("unchecked") Iterable keys = _cache.getKeys(); - return keys; - } - - protected EHCacheBin (String id) - { - synchronized (_cachemgr) { - _cache = _cachemgr.getCache(id); - if (_cache == null) { - // create the cache programatically with reasonable settings - _cache = new Cache(id, - _config.elementsInMemory, - _config.overflowToDisk, - false, - _config.timeToLiveSeconds, - _config.timeToIdleSeconds); - - if (_config.distributed) { - // a programatically created cache has to have its replicator event - // listener programatically added. - _cache.getCacheEventNotificationService().registerListener( - new RMIAsynchronousCacheReplicator( - _config.sendNewRecordsToPeers, - _config.invalidateUpdatedRecordsOnPeers, - _config.sendUpdatedRecordsToPeers, - _config.invalidateRemovedRecordsOnPeers, - _config.replicationInterval)); - } - _cachemgr.addCache(_cache); - } - } - } - - protected Cache _cache; + }); + + return Iterables.filter(tuples, Predicates.notNull()); } // from CacheAdapter @@ -146,12 +130,186 @@ public class EHCacheAdapter { } - protected EHCacheConfig _config; - protected CacheManager _cachemgr; + protected CachedValue lookup (Ehcache cache, String cacheId, Serializable key) + { + Element hit = cache.get(new EHCacheKey(cacheId, key)); + if (hit == null) { + return null; + } + + Serializable rawValue = hit.getValue(); + @SuppressWarnings("unchecked") + final T value = (T) (rawValue instanceof NullValue ? null : rawValue); + return new CachedValue() { + public T getValue () { + return value; + } + @Override public String toString () { + return String.valueOf(value); + } + }; + + } + + protected Ehcache bindEHCache (CacheManager cachemgr, CacheCategory category, String cacheName) + { + Ehcache cache = cachemgr.getCache(cacheName); + if (cache == null) { + throw new IllegalStateException( + "Could not find Ehcache '" + cacheName + "'. Please fix your ehcache configuration."); + } + cache.getCacheEventNotificationService().registerListener(_cacheEventListener); + _categories.put(category, cache); + return cache; + } + + protected Map _categories = Maps.newConcurrentHashMap(); + protected Map> _bins = Maps.newConcurrentHashMap(); + + protected CacheEventListener _cacheEventListener = new CacheEventListener() { + public Object clone () throws CloneNotSupportedException { + throw new CloneNotSupportedException(); + } + public void dispose () {} + public void notifyElementEvicted (Ehcache cache, Element element) { + log.debug("notifyElementEvicted(" + cache + ", " + element + ")"); + removeFromBin(cache, element); + } + public void notifyElementExpired (Ehcache cache, Element element) { + log.debug("notifyElementExpired(" + cache + ", " + element + ")"); + removeFromBin(cache, element); + } + public void notifyElementPut (Ehcache cache, Element element) { + log.debug("notifyElementPut(" + cache + ", " + element + ")"); + addToBin(cache, element); + } + public void notifyElementRemoved (Ehcache cache, Element element) { + log.debug("notifyElementRemoved(" + cache + ", " + element + ")"); + removeFromBin(cache, element); + } + public void notifyElementUpdated (Ehcache cache, Element element) { + log.debug("notifyElementUpdated(" + cache + ", " + element + ")"); + addToBin(cache, element); + } + public void notifyRemoveAll (Ehcache cache) {} + + protected void removeFromBin (Ehcache cache, Element element) + { + EHCacheKey key = (EHCacheKey)element.getKey(); + EHCacheBin bin = _bins.get(key.getCacheId()); + if (bin == null) { + log.warning("Dropping element removal without cache bin", "key", key); + return; + } + bin.removeKey(key.getCacheKey()); + for (Tuple> tuple : + enumerate(((EHCacheKey) element.getKey()).getCacheId())) { + log.debug("Enumeration: " + tuple); + } + } + protected void addToBin (Ehcache cache, Element element) + { + EHCacheKey key = (EHCacheKey)element.getKey(); + EHCacheBin bin = _bins.get(key.getCacheId()); + if (bin == null) { + log.warning("Dropping element addition without cache bin", "key", key); + return; + } + bin.addKey(key.getCacheKey()); + for (Tuple> tuple : + enumerate(((EHCacheKey) element.getKey()).getCacheId())) { + log.debug("Enumeration: " + tuple); + } + } + }; + + protected static class EHCacheBin + { + public EHCacheBin (Ehcache cache, String id) + { + _cache = cache; + _id = id; + } + + public Set getKeys () + { + return _keys; + } + + public void addKey (Serializable key) + { + _keys.add(key); + } + + public void removeKey (Serializable key) + { + _keys.remove(key); + } + + protected Ehcache getCache () + { + return _cache; + } + + protected Ehcache _cache; + protected String _id; + + protected Set _keys = Sets.newConcurrentHashSet(); + } // this is just for convenience and memory use; we don't rely on pointer equality anywhere protected static Serializable NULL = new NullValue() {}; + /** A class to wrap a Depot id/key into an EHCache key. */ + protected static class EHCacheKey + implements Serializable + { + public EHCacheKey (String id, Serializable key) + { + if (id == null || key == null) { + throw new IllegalArgumentException("Can't handle null key or id"); + } + _id = id; + _key = key; + } + + public String getCacheId () { + return _id; + } + + public Serializable getCacheKey () + { + return _key; + } + + @Override + public String toString () + { + return "[" + _id + ", " + _key + "]"; + } + + @Override + public int hashCode () + { + return 31 * _id.hashCode() + _key.hashCode(); + } + + @Override + public boolean equals (Object obj) + { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + return _id.equals(((EHCacheKey) obj)._id) && _key.equals(((EHCacheKey) obj)._key); + } + + protected String _id; + protected Serializable _key; + } + /** A class to represent an explicitly Serializable concept of null for EHCache. */ protected static class NullValue implements Serializable { diff --git a/src/java/com/samskivert/depot/EHCacheConfig.java b/src/java/com/samskivert/depot/EHCacheConfig.java deleted file mode 100644 index 0b70c24..0000000 --- a/src/java/com/samskivert/depot/EHCacheConfig.java +++ /dev/null @@ -1,79 +0,0 @@ -// -// $Id: EHCacheAdapter.java 325 2008-11-16 08:03:33Z samskivert $ -// -// Depot library - a Java relational persistence library -// Copyright (C) 2006-2008 Michael Bayne and Pär Winzell -// -// This library is free software; you can redistribute it and/or modify it -// under the terms of the GNU Lesser General Public License as published -// by the Free Software Foundation; either version 2.1 of the License, or -// (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -package com.samskivert.depot; - -public class EHCacheConfig -{ - /** The maximum number of cached records to keep in memory. */ - public int elementsInMemory; - - /** Whether or not to overflow records to disk. */ - public boolean overflowToDisk; - - /** How long to keep a record in the cache after it's been stored or updated. */ - public int timeToLiveSeconds; - - /** How long to keep a record in the cache after it's last accessed. */ - public int timeToIdleSeconds; - - /** Whether or not this is a distributed cache. Setting this to true makes various demands - * on your ehcache.xml configuration. */ - public boolean distributed; - - /** If true, newly cached records will be serialized and sent to all peer caches. */ - public boolean sendNewRecordsToPeers; - - /** If true, modification of a cached record will remove any cached versions on peers. */ - public boolean invalidateUpdatedRecordsOnPeers; - - /** If true, modification of a cached record will send the new version to all peer caches. */ - public boolean sendUpdatedRecordsToPeers; - - /** If true, removal of a cached record will remove any cached versions on peers. */ - public boolean invalidateRemovedRecordsOnPeers; - - /** How often to perform replication, in milliseconds. */ - public int replicationInterval; - - public EHCacheConfig (int elementsInMemory, boolean overflowToDisk, int timeToLiveSeconds, - int timeToIdleSeconds, boolean distributed, boolean sendNewRecordsToPeers, - boolean sendUpdatedRecordsToPeers, boolean invalidateUpdatedRecordsOnPeers, - boolean invalidateRemovedRecordsOnPeers, int replicationInterval) - { - this.elementsInMemory = elementsInMemory; - this.overflowToDisk = overflowToDisk; - this.timeToLiveSeconds = timeToLiveSeconds; - this.timeToIdleSeconds = timeToIdleSeconds; - this.distributed = distributed; - this.sendNewRecordsToPeers = sendNewRecordsToPeers; - this.sendUpdatedRecordsToPeers = sendUpdatedRecordsToPeers; - this.invalidateUpdatedRecordsOnPeers = invalidateUpdatedRecordsOnPeers; - this.invalidateRemovedRecordsOnPeers = invalidateRemovedRecordsOnPeers; - this.replicationInterval = replicationInterval; - } - - public EHCacheConfig (int elementsInMemory, boolean overflowToDisk, int timeToLiveSeconds, - int timeToIdleSeconds) - { - this(elementsInMemory, overflowToDisk, timeToLiveSeconds, timeToIdleSeconds, false, - false, false, false, false, -1); - } -} diff --git a/src/java/com/samskivert/depot/PersistenceContext.java b/src/java/com/samskivert/depot/PersistenceContext.java index 0544084..2564789 100644 --- a/src/java/com/samskivert/depot/PersistenceContext.java +++ b/src/java/com/samskivert/depot/PersistenceContext.java @@ -3,7 +3,7 @@ // // Depot library - a Java relational persistence library // Copyright (C) 2006-2008 Michael Bayne and Pär Winzell -// +// // This library is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published // by the Free Software Foundation; either version 2.1 of the License, or @@ -34,6 +34,7 @@ import com.google.common.collect.Sets; import com.samskivert.io.PersistenceException; import com.samskivert.util.StringUtil; +import com.samskivert.util.Tuple; import com.samskivert.jdbc.ConnectionProvider; import com.samskivert.jdbc.DatabaseLiaison; @@ -42,6 +43,8 @@ import com.samskivert.jdbc.LiaisonRegistry; import com.samskivert.jdbc.MySQLLiaison; import com.samskivert.jdbc.PostgreSQLLiaison; +import com.samskivert.depot.CacheAdapter.CacheCategory; +import com.samskivert.depot.CacheAdapter.CachedValue; import com.samskivert.depot.annotation.TableGenerator; import com.samskivert.depot.impl.DepotMarshaller; import com.samskivert.depot.impl.DepotTypes; @@ -317,15 +320,14 @@ public class PersistenceContext if (_cache == null) { return null; } - CacheAdapter.CacheBin bin = _cache.getCache(key.getCacheId()); - CacheAdapter.CachedValue ref = bin.lookup(key.getCacheKey()); + CacheAdapter.CachedValue ref = _cache.lookup(key.getCacheId(), key.getCacheKey()); return (ref == null) ? null : ref.getValue(); } /** * Stores a new entry indexed by the given key. */ - public void cacheStore (CacheKey key, T entry) + public void cacheStore (CacheCategory category, CacheKey key, T entry) { if (_cache == null) { return; @@ -337,12 +339,11 @@ public class PersistenceContext } log.debug("storing [key=" + key + ", value=" + entry + "]"); - CacheAdapter.CacheBin bin = _cache.getCache(key.getCacheId()); - CacheAdapter.CachedValue element = bin.lookup(key.getCacheKey()); + CacheAdapter.CachedValue element = _cache.lookup(key.getCacheId(), key.getCacheKey()); T oldEntry = (element != null ? element.getValue() : null); // update the cache - bin.store(key.getCacheKey(), entry); + _cache.store(category, key.getCacheId(), key.getCacheKey(), entry); // then do cache invalidations Set> listeners = _listenerSets.get(key.getCacheId()); @@ -392,8 +393,7 @@ public class PersistenceContext log.info("Invalidating", "id", cacheId, "key", cacheKey); } - CacheAdapter.CacheBin bin = _cache.getCache(cacheId); - CacheAdapter.CachedValue element = bin.lookup(cacheKey); + CacheAdapter.CachedValue element = _cache.lookup(cacheId, cacheKey); if (element != null) { // find the old entry, if any T oldEntry = element.getValue(); @@ -413,7 +413,7 @@ public class PersistenceContext } // then remove the keyed entry from the cache system - bin.remove(cacheKey); + _cache.remove(cacheId, cacheKey); } /** @@ -435,15 +435,10 @@ public class PersistenceContext if (_cache == null) { return; } - CacheAdapter.CacheBin bin = _cache.getCache(cacheId); - if (bin != null) { - for (Object key : bin.enumerateKeys()) { - CacheAdapter.CachedValue element = bin.lookup((Serializable) key); - T value; - if (element != null && (value = element.getValue()) != null) { - filter.visitCacheEntry(this, cacheId, (Serializable) key, value); - } - } + + Iterable>> entries = _cache.enumerate(cacheId); + for (Tuple> entry : entries) { + filter.visitCacheEntry(this, cacheId, entry.left, entry.right.getValue()); } } diff --git a/src/java/com/samskivert/depot/impl/FindAllQuery.java b/src/java/com/samskivert/depot/impl/FindAllQuery.java index 25d5980..3fc04a4 100644 --- a/src/java/com/samskivert/depot/impl/FindAllQuery.java +++ b/src/java/com/samskivert/depot/impl/FindAllQuery.java @@ -46,6 +46,7 @@ import com.samskivert.depot.PersistenceContext; import com.samskivert.depot.PersistentRecord; import com.samskivert.depot.SimpleCacheKey; import com.samskivert.depot.Stats; +import com.samskivert.depot.CacheAdapter.CacheCategory; import com.samskivert.depot.clause.FieldOverride; import com.samskivert.depot.clause.QueryClause; import com.samskivert.depot.clause.SelectClause; @@ -134,7 +135,8 @@ public abstract class FindAllQuery extends Query extends Query extends Query extends Query log.info("Loaded " + (key != null ? key : _marsh.getTableName())); } if (key != null) { - ctx.cacheStore(key, (result != null) ? result.clone() : null); + ctx.cacheStore(CacheCategory.RECORD, key, (result != null) ? result.clone() : null); if (PersistenceContext.CACHE_DEBUG) { log.info("Cached " + key); } diff --git a/src/java/com/samskivert/depot/impl/Modifier.java b/src/java/com/samskivert/depot/impl/Modifier.java index b7951c2..5f6aff8 100644 --- a/src/java/com/samskivert/depot/impl/Modifier.java +++ b/src/java/com/samskivert/depot/impl/Modifier.java @@ -29,6 +29,7 @@ import com.samskivert.depot.CacheKey; import com.samskivert.depot.PersistenceContext; import com.samskivert.depot.PersistentRecord; import com.samskivert.depot.Stats; +import com.samskivert.depot.CacheAdapter.CacheCategory; import com.samskivert.jdbc.DatabaseLiaison; /** @@ -97,7 +98,7 @@ public abstract class Modifier implements Operation Integer rows = super.invoke(ctx, conn, liaison); // if we have both a key and a record, cache if (_key != null && _result != null) { - ctx.cacheStore(_key, _result.clone()); + ctx.cacheStore(CacheCategory.RECORD, _key, _result.clone()); } return rows; } diff --git a/src/java/com/samskivert/depot/tests/TestCacheAdapter.java b/src/java/com/samskivert/depot/tests/TestCacheAdapter.java index 8cbab4d..492ef21 100644 --- a/src/java/com/samskivert/depot/tests/TestCacheAdapter.java +++ b/src/java/com/samskivert/depot/tests/TestCacheAdapter.java @@ -3,7 +3,7 @@ // // Depot library - a Java relational persistence library // Copyright (C) 2006-2008 Michael Bayne and Pär Winzell -// +// // This library is free software; you can redistribute it and/or modify it // under the terms of the GNU Lesser General Public License as published // by the Free Software Foundation; either version 2.1 of the License, or @@ -22,11 +22,14 @@ package com.samskivert.depot.tests; import java.io.Serializable; import java.util.Collections; +import java.util.List; import java.util.Map; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.samskivert.depot.CacheAdapter; +import com.samskivert.util.Tuple; /** * A simple cache adapter that stores all cached values in an in-memory map and never flushes. @@ -34,17 +37,6 @@ import com.samskivert.depot.CacheAdapter; */ public class TestCacheAdapter implements CacheAdapter { - // from interface CacheAdapter - public synchronized CacheAdapter.CacheBin getCache (String id) - { - @SuppressWarnings("unchecked") CacheBin bin = (CacheBin)_bins.get(id); - if (bin == null) { - bin = new TestCacheBin(); - _bins.put(id, bin); - } - return bin; - } - // from interface CacheAdapter public void shutdown () { @@ -62,27 +54,36 @@ public class TestCacheAdapter implements CacheAdapter protected final T _value; } - protected static class TestCacheBin implements CacheAdapter.CacheBin + public CacheAdapter.CachedValue lookup (String cacheId, Serializable key) { + // System.err.println("GET " + key + ": " + _cache.containsKey(key)); + @SuppressWarnings("unchecked") + CachedValue value = (CachedValue) _cache.get( + new Tuple(cacheId, key)); + return value; + } + public void store (CacheCategory category, String cacheId, Serializable key, T value) { + // System.err.println("STORE " + key); + _cache.put(new Tuple(cacheId, key), new TestCachedValue(value)); + } + public void remove (String cacheId, Serializable key) { + // System.err.println("REMOVE " + key); + _cache.remove(new Tuple(cacheId, key)); + } + public Iterable>> enumerate (String cacheId) { - public CacheAdapter.CachedValue lookup (Serializable key) { - // System.err.println("GET " + key + ": " + _cache.containsKey(key)); - return _cache.get(key); + // in a real implementation this would be a lazily constructed iterable + List>> result = Lists.newArrayList(); + for (Map.Entry, CachedValue> entry: _cache.entrySet()) { + if (entry.getKey().left.equals(cacheId)) { + @SuppressWarnings("unchecked") + CachedValue value = (CachedValue) entry.getValue(); + result.add(Tuple.newTuple(entry.getKey().right, value)); + } } - public void store (Serializable key, T value) { - // System.err.println("STORE " + key); - _cache.put(key, new TestCachedValue(value)); - } - public void remove (Serializable key) { - // System.err.println("REMOVE " + key); - _cache.remove(key); - } - public Iterable enumerateKeys () { - return _cache.keySet(); - } - protected Map> _cache = - Collections.synchronizedMap( - Maps.>newHashMap()); + return result; } - protected Map> _bins = Maps.newHashMap(); + protected Map, CachedValue> _cache = + Collections.synchronizedMap( + Maps., CachedValue>newHashMap()); }