package org.geotools.util; import java.lang.ref.Reference; import java.lang.ref.SoftReference; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.ReentrantLock; /** * Caching implementation for {@link ObjectCache}. This instance is used when * caching is desired, and memory use is an issue. * <p> * Values are held in a SoftReference, the garbage collector may reclaim them at * any time. * * <p>From the soft reference javadocs:<br> * Soft reference objects, which are cleared at the discretion of the garbage collector in response to memory demand. Soft references are most often used to implement memory-sensitive caches.</p> * * @since 2.6 * @version $Id$ * @source $URL: * http://svn.geotools.org/trunk/modules/library/metadata/src/main/ * java/org/geotools/util/SoftObjectCache.java $ * @author Emily Gouge (Refractions Research) */ final class SoftObjectCache implements ObjectCache { /** * The cached values for each key. */ private final Map/*<Object,SoftReference<Object>>*/ cache; /** * The locks for keys under construction. */ private final Map/*<Object,ReentrantLock>*/ locks; /** * Creates a new cache. */ public SoftObjectCache(){ this(50); } /** * Creates a new cache using the indicated initialSize. */ public SoftObjectCache(final int initialSize){ cache = Collections.synchronizedMap(new HashMap<Object, SoftReference<Object>>(initialSize)); locks = new HashMap<Object, ReentrantLock>(); } /** * Removes all entries from this map. */ public void clear() { synchronized (locks) { locks.clear(); cache.clear(); } } /** * Returns the indicated object from the cache, or null if not found. * * @param key * The authority code. */ public Object get( final Object key ) { Object stored = cache.get(key); if (stored instanceof Reference) { Reference reference = (Reference) stored; Object value = reference.get(); if (value == null) { cache.remove(key); } return value; } return stored; } /** * @return a copy of the keys currently in the map */ public Set<Object> getKeys() { Set<Object> keys = null; keys = new HashSet<Object>(cache.keySet()); return keys; } public Object peek( final Object key ) { Object stored = cache.get(key); if (stored instanceof Reference) { Reference reference = (Reference) stored; return reference.get(); } return stored; } /** * Stores a value */ public void put( final Object key, final Object object ) { writeLock(key); SoftReference reference = new SoftReference(object); cache.put(key, reference); writeUnLock(key); } /** * Removes the given key from the cache. */ public void remove( final Object key ) { synchronized (locks) { locks.remove(key); cache.remove(key); } } public void writeLock( final Object key ) { ReentrantLock lock; synchronized (locks) { lock = (ReentrantLock) locks.get(key); if (lock == null) { lock = new ReentrantLock(); locks.put(key, lock); } } // Must be outside the above synchronized section, since this call may // block. lock.lock(); } public void writeUnLock( final Object key ) { synchronized (locks) { final ReentrantLock lock = (ReentrantLock) locks.get(key); if (lock == null) { throw new IllegalMonitorStateException( "Cannot unlock prior to locking"); } if (lock.getHoldCount() == 0) { throw new IllegalMonitorStateException( "Cannot unlock prior to locking"); } lock.unlock(); } } }