001: package org.shiftone.cache.decorator.soft;
002:
003: import org.shiftone.cache.Cache;
004: import org.shiftone.cache.util.Log;
005: import org.shiftone.cache.util.reaper.ReapableCache;
006:
007: import java.lang.ref.Reference;
008: import java.lang.ref.ReferenceQueue;
009: import java.lang.ref.SoftReference;
010:
011: /**
012: * Memory sensitive cache.
013: * <p>
014: * This cache implementation proxies to a cache that was passed to it's constructor.
015: * When objects are added to the cache, the object wrapped in a SoftReference,
016: * and then the Reference is added to the delegate cache.
017: * <p>
018: * Once a SoftCache is created for a cache, the SoftCache should always be used to
019: * access this cache. Using the original cache directly is not recommended.
020: * <p>
021: * Note that there are <n>no guarantees</b> as to how long objects you put in cache will remain
022: * there. This is entirely at the digression of the garbage collector, which tends to
023: * be hasty about freeing up memory.
024: *
025: * @author <a href="mailto:jeff@shiftone.org">Jeff Drost</a>
026: * @version $Revision: 1.5 $
027: */
028: public class SoftCache implements Cache, ReapableCache {
029:
030: private static final Log LOG = new Log(SoftCache.class);
031:
032: //private static CacheReaper reaper = CacheReaper.getReaper();
033: private final ReferenceQueue referenceQueue = new ReferenceQueue();
034: private final Cache cache;
035:
036: public SoftCache(Cache cache) {
037:
038: if (cache instanceof SoftCache) {
039: throw new UnsupportedOperationException(
040: "SoftCache should not delegate to SoftCache");
041: }
042:
043: this .cache = cache;
044:
045: // reaper.register(this);
046: }
047:
048: public void addObject(Object userKey, Object cacheObject) {
049:
050: // thanks to JD Evora for the bug report
051: cache.addObject(userKey, new KeyReference(userKey, cacheObject,
052: referenceQueue));
053: }
054:
055: /**
056: * Gets a soft reference out of the underlying cache implementation, and then
057: * returns the value held by the reference.
058: */
059: public Object getObject(Object key) {
060:
061: Object result = null;
062: Reference ref = null;
063:
064: ref = (Reference) cache.getObject(key);
065:
066: if (ref != null) {
067: result = ref.get();
068:
069: if (result == null) {
070: LOG
071: .debug("reference found in cache but GC removed object");
072: cache.remove(key);
073: }
074: }
075:
076: return result;
077: }
078:
079: public final void remove(Object key) {
080: cache.remove(key);
081: }
082:
083: public final int size() {
084: return cache.size();
085: }
086:
087: public final void clear() {
088: cache.clear();
089: }
090:
091: /**
092: * Cleans all cache elements out that have had their objects collected by the GC.
093: */
094: public synchronized void removeExpiredElements() {
095:
096: Reference ref = null;
097: Object key = null;
098: int removeCount = 0;
099:
100: while ((ref = referenceQueue.poll()) != null) {
101: key = ((KeyReference) ref).getKey();
102:
103: cache.remove(key);
104:
105: removeCount++;
106: }
107:
108: LOG.debug(this + " removeExpiredElements() removed - "
109: + removeCount + " refs - " + size() + " left");
110: }
111:
112: public final String toString() {
113: return "SoftCache->" + cache;
114: }
115:
116: /**
117: * Class KeyReference
118: *
119: *
120: * @author <a href="mailto:jeff@shiftone.org">Jeff Drost</a>
121: * @version $Revision: 1.5 $
122: */
123: class KeyReference extends SoftReference {
124:
125: private Object key = null;
126:
127: /**
128: * Constructor KeyReference
129: *
130: *
131: * @param key
132: * @param cacheObject
133: * @param referenceQueue
134: */
135: public KeyReference(Object key, Object cacheObject,
136: ReferenceQueue referenceQueue) {
137:
138: super (cacheObject, referenceQueue);
139:
140: this .key = key;
141: }
142:
143: /**
144: * Method getKey
145: */
146: Object getKey() {
147: return key;
148: }
149: }
150: }
|