diff options
author | Brian S. O'Neill <bronee@gmail.com> | 2010-08-18 16:54:59 +0000 |
---|---|---|
committer | Brian S. O'Neill <bronee@gmail.com> | 2010-08-18 16:54:59 +0000 |
commit | c270234f37f0d9bdb1755f22561cbcba156b6e8c (patch) | |
tree | 1c1d2b14e7b9d383945c050425575802ab7aa782 /src/main/java | |
parent | 41baed7d94d0cef87d77bd9dbdac78940a71180a (diff) |
Introduce SoftValuedCache, which evicts more aggressively than SoftValuedHashMap.
Diffstat (limited to 'src/main/java')
20 files changed, 454 insertions, 71 deletions
diff --git a/src/main/java/com/amazon/carbonado/filter/Filter.java b/src/main/java/com/amazon/carbonado/filter/Filter.java index 6f3cc55..f20e30a 100644 --- a/src/main/java/com/amazon/carbonado/filter/Filter.java +++ b/src/main/java/com/amazon/carbonado/filter/Filter.java @@ -25,7 +25,6 @@ import java.util.Collections; import java.util.List;
import java.util.Map;
-import org.cojen.util.SoftValuedHashMap;
import org.cojen.util.WeakCanonicalSet;
import org.cojen.util.WeakIdentityMap;
@@ -36,6 +35,7 @@ import com.amazon.carbonado.info.ChainedProperty; import com.amazon.carbonado.info.StorableIntrospector;
import com.amazon.carbonado.util.Appender;
+import com.amazon.carbonado.util.SoftValuedCache;
/**
* An immutable tree structure representing a query result filter. Filters can
@@ -88,7 +88,7 @@ public abstract class Filter<S extends Storable> implements Serializable, Append * @throws MalformedFilterException if filter expression is malformed
*/
public static <S extends Storable> Filter<S> filterFor(Class<S> type, String expression) {
- Map<Object, Filter<S>> filterCache = getFilterCache(type);
+ SoftValuedCache<Object, Filter<S>> filterCache = getFilterCache(type);
synchronized (filterCache) {
Filter<S> filter = filterCache.get(expression);
if (filter == null) {
@@ -108,7 +108,7 @@ public abstract class Filter<S extends Storable> implements Serializable, Append * @see OpenFilter
*/
public static <S extends Storable> OpenFilter<S> getOpenFilter(Class<S> type) {
- Map<Object, Filter<S>> filterCache = getFilterCache(type);
+ SoftValuedCache<Object, Filter<S>> filterCache = getFilterCache(type);
synchronized (filterCache) {
Filter<S> filter = filterCache.get(OPEN_KEY);
if (filter == null) {
@@ -128,7 +128,7 @@ public abstract class Filter<S extends Storable> implements Serializable, Append * @see ClosedFilter
*/
public static <S extends Storable> ClosedFilter<S> getClosedFilter(Class<S> type) {
- Map<Object, Filter<S>> filterCache = getFilterCache(type);
+ SoftValuedCache<Object, Filter<S>> filterCache = getFilterCache(type);
synchronized (filterCache) {
Filter<S> filter = filterCache.get(CLOSED_KEY);
if (filter == null) {
@@ -140,11 +140,14 @@ public abstract class Filter<S extends Storable> implements Serializable, Append }
@SuppressWarnings("unchecked")
- private static <S extends Storable> Map<Object, Filter<S>> getFilterCache(Class<S> type) {
+ private static <S extends Storable> SoftValuedCache<Object, Filter<S>>
+ getFilterCache(Class<S> type)
+ {
synchronized (cCache) {
- Map<Object, Filter<S>> filterCache = (Map<Object, Filter<S>>) cCache.get(type);
+ SoftValuedCache<Object, Filter<S>> filterCache =
+ (SoftValuedCache<Object, Filter<S>>) cCache.get(type);
if (filterCache == null) {
- filterCache = new SoftValuedHashMap();
+ filterCache = SoftValuedCache.newCache(11);
cCache.put(type, filterCache);
}
return filterCache;
diff --git a/src/main/java/com/amazon/carbonado/gen/DelegateStorableGenerator.java b/src/main/java/com/amazon/carbonado/gen/DelegateStorableGenerator.java index 67e93f8..84b8c83 100644 --- a/src/main/java/com/amazon/carbonado/gen/DelegateStorableGenerator.java +++ b/src/main/java/com/amazon/carbonado/gen/DelegateStorableGenerator.java @@ -29,11 +29,12 @@ import org.cojen.classfile.TypeDesc; import org.cojen.util.ClassInjector;
import org.cojen.util.KeyFactory;
-import org.cojen.util.SoftValuedHashMap;
import com.amazon.carbonado.Storable;
import com.amazon.carbonado.SupportException;
+import com.amazon.carbonado.util.SoftValuedCache;
+
/**
* Generates and caches concrete implementations of {@link Storable} types
* which delegate to {@link DelegateSupport}. The delegating classes extend
@@ -43,10 +44,10 @@ import com.amazon.carbonado.SupportException; * @since 1.2
*/
public class DelegateStorableGenerator<S extends Storable> {
- private static final Map<Object, Class> cCache;
+ private static final SoftValuedCache<Object, Class> cCache;
static {
- cCache = new SoftValuedHashMap();
+ cCache = SoftValuedCache.newCache(11);
}
/**
diff --git a/src/main/java/com/amazon/carbonado/gen/MasterStorableGenerator.java b/src/main/java/com/amazon/carbonado/gen/MasterStorableGenerator.java index 1c63d0c..ef73a2c 100644 --- a/src/main/java/com/amazon/carbonado/gen/MasterStorableGenerator.java +++ b/src/main/java/com/amazon/carbonado/gen/MasterStorableGenerator.java @@ -38,7 +38,6 @@ import org.cojen.classfile.Opcode; import org.cojen.classfile.TypeDesc;
import org.cojen.util.ClassInjector;
import org.cojen.util.KeyFactory;
-import org.cojen.util.SoftValuedHashMap;
import com.amazon.carbonado.ConstraintException;
import com.amazon.carbonado.IsolationLevel;
@@ -55,6 +54,8 @@ import com.amazon.carbonado.info.StorableProperty; import com.amazon.carbonado.sequence.SequenceValueProducer;
+import com.amazon.carbonado.util.SoftValuedCache;
+
import static com.amazon.carbonado.gen.CommonMethodNames.*;
/**
@@ -88,7 +89,8 @@ public final class MasterStorableGenerator<S extends Storable> { private static final String DELETE_OP = "Delete";
// Cache of generated abstract classes.
- private static Map<Object, Class<? extends Storable>> cCache = new SoftValuedHashMap();
+ private static SoftValuedCache<Object, Class<? extends Storable>> cCache =
+ SoftValuedCache.newCache(11);
/**
* Returns an abstract implementation of the given Storable type, which
diff --git a/src/main/java/com/amazon/carbonado/gen/StorablePropertyMap.java b/src/main/java/com/amazon/carbonado/gen/StorablePropertyMap.java index 6ad3466..0b8086b 100644 --- a/src/main/java/com/amazon/carbonado/gen/StorablePropertyMap.java +++ b/src/main/java/com/amazon/carbonado/gen/StorablePropertyMap.java @@ -29,20 +29,20 @@ import java.util.LinkedHashSet; import java.util.Map;
import java.util.Set;
-import org.cojen.util.SoftValuedHashMap;
-
import com.amazon.carbonado.Storable;
import com.amazon.carbonado.info.StorableIntrospector;
import com.amazon.carbonado.info.StorableProperty;
+import com.amazon.carbonado.util.SoftValuedCache;
+
/**
* Basic implementation for {@link Storable#propertyMap} method.
*
* @author Brian S O'Neill
*/
public class StorablePropertyMap<S extends Storable> extends AbstractMap<String, Object> {
- private static final Map<Class, Set<String>> cPropertyNamesForType =
- new SoftValuedHashMap();
+ private static final SoftValuedCache<Class, Set<String>> cPropertyNamesForType =
+ SoftValuedCache.newCache(11);
public static <S extends Storable> StorablePropertyMap<S> createMap(Class<S> type, S storable)
{
diff --git a/src/main/java/com/amazon/carbonado/layout/Layout.java b/src/main/java/com/amazon/carbonado/layout/Layout.java index 98df22c..6c28740 100644 --- a/src/main/java/com/amazon/carbonado/layout/Layout.java +++ b/src/main/java/com/amazon/carbonado/layout/Layout.java @@ -35,8 +35,6 @@ import org.joda.time.DateTime; import org.apache.commons.logging.LogFactory;
-import org.cojen.util.SoftValuedHashMap;
-
import com.amazon.carbonado.CorruptEncodingException;
import com.amazon.carbonado.Cursor;
import com.amazon.carbonado.FetchException;
@@ -53,6 +51,7 @@ import com.amazon.carbonado.synthetic.SyntheticKey; import com.amazon.carbonado.synthetic.SyntheticProperty;
import com.amazon.carbonado.synthetic.SyntheticStorableBuilder;
import com.amazon.carbonado.util.AnnotationDescPrinter;
+import com.amazon.carbonado.util.SoftValuedCache;
import com.amazon.carbonado.capability.ResyncCapability;
@@ -63,10 +62,10 @@ import com.amazon.carbonado.capability.ResyncCapability; * @see LayoutFactory
*/
public class Layout {
- private static Map<Long, Class<? extends Storable>> cReconstructed;
+ private static SoftValuedCache<Long, Class<? extends Storable>> cReconstructed;
static {
- cReconstructed = Collections.synchronizedMap(new SoftValuedHashMap());
+ cReconstructed = SoftValuedCache.newCache(7);
}
static Class<? extends Storable> reconstruct(final Layout layout, ClassLoader loader)
diff --git a/src/main/java/com/amazon/carbonado/layout/LayoutFactory.java b/src/main/java/com/amazon/carbonado/layout/LayoutFactory.java index 7b7953b..836ee54 100644 --- a/src/main/java/com/amazon/carbonado/layout/LayoutFactory.java +++ b/src/main/java/com/amazon/carbonado/layout/LayoutFactory.java @@ -29,8 +29,6 @@ import java.lang.reflect.Method; import java.util.Arrays;
import java.util.Map;
-import org.cojen.util.SoftValuedHashMap;
-
import com.amazon.carbonado.Cursor;
import com.amazon.carbonado.FetchDeadlockException;
import com.amazon.carbonado.FetchException;
@@ -55,6 +53,8 @@ import com.amazon.carbonado.info.StorableProperty; import com.amazon.carbonado.info.StorablePropertyAdapter;
import com.amazon.carbonado.info.StorablePropertyAnnotation;
+import com.amazon.carbonado.util.SoftValuedCache;
+
/**
* Factory for obtaining references to storable layouts.
*
@@ -69,7 +69,7 @@ public class LayoutFactory implements LayoutCapability { final Storage<StoredLayout> mLayoutStorage;
final Storage<StoredLayoutProperty> mPropertyStorage;
- private Map<Class<? extends Storable>, Layout> mReconstructed;
+ private SoftValuedCache<Class<? extends Storable>, Layout> mReconstructed;
/**
* @throws com.amazon.carbonado.SupportException if underlying repository
@@ -339,7 +339,7 @@ public class LayoutFactory implements LayoutCapability { (Class<? extends Storable> reconstructed, Layout layout)
{
if (mReconstructed == null) {
- mReconstructed = new SoftValuedHashMap();
+ mReconstructed = SoftValuedCache.newCache(7);
}
mReconstructed.put(reconstructed, layout);
}
diff --git a/src/main/java/com/amazon/carbonado/qe/JoinedQueryExecutor.java b/src/main/java/com/amazon/carbonado/qe/JoinedQueryExecutor.java index b370416..9a77f26 100644 --- a/src/main/java/com/amazon/carbonado/qe/JoinedQueryExecutor.java +++ b/src/main/java/com/amazon/carbonado/qe/JoinedQueryExecutor.java @@ -32,7 +32,6 @@ import org.cojen.classfile.Modifiers; import org.cojen.classfile.TypeDesc;
import org.cojen.util.ClassInjector;
-import org.cojen.util.SoftValuedHashMap;
import com.amazon.carbonado.Cursor;
import com.amazon.carbonado.FetchException;
@@ -52,6 +51,7 @@ import com.amazon.carbonado.info.StorableIntrospector; import com.amazon.carbonado.info.StorableProperty;
import com.amazon.carbonado.util.QuickConstructorGenerator;
+import com.amazon.carbonado.util.SoftValuedCache;
import com.amazon.carbonado.gen.CodeBuilderUtil;
@@ -192,10 +192,10 @@ public class JoinedQueryExecutor<S extends Storable, T extends Storable> private static final String INNER_LOOP_FV_FIELD_NAME = "innerLoopFilterValues";
private static final String ACTIVE_SOURCE_FIELD_NAME = "active";
- private static final Map<StorableProperty, Class> cJoinerCursorClassCache;
+ private static final SoftValuedCache<StorableProperty, Class> cJoinerCursorClassCache;
static {
- cJoinerCursorClassCache = new SoftValuedHashMap();
+ cJoinerCursorClassCache = SoftValuedCache.newCache(11);
}
private static synchronized <S, T extends Storable> Joiner.Factory<S, T>
diff --git a/src/main/java/com/amazon/carbonado/qe/OrderingList.java b/src/main/java/com/amazon/carbonado/qe/OrderingList.java index 7feed40..95d8328 100644 --- a/src/main/java/com/amazon/carbonado/qe/OrderingList.java +++ b/src/main/java/com/amazon/carbonado/qe/OrderingList.java @@ -31,14 +31,14 @@ import java.util.List; import java.util.Map;
import java.util.Set;
-import org.cojen.util.SoftValuedHashMap;
-
import com.amazon.carbonado.Storable;
import com.amazon.carbonado.info.ChainedProperty;
import com.amazon.carbonado.info.OrderedProperty;
import com.amazon.carbonado.info.StorableIntrospector;
+import com.amazon.carbonado.util.SoftValuedCache;
+
/**
* Produces unmodifiable lists of {@link OrderedProperty orderings}. Instances
* are immutable, canonical and cached. Calls to "equals" and "hashCode" are
@@ -53,10 +53,10 @@ public class OrderingList<S extends Storable> extends AbstractList<OrderedProper private static final OrderingList EMPTY_LIST = new OrderingList();
- private static final Map<Class, OrderingList> cCache;
+ private static final SoftValuedCache<Class, OrderingList> cCache;
static {
- cCache = new SoftValuedHashMap();
+ cCache = SoftValuedCache.newCache(11);
}
/**
diff --git a/src/main/java/com/amazon/carbonado/qe/PropertyFilterList.java b/src/main/java/com/amazon/carbonado/qe/PropertyFilterList.java index 7d7966b..cedc05c 100644 --- a/src/main/java/com/amazon/carbonado/qe/PropertyFilterList.java +++ b/src/main/java/com/amazon/carbonado/qe/PropertyFilterList.java @@ -26,8 +26,6 @@ import java.util.HashMap; import java.util.List;
import java.util.Map;
-import org.cojen.util.SoftValuedHashMap;
-
import com.amazon.carbonado.Storable;
import com.amazon.carbonado.filter.ExistsFilter;
@@ -37,6 +35,8 @@ import com.amazon.carbonado.filter.PropertyFilter; import com.amazon.carbonado.filter.RelOp;
import com.amazon.carbonado.filter.Visitor;
+import com.amazon.carbonado.util.SoftValuedCache;
+
/**
* Produces unmodifable lists of PropertyFilters which were originally all
* 'and'ed together. The filters are ordered such that all '=' operators are
@@ -45,10 +45,10 @@ import com.amazon.carbonado.filter.Visitor; * @author Brian S O'Neill
*/
class PropertyFilterList<S extends Storable> extends AbstractList<PropertyFilter<S>> {
- private static Map<Filter<?>, PropertyFilterList> cCache;
+ private static SoftValuedCache<Filter<?>, PropertyFilterList> cCache;
static {
- cCache = new SoftValuedHashMap();
+ cCache = SoftValuedCache.newCache(11);
}
/**
diff --git a/src/main/java/com/amazon/carbonado/qe/QueryExecutorCache.java b/src/main/java/com/amazon/carbonado/qe/QueryExecutorCache.java index 806365e..e114943 100644 --- a/src/main/java/com/amazon/carbonado/qe/QueryExecutorCache.java +++ b/src/main/java/com/amazon/carbonado/qe/QueryExecutorCache.java @@ -20,7 +20,6 @@ package com.amazon.carbonado.qe; import java.util.Map;
-import org.cojen.util.SoftValuedHashMap;
import org.cojen.util.WeakIdentityMap;
import com.amazon.carbonado.RepositoryException;
@@ -28,6 +27,8 @@ import com.amazon.carbonado.Storable; import com.amazon.carbonado.filter.Filter;
+import com.amazon.carbonado.util.SoftValuedCache;
+
/**
* QueryExecutors should be cached since expensive analysis is often required
* to build them.
@@ -38,7 +39,7 @@ public class QueryExecutorCache<S extends Storable> implements QueryExecutorFact private final QueryExecutorFactory<S> mFactory;
// Maps filters to maps which map ordering lists (possibly with hints) to executors.
- private final Map<Filter<S>, Map<Object, QueryExecutor<S>>> mFilterToExecutor;
+ private final Map<Filter<S>, SoftValuedCache<Object, QueryExecutor<S>>> mFilterToExecutor;
public QueryExecutorCache(QueryExecutorFactory<S> factory) {
if (factory == null) {
@@ -62,12 +63,12 @@ public class QueryExecutorCache<S extends Storable> implements QueryExecutorFact public QueryExecutor<S> executor(Filter<S> filter, OrderingList<S> ordering, QueryHints hints)
throws RepositoryException
{
- Map<Object, QueryExecutor<S>> map;
+ SoftValuedCache<Object, QueryExecutor<S>> cache;
synchronized (mFilterToExecutor) {
- map = mFilterToExecutor.get(filter);
- if (map == null) {
- map = new SoftValuedHashMap(7);
- mFilterToExecutor.put(filter, map);
+ cache = mFilterToExecutor.get(filter);
+ if (cache == null) {
+ cache = SoftValuedCache.newCache(7);
+ mFilterToExecutor.put(filter, cache);
}
}
@@ -79,11 +80,11 @@ public class QueryExecutorCache<S extends Storable> implements QueryExecutorFact }
QueryExecutor<S> executor;
- synchronized (map) {
- executor = map.get(key);
+ synchronized (cache) {
+ executor = cache.get(key);
if (executor == null) {
executor = mFactory.executor(filter, ordering, hints);
- map.put(key, executor);
+ cache.put(key, executor);
}
}
diff --git a/src/main/java/com/amazon/carbonado/qe/StandardQueryFactory.java b/src/main/java/com/amazon/carbonado/qe/StandardQueryFactory.java index 4c80075..d6b99d6 100644 --- a/src/main/java/com/amazon/carbonado/qe/StandardQueryFactory.java +++ b/src/main/java/com/amazon/carbonado/qe/StandardQueryFactory.java @@ -31,6 +31,8 @@ import com.amazon.carbonado.Storable; import com.amazon.carbonado.filter.Filter;
import com.amazon.carbonado.filter.FilterValues;
+import com.amazon.carbonado.util.SoftValuedCache;
+
/**
* Builds and caches StandardQuery instances.
*
@@ -40,7 +42,7 @@ public abstract class StandardQueryFactory<S extends Storable> implements QueryF private final Class<S> mType;
private final boolean mLazySetExecutor;
- private final Map<String, Query<S>> mStringToQuery;
+ private final SoftValuedCache<String, Query<S>> mStringToQuery;
// Maps filters to maps which map ordering lists to queries.
private final Map<Filter<S>, Map<OrderingList<S>, Query<S>>> mFilterToQuery;
@@ -59,7 +61,7 @@ public abstract class StandardQueryFactory<S extends Storable> implements QueryF }
mType = type;
mLazySetExecutor = lazySetExecutor;
- mStringToQuery = new SoftValuedHashMap(7);
+ mStringToQuery = SoftValuedCache.newCache(7);
mFilterToQuery = new WeakIdentityMap(7);
}
diff --git a/src/main/java/com/amazon/carbonado/raw/GenericStorableCodec.java b/src/main/java/com/amazon/carbonado/raw/GenericStorableCodec.java index ed2d09d..22f36ea 100644 --- a/src/main/java/com/amazon/carbonado/raw/GenericStorableCodec.java +++ b/src/main/java/com/amazon/carbonado/raw/GenericStorableCodec.java @@ -32,7 +32,6 @@ import org.cojen.classfile.TypeDesc; import org.cojen.util.ClassInjector;
import org.cojen.util.IntHashMap;
import org.cojen.util.KeyFactory;
-import org.cojen.util.SoftValuedHashMap;
import org.cojen.util.ThrowUnchecked;
import com.amazon.carbonado.CorruptEncodingException;
@@ -55,6 +54,7 @@ import com.amazon.carbonado.gen.StorableGenerator; import com.amazon.carbonado.gen.TriggerSupport;
import com.amazon.carbonado.util.QuickConstructorGenerator;
+import com.amazon.carbonado.util.SoftValuedCache;
/**
* Generic codec that supports any kind of storable by auto-generating and
@@ -67,7 +67,7 @@ public class GenericStorableCodec<S extends Storable> implements StorableCodec<S private static final String BLANK_KEY_FIELD_NAME = "blankKey$";
// Maps GenericEncodingStrategy instances to Storable classes.
- private static final Map cCache = new SoftValuedHashMap();
+ private static final SoftValuedCache cCache = SoftValuedCache.newCache(11);
/**
* Returns an instance of the codec. The Storable type itself may be an
@@ -291,10 +291,10 @@ public class GenericStorableCodec<S extends Storable> implements StorableCodec<S }
// Maps codec key and OrderedProperty[] keys to SearchKeyFactory instances.
- private static final Map cCodecSearchKeyFactories = new SoftValuedHashMap();
+ private static final SoftValuedCache cCodecSearchKeyFactories = SoftValuedCache.newCache(11);
// Maps codec key and layout generations to Decoders.
- private static final Map cCodecDecoders = new SoftValuedHashMap();
+ private static final SoftValuedCache cCodecDecoders = SoftValuedCache.newCache(11);
private final Object mCodecKey;
private final GenericStorableCodecFactory mFactory;
diff --git a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableGenerator.java b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableGenerator.java index e929e6b..e40ce04 100644 --- a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableGenerator.java +++ b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableGenerator.java @@ -43,7 +43,6 @@ import org.cojen.classfile.Opcode; import org.cojen.classfile.TypeDesc;
import org.cojen.util.ClassInjector;
import org.cojen.util.KeyFactory;
-import org.cojen.util.SoftValuedHashMap;
import org.joda.time.ReadableInstant;
@@ -65,6 +64,8 @@ import com.amazon.carbonado.gen.StorableGenerator; import com.amazon.carbonado.gen.TriggerSupport;
import static com.amazon.carbonado.gen.CommonMethodNames.*;
+import com.amazon.carbonado.util.SoftValuedCache;
+
/**
* Generates concrete implementations of {@link Storable} types for
* {@link JDBCRepository}.
@@ -87,10 +88,10 @@ class JDBCStorableGenerator<S extends Storable> { private static final int INITIAL_VERSION = 2;
private static final int INCREMENT_VERSION = 3;
- private static final Map<Object, Class<? extends Storable>> cCache;
+ private static final SoftValuedCache<Object, Class<? extends Storable>> cCache;
static {
- cCache = new SoftValuedHashMap();
+ cCache = SoftValuedCache.newCache(11);
}
static <S extends Storable> Class<? extends S> getGeneratedClass(JDBCStorableInfo<S> info,
diff --git a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableIntrospector.java b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableIntrospector.java index 042974f..acba43e 100644 --- a/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableIntrospector.java +++ b/src/main/java/com/amazon/carbonado/repo/jdbc/JDBCStorableIntrospector.java @@ -46,7 +46,6 @@ import org.apache.commons.logging.LogFactory; import org.cojen.classfile.TypeDesc;
import org.cojen.util.KeyFactory;
-import org.cojen.util.SoftValuedHashMap;
import org.cojen.util.ThrowUnchecked;
import com.amazon.carbonado.capability.IndexInfo;
@@ -66,6 +65,8 @@ import com.amazon.carbonado.info.StorableProperty; import com.amazon.carbonado.info.StorablePropertyAdapter;
import com.amazon.carbonado.info.StorablePropertyConstraint;
+import com.amazon.carbonado.util.SoftValuedCache;
+
/**
* Provides additional metadata for a {@link Storable} type needed by
* JDBCRepository. The storable type must match to a table in an external
@@ -80,7 +81,8 @@ import com.amazon.carbonado.info.StorablePropertyConstraint; public class JDBCStorableIntrospector extends StorableIntrospector {
// Maps compound keys to softly referenced JDBCStorableInfo objects.
@SuppressWarnings("unchecked")
- private static Map<Object, JDBCStorableInfo<?>> cCache = new SoftValuedHashMap();
+ private static SoftValuedCache<Object, JDBCStorableInfo<?>> cCache =
+ SoftValuedCache.newCache(11);
/**
* Examines the given class and returns a JDBCStorableInfo describing it. A
diff --git a/src/main/java/com/amazon/carbonado/repo/map/Key.java b/src/main/java/com/amazon/carbonado/repo/map/Key.java index 9348516..86a0023 100644 --- a/src/main/java/com/amazon/carbonado/repo/map/Key.java +++ b/src/main/java/com/amazon/carbonado/repo/map/Key.java @@ -34,7 +34,6 @@ import org.cojen.classfile.Modifiers; import org.cojen.classfile.TypeDesc;
import org.cojen.util.ClassInjector;
-import org.cojen.util.SoftValuedHashMap;
import com.amazon.carbonado.Storable;
@@ -45,6 +44,8 @@ import com.amazon.carbonado.info.StorableProperty; import com.amazon.carbonado.gen.CodeBuilderUtil;
+import com.amazon.carbonado.util.SoftValuedCache;
+
/**
*
*
@@ -82,10 +83,10 @@ class Key<S extends Storable> implements Comparable<Key<S>> { void setKeyValues(S storable, Object[] identityValues, Object rangeValue);
}
- private static final Map<Class, Assigner> mAssigners;
+ private static final SoftValuedCache<Class, Assigner> mAssigners;
static {
- mAssigners = new SoftValuedHashMap();
+ mAssigners = SoftValuedCache.newCache(11);
}
public static synchronized <S extends Storable> Assigner<S> getAssigner(Class<S> clazz) {
diff --git a/src/main/java/com/amazon/carbonado/spi/LobEngine.java b/src/main/java/com/amazon/carbonado/spi/LobEngine.java index 143bd26..0262890 100644 --- a/src/main/java/com/amazon/carbonado/spi/LobEngine.java +++ b/src/main/java/com/amazon/carbonado/spi/LobEngine.java @@ -30,7 +30,6 @@ import java.util.Map; import java.util.List;
import org.cojen.util.KeyFactory;
-import org.cojen.util.SoftValuedHashMap;
import com.amazon.carbonado.Cursor;
import com.amazon.carbonado.FetchException;
@@ -58,6 +57,8 @@ import com.amazon.carbonado.lob.Lob; import com.amazon.carbonado.sequence.SequenceValueGenerator;
import com.amazon.carbonado.sequence.SequenceValueProducer;
+import com.amazon.carbonado.util.SoftValuedCache;
+
/**
* Complete Lob support for repositories, although repository is responsible
* for binding Lob properties to this engine. Lobs are referenced by locators,
@@ -88,7 +89,7 @@ public class LobEngine { final Storage<StoredLob.Block> mLobBlockStorage;
final SequenceValueProducer mLocatorSequence;
- private Map mTriggers;
+ private SoftValuedCache mTriggers;
/**
* @param lobRepo storage for Lobs - should not be replicated
@@ -460,7 +461,7 @@ public class LobEngine { }
if (mTriggers == null) {
- mTriggers = new SoftValuedHashMap();
+ mTriggers = SoftValuedCache.newCache(7);
}
mTriggers.put(key, trigger);
diff --git a/src/main/java/com/amazon/carbonado/util/BelatedCreator.java b/src/main/java/com/amazon/carbonado/util/BelatedCreator.java index 513a0c0..cc476cf 100644 --- a/src/main/java/com/amazon/carbonado/util/BelatedCreator.java +++ b/src/main/java/com/amazon/carbonado/util/BelatedCreator.java @@ -36,7 +36,6 @@ import org.cojen.classfile.Modifiers; import org.cojen.classfile.TypeDesc;
import org.cojen.util.ClassInjector;
-import org.cojen.util.SoftValuedHashMap;
/**
* Generic one-shot factory which supports late object creation. If the object
@@ -54,12 +53,12 @@ import org.cojen.util.SoftValuedHashMap; public abstract class BelatedCreator<T, E extends Exception> {
private static final String REF_FIELD_NAME = "ref";
- private static final Map<Class<?>, Class<?>> cWrapperCache;
+ private static final SoftValuedCache<Class<?>, Class<?>> cWrapperCache;
private static final ExecutorService cThreadPool;
static {
- cWrapperCache = new SoftValuedHashMap();
+ cWrapperCache = SoftValuedCache.newCache(11);
cThreadPool = Executors.newCachedThreadPool(new TFactory());
}
diff --git a/src/main/java/com/amazon/carbonado/util/Converter.java b/src/main/java/com/amazon/carbonado/util/Converter.java index 61f832a..44f28eb 100644 --- a/src/main/java/com/amazon/carbonado/util/Converter.java +++ b/src/main/java/com/amazon/carbonado/util/Converter.java @@ -38,7 +38,6 @@ import org.cojen.classfile.Opcode; import org.cojen.classfile.TypeDesc;
import org.cojen.util.ClassInjector;
-import org.cojen.util.SoftValuedHashMap;
/**
* General purpose type converter. Custom conversions are possible by supplying
@@ -50,7 +49,8 @@ import org.cojen.util.SoftValuedHashMap; * @since 1.2
*/
public abstract class Converter {
- private static final Map<Class, Class<? extends Converter>> cCache = new SoftValuedHashMap();
+ private static final SoftValuedCache<Class, Class<? extends Converter>> cCache =
+ SoftValuedCache.newCache(7);
/**
* @param converterType type of converter to generate
diff --git a/src/main/java/com/amazon/carbonado/util/QuickConstructorGenerator.java b/src/main/java/com/amazon/carbonado/util/QuickConstructorGenerator.java index 7a10440..a746c1f 100644 --- a/src/main/java/com/amazon/carbonado/util/QuickConstructorGenerator.java +++ b/src/main/java/com/amazon/carbonado/util/QuickConstructorGenerator.java @@ -29,7 +29,6 @@ import org.cojen.classfile.CodeBuilder; import org.cojen.classfile.TypeDesc;
import org.cojen.util.ClassInjector;
import org.cojen.util.WeakIdentityMap;
-import org.cojen.util.SoftValuedHashMap;
/**
* Generates code to invoke constructors. This is a replacement for {@link
@@ -48,7 +47,7 @@ import org.cojen.util.SoftValuedHashMap; public class QuickConstructorGenerator {
// Map<factory class, Map<object type, factory instance>>
@SuppressWarnings("unchecked")
- private static Map<Class<?>, Map<Class<?>, Object>> cCache = new WeakIdentityMap();
+ private static Map<Class<?>, SoftValuedCache<Class<?>, Object>> cCache = new WeakIdentityMap();
/**
* Returns a factory instance for one type of object. Each method in the
@@ -87,9 +86,9 @@ public class QuickConstructorGenerator { */
@SuppressWarnings("unchecked")
public static synchronized <F> F getInstance(Class<?> objectType, Class<F> factory) {
- Map<Class<?>, Object> innerCache = cCache.get(factory);
+ SoftValuedCache<Class<?>, Object> innerCache = cCache.get(factory);
if (innerCache == null) {
- innerCache = new SoftValuedHashMap();
+ innerCache = SoftValuedCache.newCache(7);
cCache.put(factory, innerCache);
}
F instance = (F) innerCache.get(objectType);
diff --git a/src/main/java/com/amazon/carbonado/util/SoftValuedCache.java b/src/main/java/com/amazon/carbonado/util/SoftValuedCache.java new file mode 100644 index 0000000..3e9e767 --- /dev/null +++ b/src/main/java/com/amazon/carbonado/util/SoftValuedCache.java @@ -0,0 +1,372 @@ +/*
+ * Copyright 2010 Amazon Technologies, Inc. or its affiliates.
+ * Amazon, Amazon.com and Carbonado are trademarks or registered trademarks
+ * of Amazon Technologies, Inc. or its affiliates. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.amazon.carbonado.util;
+
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.SoftReference;
+
+/**
+ * Simple thread-safe cache which evicts entries via a shared background
+ * thread. Cache permits null keys, but not null values.
+ *
+ * @author Brian S O'Neill
+ */
+public abstract class SoftValuedCache<K, V> {
+ // Design note: Public class is abstract to make it easy to swap out this
+ // implementation with something available in a shared collection library.
+
+ public static <K, V> SoftValuedCache<K, V> newCache(int capacity) {
+ return new Impl<K, V>(capacity);
+ }
+
+ public abstract int size();
+
+ public abstract boolean isEmpty();
+
+ public abstract V get(K key);
+
+ public abstract V put(K key, V value);
+
+ public abstract V putIfAbsent(K key, V value);
+
+ public abstract V remove(K key);
+
+ public abstract boolean remove(K key, V value);
+
+ public abstract boolean replace(K key, V oldValue, V newValue);
+
+ public abstract V replace(K key, V value);
+
+ public abstract void clear();
+
+ public abstract String toString();
+
+ private static class Impl<K, V> extends SoftValuedCache<K, V> {
+ private static final float LOAD_FACTOR = 0.75f;
+
+ private Entry<K, V>[] mEntries;
+ private int mSize;
+ private int mThreshold;
+
+ Impl(int capacity) {
+ mEntries = new Entry[capacity];
+ mThreshold = (int) (capacity * LOAD_FACTOR);
+ }
+
+ @Override
+ public synchronized int size() {
+ return mSize;
+ }
+
+ @Override
+ public synchronized boolean isEmpty() {
+ return mSize == 0;
+ }
+
+ @Override
+ public synchronized V get(K key) {
+ int hash = key == null ? 0 : key.hashCode();
+ Entry<K, V>[] entries = mEntries;
+ int index = (hash & 0x7fffffff) % entries.length;
+ for (Entry<K, V> e = entries[index]; e != null; e = e.mNext) {
+ if (e.matches(key, hash)) {
+ return e.get();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public synchronized V put(K key, V value) {
+ int hash = key == null ? 0 : key.hashCode();
+ Entry<K, V>[] entries = mEntries;
+ int index = (hash & 0x7fffffff) % entries.length;
+ for (Entry<K, V> e = entries[index], prev = null; e != null; e = e.mNext) {
+ if (e.matches(key, hash)) {
+ V old = e.get();
+ e.clear();
+ Entry<K, V> newEntry;
+ if (prev == null) {
+ newEntry = new Entry<K, V>(this, hash, key, value, e.mNext);
+ } else {
+ prev.mNext = e.mNext;
+ newEntry = new Entry<K, V>(this, hash, key, value, entries[index]);
+ }
+ entries[index] = newEntry;
+ return old;
+ } else {
+ prev = e;
+ }
+ }
+
+ if (mSize >= mThreshold) {
+ cleanup();
+ if (mSize >= mThreshold) {
+ rehash();
+ entries = mEntries;
+ index = (hash & 0x7fffffff) % entries.length;
+ }
+ }
+
+ entries[index] = new Entry<K, V>(this, hash, key, value, entries[index]);
+ mSize++;
+ return null;
+ }
+
+ @Override
+ public synchronized V putIfAbsent(K key, V value) {
+ V existing = get(key);
+ return existing == null ? put(key, value) : existing;
+ }
+
+ @Override
+ public synchronized V remove(K key) {
+ int hash = key == null ? 0 : key.hashCode();
+ Entry<K, V>[] entries = mEntries;
+ int index = (hash & 0x7fffffff) % entries.length;
+ for (Entry<K, V> e = entries[index], prev = null; e != null; e = e.mNext) {
+ if (e.matches(key, hash)) {
+ V old = e.get();
+ e.clear();
+ if (prev == null) {
+ entries[index] = e.mNext;
+ } else {
+ prev.mNext = e.mNext;
+ }
+ mSize--;
+ return old;
+ } else {
+ prev = e;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public synchronized boolean remove(K key, V value) {
+ V existing = get(key);
+ if (existing != null && existing.equals(value)) {
+ remove(key);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public synchronized boolean replace(K key, V oldValue, V newValue) {
+ V existing = get(key);
+ if (existing != null && existing.equals(oldValue)) {
+ put(key, newValue);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public synchronized V replace(K key, V value) {
+ return get(key) == null ? null : put(key, value);
+ }
+
+ @Override
+ public synchronized void clear() {
+ Entry[] entries = mEntries;
+ for (int i=entries.length; --i>=0; ) {
+ Entry e = entries[i];
+ if (e != null) {
+ e.clear();
+ entries[i] = null;
+ }
+ }
+ mSize = 0;
+ }
+
+ @Override
+ public synchronized String toString() {
+ if (isEmpty()) {
+ return "{}";
+ }
+
+ StringBuilder b = new StringBuilder();
+ b.append('{');
+
+ Entry<K, V>[] entries = mEntries;
+ int removed = 0;
+ boolean any = false;
+
+ for (int i=entries.length; --i>=0 ;) {
+ for (Entry<K, V> e = entries[i], prev = null; e != null; e = e.mNext) {
+ V value = e.get();
+ if (value == null) {
+ // Clean up after a cleared Reference.
+ if (prev == null) {
+ entries[i] = e.mNext;
+ } else {
+ prev.mNext = e.mNext;
+ }
+ removed++;
+ } else {
+ prev = e;
+ if (any) {
+ b.append(',').append(' ');
+ }
+ K key = e.mKey;
+ b.append(key).append('=').append(value);
+ any = true;
+ }
+ }
+ }
+
+ mSize -= removed;
+
+ b.append('}');
+ return b.toString();
+ }
+
+ synchronized void removeCleared(Entry<K, V> cleared) {
+ Entry<K, V>[] entries = mEntries;
+ int index = (cleared.mHash & 0x7fffffff) % entries.length;
+ for (Entry<K, V> e = entries[index], prev = null; e != null; e = e.mNext) {
+ if (e == cleared) {
+ if (prev == null) {
+ entries[index] = e.mNext;
+ } else {
+ prev.mNext = e.mNext;
+ }
+ mSize--;
+ return;
+ } else {
+ prev = e;
+ }
+ }
+ }
+
+ private synchronized void cleanup() {
+ Entry<K, V>[] entries = mEntries;
+ int removed = 0;
+
+ for (int i=entries.length; --i>=0 ;) {
+ for (Entry<K, V> e = entries[i], prev = null; e != null; e = e.mNext) {
+ if (e.get() == null) {
+ // Clean up after a cleared Reference.
+ if (prev == null) {
+ entries[i] = e.mNext;
+ } else {
+ prev.mNext = e.mNext;
+ }
+ removed++;
+ } else {
+ prev = e;
+ }
+ }
+ }
+
+ mSize -= removed;
+ }
+
+ private synchronized void rehash() {
+ Entry<K, V>[] oldEntries = mEntries;
+ int newCapacity = oldEntries.length * 2 + 1;
+ Entry<K, V>[] newEntries = new Entry[newCapacity];
+ int removed = 0;
+
+ for (int i=oldEntries.length; --i>=0 ;) {
+ for (Entry<K, V> old = oldEntries[i]; old != null; ) {
+ Entry<K, V> e = old;
+ old = old.mNext;
+ // Only copy entry if its value hasn't been cleared.
+ if (e.get() == null) {
+ removed++;
+ } else {
+ int index = (e.mHash & 0x7fffffff) % newCapacity;
+ e.mNext = newEntries[index];
+ newEntries[index] = e;
+ }
+ }
+ }
+
+ mEntries = newEntries;
+ mSize -= removed;
+ mThreshold = (int) (newCapacity * LOAD_FACTOR);
+ }
+ }
+
+ private static class Entry<K, V> extends Ref<V> {
+ final Impl<K, V> mCache;
+ final int mHash;
+ final K mKey;
+ Entry mNext;
+
+ Entry(Impl<K, V> cache, int hash, K key, V value, Entry next) {
+ super(value);
+ mCache = cache;
+ mHash = hash;
+ mKey = key;
+ mNext = next;
+ }
+
+ @Override
+ void remove() {
+ mCache.removeCleared(this);
+ }
+
+ boolean matches(K key, int hash) {
+ return hash == mHash && (key == null ? mKey == null : key.equals(mKey));
+ }
+ }
+
+ final static Evictor cEvictor;
+
+ static {
+ Evictor evictor = new Evictor();
+ evictor.setName("SoftValuedCache Evictor");
+ evictor.setDaemon(true);
+ evictor.setPriority(Thread.MAX_PRIORITY);
+ evictor.start();
+ cEvictor = evictor;
+ }
+
+ private static abstract class Ref<T> extends SoftReference<T> {
+ Ref(T referent) {
+ super(referent, cEvictor.mQueue);
+ }
+
+ abstract void remove();
+ }
+
+ private static class Evictor extends Thread {
+ final ReferenceQueue<Object> mQueue;
+
+ Evictor() {
+ mQueue = new ReferenceQueue<Object>();
+ }
+
+ @Override
+ public void run() {
+ try {
+ while (true) {
+ ((Ref) mQueue.remove()).remove();
+ }
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+}
|