/* * The contents of this file are subject to the Mozilla Public License Version 1.1 * (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at . * * Software distributed under the License is distributed on an "AS IS" basis, WITHOUT * WARRANTY OF ANY KIND, either express or implied. See the License for the specific * language governing rights and limitations under the License. * * The Original Code is the Venice Web Communities System. * * The Initial Developer of the Original Code is Eric J. Bowersox , * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are * Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved. * * Contributor(s): */ package com.silverwrist.util.cache; import java.lang.ref.*; import java.util.*; /** * A chache which stores objects by key value, and is capable of creating them given an instance of * ObjectFactory to do the creating. The cache uses SoftReferences which can * automatically be removed by the garbage collector if necessary. * * @author Eric J. Bowersox <erbo@silcom.com> * @version X * @see ObjectFactory * @see java.lang.ref.SoftReference */ public class ObjectCache { /*-------------------------------------------------------------------------------- * Attributes *-------------------------------------------------------------------------------- */ private HashMap the_data = new HashMap(); // the actual underlying map private ObjectFactory factory; // used to create new objects private ReferenceQueue rq = new ReferenceQueue(); // where our references go when they die private ArrayList sweeper = new ArrayList(); // temporary used in doing sweeps /*-------------------------------------------------------------------------------- * Constructor *-------------------------------------------------------------------------------- */ /** * Creates a new ObjectCache. * * @param factory The factory object used to create new objects when getOrCreate is called. * @exception java.lang.NullPointerException The object factory passed in is null. */ public ObjectCache(ObjectFactory factory) { if (factory==null) throw new NullPointerException("object factory cannot be null!"); this.factory = factory; } // end constructor /*-------------------------------------------------------------------------------- * Internal operations *-------------------------------------------------------------------------------- */ /** * "Sweeps" the cache by taking all references that have been cleared and queued by the garbage * collector and removing them from the map. Should be called fairly often, to minimize wasted * hashmap slots. */ private synchronized void doSweep() { Set entries = the_data.entrySet(); // used to find entries with the specified value Reference r = rq.poll(); // reference that's been cleared Iterator it; while (r!=null) { // look for this reference in our hash map it = entries.iterator(); while (it.hasNext()) { // look for the map entry containing the reference Map.Entry ntry = (Map.Entry)(it.next()); if (r==(Reference)(ntry.getValue())) { // found the entry with this reference - nuke it sweeper.add(ntry.getKey()); break; // don't need to take this loop any farther } // end if } // end while r = rq.poll(); // get the next cleared reference } // end while if (sweeper.isEmpty()) return; // no entries to remove // Remove all the corresponding keys from the hashmap. it = sweeper.iterator(); while (it.hasNext()) the_data.remove(it.next()); sweeper.clear(); // reset for next time } // end doSweep /*-------------------------------------------------------------------------------- * External operations *-------------------------------------------------------------------------------- */ /** * Retrieves an object from the cache. * * @param key The key value of the object to look up. * @return The corresponding object value, or null if that object isn't in the cache. */ public synchronized Object get(Object key) { doSweep(); SoftReference r = (SoftReference)(the_data.get(key)); return ((r==null) ? null : r.get()); } // end get /** * Retrieves an object from the cache, creating it if it doesn't already exist. * * @param key The key value of the object to look up or create. * @return The corresponding object value. * @exception com.silverwrist.util.cache.ObjectFactoryException If an exception occurred while creating * a new object. * @see ObjectFactory#newObject(java.lang.Object) */ public synchronized Object getOrCreate(Object key) throws ObjectFactoryException { doSweep(); SoftReference r = (SoftReference)(the_data.get(key)); Object rc = ((r==null) ? null : r.get()); if (rc==null) { // attempt to create a new object rc = factory.newObject(key); if (rc!=null) { // clear the old reference, throw it away, and put in a new one if (r!=null) r.clear(); r = new SoftReference(rc,rq); the_data.put(key,r); } // end if } // end if return rc; } // end getOrCreate /** * Registers a newly-created object with the cache. * * @param key The key value to register this object with. * @param data The object to be registered. * @exception com.silverwrist.util.cache.ObjectCacheException If an object with that key value already * exists in the cache. */ public synchronized void register(Object key, Object data) { doSweep(); SoftReference old = (SoftReference)(the_data.get(key)); if ((old!=null) && (old.get()!=null)) throw new ObjectCacheException("object already in cache",key); the_data.put(key,new SoftReference(data,rq)); if (old!=null) old.clear(); } // end register /** * Detaches an object from the cache. * * @param key Key value of the object to be detached. */ public synchronized void detach(Object key) { doSweep(); SoftReference old = (SoftReference)(the_data.remove(key)); if (old!=null) old.clear(); } // end detach } // end class ObjectCache