Step one in making Depot's caching classloader friendly: make the keys use only

system classes (not 100% done, if you use a ByteEnum in a key right now it
doesn't do the right thing). Also moved SimpleCacheKey into impl because it's
an implementation detail, not meant to be used by external parties.
This commit is contained in:
Michael Bayne
2009-07-30 22:06:18 +00:00
parent c0b031a36d
commit e876ff0832
8 changed files with 129 additions and 72 deletions
+8 -19
View File
@@ -28,23 +28,24 @@ import java.util.Map;
import com.google.common.base.Function; import com.google.common.base.Function;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.samskivert.util.StringUtil;
import com.samskivert.depot.clause.WhereClause; import com.samskivert.depot.clause.WhereClause;
import com.samskivert.depot.expression.ColumnExp; import com.samskivert.depot.expression.ColumnExp;
import com.samskivert.depot.expression.SQLExpression; import com.samskivert.depot.expression.SQLExpression;
import com.samskivert.depot.impl.DepotMarshaller; import com.samskivert.depot.impl.DepotMarshaller;
import com.samskivert.depot.impl.DepotUtil; import com.samskivert.depot.impl.DepotUtil;
import com.samskivert.depot.impl.ExpressionVisitor; import com.samskivert.depot.impl.ExpressionVisitor;
import com.samskivert.util.StringUtil; import com.samskivert.depot.impl.KeyCacheKey;
/** /**
* A special form of {@link WhereClause} that uniquely specifies a single database row and thus * A special form of {@link WhereClause} that uniquely specifies a single database row and thus
* also a single persistent object. Because it implements both {@link CacheKey} and {@link * also a single persistent object. It knows how to invalidate itself upon modification. This class
* CacheInvalidator} it also uniquely indexes into the cache and knows how to invalidate itself * is created by many {@link DepotMarshaller} methods as a convenience, and may also be
* upon modification. This class is created by many {@link DepotMarshaller} methods as a * instantiated explicitly.
* convenience, and may also be instantiated explicitly.
*/ */
public class Key<T extends PersistentRecord> extends WhereClause public class Key<T extends PersistentRecord> extends WhereClause
implements SQLExpression, CacheKey, ValidatingCacheInvalidator, Serializable implements SQLExpression, ValidatingCacheInvalidator
{ {
/** Handles the matching of the key columns to its bound values. This is needed so that we can /** Handles the matching of the key columns to its bound values. This is needed so that we can
* combine a bunch of keys into a {@link KeySet}. */ * combine a bunch of keys into a {@link KeySet}. */
@@ -199,18 +200,6 @@ public class Key<T extends PersistentRecord> extends WhereClause
return builder.visit(this); return builder.visit(this);
} }
// from CacheKey
public String getCacheId ()
{
return _pClass.getName();
}
// from CacheKey
public Serializable getCacheKey ()
{
return this;
}
// from ValidatingCacheInvalidator // from ValidatingCacheInvalidator
public void validateFlushType (Class<?> pClass) public void validateFlushType (Class<?> pClass)
{ {
@@ -225,7 +214,7 @@ public class Key<T extends PersistentRecord> extends WhereClause
// from CacheInvalidator // from CacheInvalidator
public void invalidate (PersistenceContext ctx) public void invalidate (PersistenceContext ctx)
{ {
ctx.cacheInvalidate(this); ctx.cacheInvalidate(new KeyCacheKey(this));
} }
/** /**
+4 -3
View File
@@ -34,13 +34,14 @@ import com.samskivert.util.Logger;
import com.samskivert.util.StringUtil; import com.samskivert.util.StringUtil;
import com.samskivert.depot.clause.WhereClause; import com.samskivert.depot.clause.WhereClause;
import com.samskivert.depot.operator.In;
import com.samskivert.depot.operator.Or;
import com.samskivert.depot.expression.ColumnExp; import com.samskivert.depot.expression.ColumnExp;
import com.samskivert.depot.expression.LiteralExp; import com.samskivert.depot.expression.LiteralExp;
import com.samskivert.depot.expression.SQLExpression; import com.samskivert.depot.expression.SQLExpression;
import com.samskivert.depot.impl.DepotUtil; import com.samskivert.depot.impl.DepotUtil;
import com.samskivert.depot.impl.ExpressionVisitor; import com.samskivert.depot.impl.ExpressionVisitor;
import com.samskivert.depot.impl.KeyCacheKey;
import com.samskivert.depot.operator.In;
import com.samskivert.depot.operator.Or;
/** /**
* Contains a set of primary keys that match a set of persistent records. This is used internally * Contains a set of primary keys that match a set of persistent records. This is used internally
@@ -299,7 +300,7 @@ public abstract class KeySet<T extends PersistentRecord> extends WhereClause
// from ValidatingCacheInvalidator // from ValidatingCacheInvalidator
public void invalidate (PersistenceContext ctx) { public void invalidate (PersistenceContext ctx) {
for (Key<T> key : this) { for (Key<T> key : this) {
ctx.cacheInvalidate(key); ctx.cacheInvalidate(new KeyCacheKey(key));
} }
} }
@@ -74,22 +74,21 @@ public class PersistenceContext
public static interface CacheListener<T> public static interface CacheListener<T>
{ {
/** /**
* The given entry (which is never null) has just been evicted from the cache slot * The given entry (which is never null) has just been evicted from the cache.
* indicated by the given key.
* *
* This method is most commonly used to trigger custom cache invalidation of records that * This method is most commonly used to trigger custom cache invalidation of records that
* depend on the one that was just invalidated. * depend on the one that was just invalidated.
*/ */
public void entryInvalidated (CacheKey key, T oldEntry); public void entryInvalidated (T oldEntry);
/** /**
* The given entry, which may be an explicit null, has just been placed into the cache * The given entry, which may be an explicit null, has just been placed into the cache. The
* under the given key. The previous cache entry, if any, is also supplied. * previous cache entry, if any, is also supplied.
* *
* This method is most likely used by repositories to index entries by attribute for quick * This method is most likely used by repositories to index entries by attribute for quick
* cache invalidation when brute force is unrealistically time consuming. * cache invalidation when brute force is unrealistically time consuming.
*/ */
public void entryCached (CacheKey key, T newEntry, T oldEntry); public void entryCached (T newEntry, T oldEntry);
} }
/** /**
@@ -345,7 +344,7 @@ public class PersistenceContext
log.debug("cascading [listener=" + listener + "]"); log.debug("cascading [listener=" + listener + "]");
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
CacheListener<T> casted = (CacheListener<T>)listener; CacheListener<T> casted = (CacheListener<T>)listener;
casted.entryCached(key, entry, oldEntry); casted.entryCached(entry, oldEntry);
} }
} }
} }
@@ -363,15 +362,6 @@ public class PersistenceContext
} }
} }
/**
* Evicts the cache entry indexed under the given class and cache key, if there is one. The
* eviction may trigger further cache invalidations.
*/
public void cacheInvalidate (Class<? extends PersistentRecord> pClass, Serializable cacheKey)
{
cacheInvalidate(pClass.getName(), cacheKey);
}
/** /**
* Evicts the cache entry indexed under the given cache id and cache key, if there is one. The * Evicts the cache entry indexed under the given cache id and cache key, if there is one. The
* eviction may trigger further cache invalidations. * eviction may trigger further cache invalidations.
@@ -393,12 +383,11 @@ public class PersistenceContext
// if there was one, do (possibly cascading) cache invalidations // if there was one, do (possibly cascading) cache invalidations
Set<CacheListener<?>> listeners = _listenerSets.get(cacheId); Set<CacheListener<?>> listeners = _listenerSets.get(cacheId);
if (listeners != null && listeners.size() > 0) { if (listeners != null && listeners.size() > 0) {
CacheKey key = new SimpleCacheKey(cacheId, cacheKey);
for (CacheListener<?> listener : listeners) { for (CacheListener<?> listener : listeners) {
log.debug("cascading [listener=" + listener + "]"); log.debug("cascading [listener=" + listener + "]");
@SuppressWarnings("unchecked") CacheListener<T> casted = @SuppressWarnings("unchecked") CacheListener<T> casted =
(CacheListener<T>)listener; (CacheListener<T>)listener;
casted.entryInvalidated(key, oldEntry); casted.entryInvalidated(oldEntry);
} }
} }
} }
@@ -46,7 +46,6 @@ import com.samskivert.depot.Key;
import com.samskivert.depot.KeySet; import com.samskivert.depot.KeySet;
import com.samskivert.depot.PersistenceContext; import com.samskivert.depot.PersistenceContext;
import com.samskivert.depot.PersistentRecord; import com.samskivert.depot.PersistentRecord;
import com.samskivert.depot.SimpleCacheKey;
import com.samskivert.depot.Stats; import com.samskivert.depot.Stats;
import com.samskivert.depot.XArrayList; import com.samskivert.depot.XArrayList;
import com.samskivert.depot.XList; import com.samskivert.depot.XList;
@@ -283,7 +282,7 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<XLi
{ {
Set<Key<T>> fetchKeys = Sets.newHashSet(); Set<Key<T>> fetchKeys = Sets.newHashSet();
for (Key<T> key : allKeys) { for (Key<T> key : allKeys) {
T value = ctx.<T>cacheLookup(key); T value = ctx.<T>cacheLookup(new KeyCacheKey(key));
if (value != null) { if (value != null) {
@SuppressWarnings("unchecked") T newValue = (T) value.clone(); @SuppressWarnings("unchecked") T newValue = (T) value.clone();
entities.put(key, newValue); entities.put(key, newValue);
@@ -360,7 +359,7 @@ public abstract class FindAllQuery<T extends PersistentRecord> extends Query<XLi
if (entities.put(key, obj) != null) { if (entities.put(key, obj) != null) {
dups++; dups++;
} }
ctx.cacheStore(CacheCategory.RECORD, key, obj.clone()); // cache our result ctx.cacheStore(CacheCategory.RECORD, new KeyCacheKey(key), obj.clone());
got.add(key); got.add(key);
cnt++; cnt++;
} }
@@ -98,7 +98,7 @@ public class FindOneQuery<T extends PersistentRecord> extends Query<T>
if (key == null) { if (key == null) {
// no row-specific cache key was given, if we can, create a key from the record // no row-specific cache key was given, if we can, create a key from the record
if (result != null && _marsh.hasPrimaryKey()) { if (result != null && _marsh.hasPrimaryKey()) {
key = _marsh.getPrimaryKey(result); key = new KeyCacheKey(_marsh.getPrimaryKey(result));
} }
} }
if (PersistenceContext.CACHE_DEBUG) { if (PersistenceContext.CACHE_DEBUG) {
@@ -0,0 +1,94 @@
//
// $Id$
//
// Depot library - a Java relational persistence library
// Copyright (C) 2006-2009 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.impl;
import java.io.Serializable;
import java.util.Arrays;
import com.samskivert.depot.CacheKey;
import com.samskivert.depot.Key;
/**
* Converts a {@link CacheKey} to and from a {@link Key} in a way that eliminates references to
* non-Java classes (so that we don't have to deal with RMI classloader hell when replicating cache
* contents).
*/
public class KeyCacheKey
implements CacheKey, Serializable
{
public KeyCacheKey (Key<?> key)
{
_cacheId = key.getPersistentClass().getName();
Comparable<?>[] values = key.getValues();
_values = new Comparable<?>[values.length];
for (int ii = 0; ii < _values.length; ii++) {
_values[ii] = values[ii]; // TODO: check for non-system-class and serialize
}
}
// from CacheKey
public String getCacheId ()
{
return _cacheId;
}
// from CacheKey
public Serializable getCacheKey ()
{
return this;
}
@Override // from Object
public boolean equals (Object obj)
{
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
return Arrays.equals(_values, ((KeyCacheKey) obj)._values);
}
@Override // from Object
public int hashCode ()
{
return Arrays.hashCode(_values);
}
@Override // from Object
public String toString ()
{
StringBuilder builder = new StringBuilder(_cacheId);
builder.append("(");
for (int ii = 0; ii < _values.length; ii++) {
if (ii > 0) {
builder.append(", ");
}
builder.append(_values[ii]);
}
builder.append(")");
return builder.toString();
}
protected String _cacheId;
protected Comparable<?>[] _values;
}
@@ -24,12 +24,13 @@ import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement; import java.sql.Statement;
import com.samskivert.depot.CacheAdapter;
import com.samskivert.depot.CacheInvalidator; import com.samskivert.depot.CacheInvalidator;
import com.samskivert.depot.CacheKey; import com.samskivert.depot.CacheKey;
import com.samskivert.depot.Key;
import com.samskivert.depot.PersistenceContext; import com.samskivert.depot.PersistenceContext;
import com.samskivert.depot.PersistentRecord; import com.samskivert.depot.PersistentRecord;
import com.samskivert.depot.Stats; import com.samskivert.depot.Stats;
import com.samskivert.depot.CacheAdapter.CacheCategory;
import com.samskivert.jdbc.DatabaseLiaison; import com.samskivert.jdbc.DatabaseLiaison;
/** /**
@@ -73,21 +74,21 @@ public abstract class Modifier implements Operation<Integer>
* Construct a new CachingModifier with the given result, cache key, and invalidator, * Construct a new CachingModifier with the given result, cache key, and invalidator,
* all of which are optional, and may also be set during execution. * all of which are optional, and may also be set during execution.
*/ */
protected CachingModifier (T result, CacheKey key, CacheInvalidator invalidator) protected CachingModifier (T result, Key<T> key, CacheInvalidator invalidator)
{ {
super(invalidator); super(invalidator);
_result = result; _result = result;
_key = key; _key = (key == null) ? null : new KeyCacheKey(key);
} }
/** /**
* Update this {@link CachingModifier}'s cache key, e.g. during insertion when a * Update this {@link CachingModifier}'s cache key, e.g. during insertion when a persistent
* persistent object first receives a generated key. * object first receives a generated key.
*/ */
protected void updateKey (CacheKey key) protected void updateKey (Key<T> key)
{ {
if (key != null) { if (key != null) {
_key = key; _key = new KeyCacheKey(key);
} }
} }
@@ -98,7 +99,7 @@ public abstract class Modifier implements Operation<Integer>
Integer rows = super.invoke(ctx, conn, liaison); Integer rows = super.invoke(ctx, conn, liaison);
// if we have both a key and a record, cache // if we have both a key and a record, cache
if (_key != null && _result != null) { if (_key != null && _result != null) {
ctx.cacheStore(CacheCategory.RECORD, _key, _result.clone()); ctx.cacheStore(CacheAdapter.CacheCategory.RECORD, _key, _result.clone());
} }
return rows; return rows;
} }
@@ -18,10 +18,11 @@
// License along with this library; if not, write to the Free Software // License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.depot; package com.samskivert.depot.impl;
import java.io.Serializable; import java.io.Serializable;
import com.samskivert.depot.CacheKey;
import com.samskivert.util.ObjectUtil; import com.samskivert.util.ObjectUtil;
/** /**
@@ -32,27 +33,10 @@ import com.samskivert.util.ObjectUtil;
public class SimpleCacheKey public class SimpleCacheKey
implements CacheKey implements CacheKey
{ {
/**
* Construct a {@link SimpleCacheKey} for a query that has no parameters whatsoever.
*/
public SimpleCacheKey (String cacheId)
{
this(cacheId, Boolean.TRUE);
}
/**
* Construct a {@link SimpleCacheKey} associated with the given persistent class with
* the given cache key.
*/
public SimpleCacheKey (Class<?> cacheClass, Serializable cacheKey)
{
this(cacheClass.getName(), cacheKey);
}
/** /**
* Construct a {@link SimpleCacheKey} for the given cache id with the given cache key. * Construct a {@link SimpleCacheKey} for the given cache id with the given cache key.
*/ */
public SimpleCacheKey (String cacheId, Serializable cacheKey) public SimpleCacheKey (String cacheId, String cacheKey)
{ {
_cacheId = cacheId; _cacheId = cacheId;
_cacheKey = cacheKey; _cacheKey = cacheKey;
@@ -98,5 +82,5 @@ public class SimpleCacheKey
} }
protected String _cacheId; protected String _cacheId;
protected Serializable _cacheKey; protected String _cacheKey;
} }