/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2007-2008, Open Source Geospatial Foundation (OSGeo) * * 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; * version 2.1 of the License. * * 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. */ package org.geotools.util; import java.lang.ref.Reference; import java.lang.ref.WeakReference; 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 WeakReference, the garbage collector may reclaim them at * any time. * * @since 2.5 * @version $Id$ * @source $URL: * http://svn.geotools.org/trunk/modules/library/metadata/src/main/ * java/org/geotools/util/WeakObjectCache.java $ * @author Cory Horner (Refractions Research) * @author Jody Garnett (Refractions Research) * @author Martin Desruisseaux (Geomatys) */ final class WeakObjectCache implements ObjectCache { /** * The cached values for each key. */ private final Map/* <Object,WeakReference> */cache; /** * The locks for keys under construction. */ private final Map/* <K,ReentrantLock> */locks; /** * Creates a new cache. */ public WeakObjectCache() { this(50); } /** * Creates a new cache using the indicated initialSize. */ public WeakObjectCache(final int initialSize) { cache = Collections.synchronizedMap(new HashMap(initialSize)); locks = new HashMap(); } /** * Removes all entries from this map. */ public void clear() { synchronized (locks) { locks.clear(); cache.clear(); } } /** * Check if an entry exists in the cache. * * @param key * @return boolean */ public boolean containsKey(final Object key) { if (cache.containsKey(key)) { Object stored = cache.get(key); if (stored instanceof Reference) { Reference reference = (Reference) stored; return reference.get() != null; } return stored != null; } return false; } /** * 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; } public Object peek(final Object key) { Object stored = cache.get(key); if (stored instanceof Reference) { Reference reference = (Reference) stored; return reference.get(); } return stored; } 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(); // TODO: stop lock from being removed when another worker is trying // to acquire it // TODO: review w/ J2SE 5.0 // if (lock.holds() == 0) { // locks.remove(key); // } } } boolean holdsLock(final Object key) { synchronized (locks) { final ReentrantLock lock = (ReentrantLock) locks.get(key); if (lock != null) { return lock.getHoldCount() != 0; } } return false; } /** * Stores a value */ public void put(final Object key, final Object object) { writeLock(key); WeakReference reference = new WeakReference(object); cache.put(key, reference); writeUnLock(key); } /** * @return the keys of the object currently in the set */ public Set<Object> getKeys() { Set<Object> keys = null; keys = new HashSet<Object>(cache.keySet()); return keys; } /** * Removes the given key from the cache. */ public void remove(Object key) { synchronized (locks) { locks.remove(key); cache.remove(key); } } }