/*
* 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