/* * 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.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 WealValueHashSet, the garbage collector * may reclaim them at any time. After the LIMIT is reached additional values * are ignored by the cache. * </p> * * @since 2.5 * @version $Id$ * @source $URL$ * @author Jody Garnett (Refractions Research) */ final class FixedSizeObjectCache implements ObjectCache { private final int LIMIT; /** * The cached values for each key. */ private final Map cache; /** * The locks for keys under construction. */ private final Map/*<K,ReentrantLock>*/ locks; /** * Creates a new cache. */ public FixedSizeObjectCache() { this( 50 ); } /** * Creates a new cache using the indicated initialSize. */ public FixedSizeObjectCache(final int initialSize) { LIMIT = initialSize; cache = Collections.synchronizedMap( new WeakValueHashMap(initialSize) ); locks = new HashMap(initialSize); } /** * 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) { return cache.containsKey(key); } /** * Returns the indicated object from the cache, or null if not found. * * @param key * The authority code. */ public Object get(final Object key) { return cache.get(key); } public Object peek(final Object key) { return cache.get(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(); if (lock.getHoldCount() == 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) { if( cache.size() < LIMIT ){ writeLock(key); cache.put(key, object); 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){ //ensure nobody else is writing to this key as we remove it synchronized (locks) { locks.remove(key); cache.remove(key); } } }