From fc52b277a3df66905569ecfd1d48fc8f988f2f8e Mon Sep 17 00:00:00 2001 From: Par Winzell Date: Fri, 23 Jan 2009 16:12:11 +0000 Subject: [PATCH] The CacheAdapter interface has changed: * No more CacheBins, operations happen directly on the adapter. * Each cache write must supply a CacheCategory value that identifies its type. Current values are RECORD, KEYSET and RESULT. We will probably need a MISC or USER or something along those lines, too. The EHCache adapter has been entirely rewritten: * Each CacheCategory maps to one EHCache, which means there are only a very few EHCaches, and we know what they are. We expect these to be declared in ehcache.xml rather than programmatically generated as before. * Elements inside each EHCache are indexed by (cacheId, elementKey) tuples. The main purpose of this refactor is to sort all Records into a single EHCache, which we can then make really, really large, and then let the LRU mechanism sort out what should be in memory and what shouldn't. The previous implementation would make no ram allocation distinction between busy records and ones rarely read. --- .../com/samskivert/depot/CacheAdapter.java | 65 ++-- .../com/samskivert/depot/EHCacheAdapter.java | 350 +++++++++++++----- .../com/samskivert/depot/EHCacheConfig.java | 79 ---- .../samskivert/depot/PersistenceContext.java | 33 +- .../samskivert/depot/impl/FindAllQuery.java | 8 +- .../samskivert/depot/impl/FindOneQuery.java | 3 +- .../com/samskivert/depot/impl/Modifier.java | 3 +- .../depot/tests/TestCacheAdapter.java | 63 ++-- 8 files changed, 344 insertions(+), 260 deletions(-) delete mode 100644 src/java/com/samskivert/depot/EHCacheConfig.java 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()); }