From 3628ec528d064798adaa3048157fd52d2449126d Mon Sep 17 00:00:00 2001 From: Michael Bayne Date: Thu, 26 Jul 2007 19:07:20 +0000 Subject: [PATCH] Zell patch to factor caching code into pluggable CacheAdapter class. Allows us to disable caching and/or replace it with less industrial strength system during development. --- .../samskivert/jdbc/depot/CacheAdapter.java | 77 ++++++++++++ .../samskivert/jdbc/depot/EHCacheAdapter.java | 104 +++++++++++++++ .../samskivert/jdbc/depot/FindAllQuery.java | 10 +- src/java/com/samskivert/jdbc/depot/Log.java | 24 ---- .../jdbc/depot/PersistenceContext.java | 119 ++++++++++-------- 5 files changed, 249 insertions(+), 85 deletions(-) create mode 100644 src/java/com/samskivert/jdbc/depot/CacheAdapter.java create mode 100644 src/java/com/samskivert/jdbc/depot/EHCacheAdapter.java diff --git a/src/java/com/samskivert/jdbc/depot/CacheAdapter.java b/src/java/com/samskivert/jdbc/depot/CacheAdapter.java new file mode 100644 index 0000000..3ab935f --- /dev/null +++ b/src/java/com/samskivert/jdbc/depot/CacheAdapter.java @@ -0,0 +1,77 @@ +// +// $Id$ +// +// samskivert library - useful routines for java programs +// Copyright (C) 2006-2007 Michael Bayne, 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.jdbc.depot; + +import java.io.Serializable; + +/** + * 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. + */ +public interface CacheAdapter +{ + /** The encapsulated result of a cache lookup. */ + public interface CachedValue + { + /** Returns the cached value, which can be null. */ + public T getValue (); + } + + /** + * A reference to a specific bin within the cache; this is the type where most of the actual + * caching functionality occurs. + */ + 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 (); + } + + /** + * Fetch the {@link CacheBin} associated with the given ID, creating one on the fly if needed. + */ + public CacheBin getCache (String id); + + /** + * Shut down all operations, e.g. persisting memory contents to disk. + */ + public void shutdown (); +} diff --git a/src/java/com/samskivert/jdbc/depot/EHCacheAdapter.java b/src/java/com/samskivert/jdbc/depot/EHCacheAdapter.java new file mode 100644 index 0000000..7b1680d --- /dev/null +++ b/src/java/com/samskivert/jdbc/depot/EHCacheAdapter.java @@ -0,0 +1,104 @@ +// +// $Id$ +// +// samskivert library - useful routines for java programs +// Copyright (C) 2006-2007 Michael Bayne, 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.jdbc.depot; + +import java.io.Serializable; + +import net.sf.ehcache.Cache; +import net.sf.ehcache.CacheManager; +import net.sf.ehcache.Element; + +/** + * An implementation of {@link CacheAdapter} for ehcache. + */ +public class EHCacheAdapter + implements CacheAdapter +{ + public EHCacheAdapter () + { + _cachemgr = CacheManager.getInstance(); + } + + public CacheBin getCache (String id) + { + return new EHCacheBin(id); + } + + /** + * 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; + } + + @SuppressWarnings("unchecked") final T value = (T) hit.getValue(); + return new CachedValue() { + public T getValue () { + return value; + } + }; + } + + // from CacheBin + public void store (Serializable key, T value) + { + _cache.put(new Element(key, value)); + } + + // 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) + { + _cache = _cachemgr.getCache(id); + if (_cache == null) { + _cache = new Cache(id, 5000, false, false, 600, 60); + _cachemgr.addCache(_cache); + } + } + + protected Cache _cache; + } + + // from CacheAdapter + public void shutdown () + { + CacheManager.getInstance().shutdown(); + } + + protected CacheManager _cachemgr; +} diff --git a/src/java/com/samskivert/jdbc/depot/FindAllQuery.java b/src/java/com/samskivert/jdbc/depot/FindAllQuery.java index 4475c34..dd6deaf 100644 --- a/src/java/com/samskivert/jdbc/depot/FindAllQuery.java +++ b/src/java/com/samskivert/jdbc/depot/FindAllQuery.java @@ -3,7 +3,7 @@ // // samskivert library - useful routines for java programs // Copyright (C) 2006-2007 Michael Bayne, 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 @@ -29,9 +29,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import net.sf.ehcache.Cache; -import net.sf.ehcache.Element; - import com.samskivert.io.PersistenceException; import com.samskivert.util.ArrayUtil; @@ -83,10 +80,9 @@ public abstract class FindAllQuery allKeys.add(key); // TODO: All this cache fiddling needs to move to PersistenceContext? - Cache cache = _ctx.getCache(key.getCacheId()); - Element hit = cache.get(key.getCacheKey()); + CacheAdapter.CachedValue hit = _ctx.cacheLookup(key); if (hit != null) { - @SuppressWarnings("unchecked") T value = (T) hit.getValue(); + T value = hit.getValue(); if (value != null) { @SuppressWarnings("unchecked") T newValue = (T) value.clone(); entities.put(key, newValue); diff --git a/src/java/com/samskivert/jdbc/depot/Log.java b/src/java/com/samskivert/jdbc/depot/Log.java index 8642215..29315fe 100644 --- a/src/java/com/samskivert/jdbc/depot/Log.java +++ b/src/java/com/samskivert/jdbc/depot/Log.java @@ -30,28 +30,4 @@ public class Log { /** We dispatch our log messages through this logger. */ public static Logger log = Logger.getLogger("com.samskivert.jdbc.depot"); - - /** Convenience function. */ - public static void debug (String message) - { - log.fine(message); - } - - /** Convenience function. */ - public static void info (String message) - { - log.info(message); - } - - /** Convenience function. */ - public static void warning (String message) - { - log.warning(message); - } - - /** Convenience function. */ - public static void logStackTrace (Throwable t) - { - log.log(Level.WARNING, t.getMessage(), t); - } } diff --git a/src/java/com/samskivert/jdbc/depot/PersistenceContext.java b/src/java/com/samskivert/jdbc/depot/PersistenceContext.java index db72591..5df0aab 100644 --- a/src/java/com/samskivert/jdbc/depot/PersistenceContext.java +++ b/src/java/com/samskivert/jdbc/depot/PersistenceContext.java @@ -21,25 +21,17 @@ package com.samskivert.jdbc.depot; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; -import com.samskivert.jdbc.depot.annotation.TableGenerator; - import java.io.Serializable; import java.sql.Connection; import java.sql.SQLException; -import net.sf.ehcache.Cache; -import net.sf.ehcache.CacheManager; -import net.sf.ehcache.Element; - import com.samskivert.io.PersistenceException; - import com.samskivert.util.StringUtil; import com.samskivert.jdbc.ConnectionProvider; @@ -49,6 +41,10 @@ import com.samskivert.jdbc.LiaisonRegistry; import com.samskivert.jdbc.MySQLLiaison; import com.samskivert.jdbc.PostgreSQLLiaison; +import com.samskivert.jdbc.depot.annotation.TableGenerator; + +import static com.samskivert.jdbc.depot.Log.log; + /** * Defines a scope in which global annotations are shared. */ @@ -126,11 +122,23 @@ public class PersistenceContext * connection. */ public PersistenceContext (String ident, ConnectionProvider conprov) + { + this(ident, conprov, null); + } + + /** + * Creates a persistence context that will use the supplied provider to obtain JDBC + * connections. + * + * @param ident the identifier to provide to the connection provider when requesting a + * connection. + */ + public PersistenceContext (String ident, ConnectionProvider conprov, CacheAdapter adapter) { _ident = ident; _conprov = conprov; - _cachemgr = CacheManager.getInstance(); _liaison = LiaisonRegistry.getLiaison(conprov.getURL(ident)); + _cache = adapter; } /** @@ -204,21 +212,18 @@ public class PersistenceContext { CacheKey key = query.getCacheKey(); // if there is a cache key, check the cache - if (key != null) { - Cache cache = getCache(key.getCacheId()); - if (cache != null) { - Element cacheHit = cache.get(key.getCacheKey()); - if (cacheHit != null) { - Log.debug("invoke: cache hit [hit=" + cacheHit + "]"); - @SuppressWarnings("unchecked") T value = (T) cacheHit.getValue(); - value = query.transformCacheHit(key, value); - if (value != null) { - return value; - } - Log.debug("invoke: transformCacheHit returned null; rejecting cached value."); + if (key != null && _cache != null) { + CacheAdapter.CachedValue cacheHit = cacheLookup(key); + if (cacheHit != null) { + log.fine("invoke: cache hit [hit=" + cacheHit + "]"); + T value = cacheHit.getValue(); + value = query.transformCacheHit(key, value); + if (value != null) { + return value; } + log.fine("invoke: transformCacheHit returned null; rejecting cached value."); } - Log.debug("invoke: cache miss [key=" + key + "]"); + log.fine("invoke: cache miss [key=" + key + "]"); } // otherwise, perform the query @SuppressWarnings("unchecked") T result = (T) invoke(query, null, true); @@ -242,16 +247,15 @@ public class PersistenceContext } /** - * Returns the {@link Cache} for the given cache id, or creates one if necessary. + * Looks up an entry in the cache by the given key. */ - public Cache getCache (String cacheId) + public CacheAdapter.CachedValue cacheLookup (CacheKey key) { - Cache cache = _cachemgr.getCache(cacheId); - if (cache == null) { - cache = new Cache(cacheId, 5000, false, false, 600, 60); - _cachemgr.addCache(cache); + if (_cache == null) { + return null; } - return cache; + CacheAdapter.CacheBin bin = _cache.getCache(key.getCacheId()); + return bin.lookup(key.getCacheKey()); } /** @@ -259,27 +263,29 @@ public class PersistenceContext */ public void cacheStore (CacheKey key, T entry) { + if (_cache == null) { + return; + } if (key == null) { - Log.warning("Cache key must not be null [entry=" + entry + "]"); + log.warning("Cache key must not be null [entry=" + entry + "]"); Thread.dumpStack(); return; } - Log.debug("cacheStore: entry [key=" + key + ", value=" + entry + "]"); + log.fine("cacheStore: entry [key=" + key + ", value=" + entry + "]"); - // find the old entry, if any - Cache cache = getCache(key.getCacheId()); - Element element = cache.get(key.getCacheKey()); + CacheAdapter.CacheBin bin = _cache.getCache(key.getCacheId()); + CacheAdapter.CachedValue element = bin.lookup(key.getCacheKey()); @SuppressWarnings("unchecked") T oldEntry = (element != null ? (T) element.getValue() : null); // update the cache - cache.put(new Element(key.getCacheKey(), entry)); + bin.store(key.getCacheKey(), entry); // then do cache invalidations Set> listeners = _listenerSets.get(key.getCacheId()); if (listeners != null && listeners.size() > 0) { for (CacheListener listener : listeners) { - Log.debug("cacheInvalidate: cascading [listener=" + listener + "]"); + log.fine("cacheInvalidate: cascading [listener=" + listener + "]"); @SuppressWarnings("unchecked") CacheListener casted = (CacheListener)listener; casted.entryCached(key, entry, oldEntry); @@ -294,7 +300,7 @@ public class PersistenceContext public void cacheInvalidate (CacheKey key) { if (key == null) { - Log.warning("Cache key to invalidate must not be null."); + log.warning("Cache key to invalidate must not be null."); Thread.dumpStack(); } else { cacheInvalidate(key.getCacheId(), key.getCacheKey()); @@ -316,23 +322,26 @@ public class PersistenceContext */ public void cacheInvalidate (String cacheId, Serializable cacheKey) { - Log.debug("cacheInvalidate: entry [cacheId=" + cacheId + ", cacheKey=" + cacheKey + "]"); + if (_cache == null) { + return; + } + log.fine("cacheInvalidate: entry [cacheId=" + cacheId + ", cacheKey=" + cacheKey + "]"); - Cache cache = getCache(cacheId); - Element element = cache.get(cacheKey); + CacheAdapter.CacheBin bin = _cache.getCache(cacheId); + CacheAdapter.CachedValue element = bin.lookup(cacheKey); if (element == null) { return; } // find the old entry, if any - @SuppressWarnings("unchecked") T oldEntry = (T) element.getValue(); + T oldEntry = element.getValue(); if (oldEntry != null) { // if there was one, do (possibly cascading) cache invalidations Set> listeners = _listenerSets.get(cacheId); if (listeners != null && listeners.size() > 0) { CacheKey key = new SimpleCacheKey(cacheId, cacheKey); for (CacheListener listener : listeners) { - Log.debug("cacheInvalidate: cascading [listener=" + listener + "]"); + log.fine("cacheInvalidate: cascading [listener=" + listener + "]"); @SuppressWarnings("unchecked") CacheListener casted = (CacheListener)listener; casted.entryInvalidated(key, oldEntry); @@ -341,8 +350,8 @@ public class PersistenceContext } // then evict the keyed entry, if needed - Log.debug("cacheInvalidate: evicting [cacheKey=" + cacheKey + "]"); - cache.remove(cacheKey); + log.fine("cacheInvalidate: evicting [cacheKey=" + cacheKey + "]"); + bin.remove(cacheKey); } /** @@ -360,14 +369,15 @@ public class PersistenceContext */ public void cacheTraverse (String cacheId, CacheTraverser filter) { - Cache cache = getCache(cacheId); - if (cache != null) { - for (Object key : cache.getKeys()) { - Serializable sKey = (Serializable) key; - Element element = cache.get(sKey); + 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); if (element != null) { - @SuppressWarnings("unchecked") T value = (T) element.getValue(); - filter.visitCacheEntry(this, cacheId, sKey, value); + filter.visitCacheEntry(this, cacheId, (Serializable) key, element.getValue()); } } } @@ -473,8 +483,7 @@ public class PersistenceContext // outer exception; if I want a fucking stack trace, I'll call // printStackTrace() thanksverymuch String msg = StringUtil.split(String.valueOf(sqe), "\n")[0]; - Log.info("Transient failure executing operation, " + - "retrying [error=" + msg + "]."); + log.info("Transient failure executing operation, retrying [error=" + msg + "]."); } else { String msg = isReadOnlyQuery ? "Query failure " + query @@ -492,9 +501,11 @@ public class PersistenceContext protected String _ident; protected ConnectionProvider _conprov; - protected CacheManager _cachemgr; protected DatabaseLiaison _liaison; + /** The object through which all our caching is relayed, or null, for no caching. */ + protected CacheAdapter _cache; + protected Map>> _listenerSets = new HashMap>>();