Cache invalidation patch from Zell.

git-svn-id: https://samskivert.googlecode.com/svn/trunk@2055 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
mdb
2007-02-19 19:30:48 +00:00
parent 028d9ebfe8
commit e37cc33d68
@@ -3,7 +3,7 @@
// //
// samskivert library - useful routines for java programs // samskivert library - useful routines for java programs
// Copyright (C) 2006 Michael Bayne, Pär Winzell // Copyright (C) 2006 Michael Bayne, Pär Winzell
// //
// This library is free software; you can redistribute it and/or modify it // 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 // 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 // by the Free Software Foundation; either version 2.1 of the License, or
@@ -52,16 +52,28 @@ public class PersistenceContext
public HashMap<String, TableGenerator> tableGenerators = new HashMap<String, TableGenerator>(); public HashMap<String, TableGenerator> tableGenerators = new HashMap<String, TableGenerator>();
/** /**
* A cache listener is notified when cache entries are invalited through creation, deletion, * A cache listener is notified when cache entries change. Its purpose is typically to do
* or modification. Its purpose is typically to do further invalidation of dependent entries * further invalidation of dependent entries in other caches.
* in other caches.
*/ */
public static interface CacheListener<T> public static interface CacheListener<T>
{ {
/** /**
* The given entry has just been deleted, modified or created. Do what thou wilt. * The given entry (which is never null) has just been evicted from the cache slot
* indicated by the given key.
*
* This method is most commonly used to trigger custom cache invalidation of records that
* depend on the one that was just invalidated.
*/ */
public void entryModified (CacheKey key, T entry); public void entryInvalidated (CacheKey key, T oldEntry);
/**
* The given entry, which may be an explicit null, has just been placed into the cache
* under the given key. The previous cache entry, if any, is also supplied.
*
* This method is most likely used by repositories to index entries by attribute for quick
* cache invalidation when brute force is unrealistically time consuming.
*/
public void entryCached (CacheKey key, T newEntry, T oldEntry);
} }
/** /**
@@ -99,7 +111,7 @@ public class PersistenceContext
*/ */
protected abstract boolean testForEviction (Serializable key, T record); protected abstract boolean testForEviction (Serializable key, T record);
} }
/** /**
* Creates a persistence context that will use the supplied provider to obtain JDBC * Creates a persistence context that will use the supplied provider to obtain JDBC
* connections. * connections.
@@ -129,7 +141,7 @@ public class PersistenceContext
* <li> Run all registered pre-migrations * <li> Run all registered pre-migrations
* <li> Perform all default migrations (column additions and removals) * <li> Perform all default migrations (column additions and removals)
* <li> Run all registered post-migrations </ul> * <li> Run all registered post-migrations </ul>
* *
* Thus you must either be prepared for the entity to be at <b>any</b> version prior to your * Thus you must either be prepared for the entity to be at <b>any</b> version prior to your
* migration target version because we may start up, find the schema at version 1 and the * migration target version because we may start up, find the schema at version 1 and the
* Entity class at version 8 and do all "standard" migrations in one fell swoop. So if a column * Entity class at version 8 and do all "standard" migrations in one fell swoop. So if a column
@@ -222,25 +234,33 @@ public class PersistenceContext
/** /**
* Stores a new entry indexed by the given key. * Stores a new entry indexed by the given key.
*/ */
public <T> void cacheStore (CacheKey key, T value) public <T> void cacheStore (CacheKey key, T entry)
{ {
Log.debug("cacheStore: entry [key=" + key + ", value=" + value + "]"); Log.debug("cacheStore: entry [key=" + key + ", value=" + entry + "]");
getCache(key.getCacheId()).put(new Element(key.getCacheKey(), value));
// first do cascading invalidations // find the old entry, if any
Cache cache = getCache(key.getCacheId());
Element element = cache.get(key.getCacheKey());
@SuppressWarnings("unchecked") T oldEntry =
(element != null ? (T) element.getValue() : null);
// update the cache
cache.put(new Element(key.getCacheKey(), entry));
// then do cache invalidations
Set<CacheListener<?>> listeners = _listenerSets.get(key.getCacheId()); Set<CacheListener<?>> listeners = _listenerSets.get(key.getCacheId());
if (listeners != null && listeners.size() > 0) { if (listeners != null && listeners.size() > 0) {
for (CacheListener<?> listener : listeners) { for (CacheListener<?> listener : listeners) {
Log.debug("cacheInvalidate: cascading [listener=" + listener + "]"); Log.debug("cacheInvalidate: cascading [listener=" + listener + "]");
@SuppressWarnings("unchecked") CacheListener<T> casted = (CacheListener<T>)listener; @SuppressWarnings("unchecked") CacheListener<T> casted = (CacheListener<T>)listener;
casted.entryModified(key, value); casted.entryCached(key, entry, oldEntry);
} }
} }
} }
/** /**
* Evicts the cache entry indexed under the given key, if there is one. * Evicts the cache entry indexed under the given key, if there is one. The eviction may
* The eviction may trigger further cache invalidations. * trigger further cache invalidations.
*/ */
public void cacheInvalidate (CacheKey key) public void cacheInvalidate (CacheKey key)
{ {
@@ -248,8 +268,8 @@ public class PersistenceContext
} }
/** /**
* Evicts the cache entry indexed under the given class and cache key, if there is one. * Evicts the cache entry indexed under the given class and cache key, if there is one. The
* The eviction may trigger further cache invalidations. * eviction may trigger further cache invalidations.
*/ */
public void cacheInvalidate (Class pClass, Serializable cacheKey) public void cacheInvalidate (Class pClass, Serializable cacheKey)
{ {
@@ -257,33 +277,38 @@ public class PersistenceContext
} }
/** /**
* Evicts the cache entry indexed under the given cache id and cache key, if there is one. * Evicts the cache entry indexed under the given cache id and cache key, if there is one. The
* The eviction may trigger further cache invalidations. * eviction may trigger further cache invalidations.
*/ */
public <T extends Serializable> void cacheInvalidate (String cacheId, Serializable cacheKey) public <T extends Serializable> void cacheInvalidate (String cacheId, Serializable cacheKey)
{ {
Log.debug("cacheInvalidate: entry [cacheId=" + cacheId + ", cacheKey=" + cacheKey + "]"); Log.debug("cacheInvalidate: entry [cacheId=" + cacheId + ", cacheKey=" + cacheKey + "]");
Cache cache = getCache(cacheId); Cache cache = getCache(cacheId);
Element element = cache.get(cacheKey); Element element = cache.get(cacheKey);
if (element == null) {
return;
}
// first do cascading invalidations // find the old entry, if any
Set<CacheListener<?>> listeners = _listenerSets.get(cacheId); @SuppressWarnings("unchecked") T oldEntry = (T) element.getValue();
if (listeners != null && listeners.size() > 0) { if (oldEntry != null) {
CacheKey key = new SimpleCacheKey(cacheId, cacheKey); // if there was one, do (possibly cascading) cache invalidations
for (CacheListener<?> listener : listeners) { Set<CacheListener<?>> listeners = _listenerSets.get(cacheId);
Log.debug("cacheInvalidate: cascading [listener=" + listener + "]"); if (listeners != null && listeners.size() > 0) {
@SuppressWarnings("unchecked") CacheListener<T> casted = (CacheListener<T>)listener; CacheKey key = new SimpleCacheKey(cacheId, cacheKey);
@SuppressWarnings("unchecked") T value = for (CacheListener<?> listener : listeners) {
(element != null ? (T) element.getValue() : null); Log.debug("cacheInvalidate: cascading [listener=" + listener + "]");
casted.entryModified(key, value); @SuppressWarnings("unchecked") CacheListener<T> casted =
(CacheListener<T>)listener;
casted.entryInvalidated(key, oldEntry);
}
} }
} }
// then evict the keyed entry, if needed // then evict the keyed entry, if needed
if (element != null) { Log.debug("cacheInvalidate: evicting [cacheKey=" + cacheKey + "]");
Log.debug("cacheInvalidate: evicting [cacheKey=" + cacheKey + "]"); cache.remove(cacheKey);
cache.remove(cacheKey);
}
} }
/** /**
@@ -429,7 +454,7 @@ public class PersistenceContext
protected CacheManager _cachemgr; protected CacheManager _cachemgr;
protected Map<String, Set<CacheListener<?>>> _listenerSets = protected Map<String, Set<CacheListener<?>>> _listenerSets =
new HashMap<String, Set<CacheListener<?>>>(); new HashMap<String, Set<CacheListener<?>>>();
protected Map<Class<?>, DepotMarshaller<?>> _marshallers = protected Map<Class<?>, DepotMarshaller<?>> _marshallers =
new HashMap<Class<?>, DepotMarshaller<?>>(); new HashMap<Class<?>, DepotMarshaller<?>>();