It doesn't seem as if anyone has submitted this patch into the CVS
repository for testing... Is this any way to encourage new participants?
Chris
>From: "christopher marshall" <[EMAIL PROTECTED]>
>Reply-To: "Jakarta Commons Developers List"
><[EMAIL PROTECTED]>
>To: [EMAIL PROTECTED]
>Subject: PATCH : org.apache.commons.collections.SoftRefHashMap
>Date: Fri, 17 May 2002 16:03:33 +0000
>
>All -
>
>Please let me know if this is OK as I haven't submitted code before.
>
>Patches fix the SoftRefHashMap so that the "entrySet()" and "values()"
>methods are backed by the underlying map and the keys whose referent-values
>have been cleared will be automatically (and performant-ly) purged upon
>writes to the map. I have deprecated the "purge()" method and
>re-implemented
>it to call "processQueue()", which is a more performant way of purging the
>Map as it goes straight to the cleared values without iterating through the
>whole Map.
>
>The Map also has the new method "getIfAbsentPut(Object key, ObjectFactory
>fac)" which takes a new paramter of type
>org.apache.common.collections.ObjectFactory (an Interface). It aids the use
>of this map as a cache (see below).
>
>I feel that the idea to extend the class with "createReference()"
>overimplemented is a bad one, and have removed the functionality. Among
>other things, it is difficult to see how this would be implemented here
>without unnecessary complications - considering the functionality is
>questionable (to extend SoftRefHashMap to actually be a WeakRefHashMap?) I
>think that this is justified.
>
>I include two files org.apache.commons.collections.SoftRefHashMap and
>org.apache.commons.collections.ObjectFactory.
>
>The idea of Object factory is so that the Map can be used effectively as a
>Cache; suppose that a User wishes to store (expensively retrieved) database
>entries in the Map which must always be retrieved (whether the referent
>value has since been GC-ed or not). The user code looks like
>
>//
>public UserObject apiGetUser(final String userId) {
> return (UserObject) map.getIfAbsentPut(userId, new ObjectFactory() {
> public Object create() {
> //expensive database access to return the
> //required Object
> Object user = expensiveDBAccess( userId );
> return user;
> }
> });
>}
>//
>
>The ObjectFactory.create() will only be invoked (and the expensive
>operation
>executed) should the value have been cleared from the Map (ie. GC-d). The
>factory method will be invoked and the Object stored in the map for the
>key.
>
>Anyway here is the source code...
>
>
>*****************************************
>
>
>/**
>*
>* The Apache Software License, Version 1.1
>*
>* Copyright (c) 1999-2002 The Apache Software Foundation. All rights
>* reserved.
>*
>* Redistribution and use in source and binary forms, with or without
>* modification, are permitted provided that the following conditions
>* are met:
>*
>* 1. Redistributions of source code must retain the above copyright
>* notice, this list of conditions and the following disclaimer.
>*
>* 2. Redistributions in binary form must reproduce the above copyright
>* notice, this list of conditions and the following disclaimer in
>* the documentation and/or other materials provided with the
>* distribution.
>*
>* 3. The end-user documentation included with the redistribution, if
>* any, must include the following acknowlegement:
>* "This product includes software developed by the
>* Apache Software Foundation (http://www.apache.org/)."
>* Alternately, this acknowlegement may appear in the software itself,
>* if and wherever such third-party acknowlegements normally appear.
>*
>* 4. The names "The Jakarta Project", "Commons", and "Apache Software
>* Foundation" must not be used to endorse or promote products derived
>* from this software without prior written permission. For written
>* permission, please contact [EMAIL PROTECTED]
>*
>* 5. Products derived from this software may not be called "Apache"
>* nor may "Apache" appear in their names without prior written
>* permission of the Apache Group.
>*
>* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
>* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
>* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
>* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
>* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
>* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
>* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
>* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
>* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
>* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
>* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
>* SUCH DAMAGE.
>* ====================================================================
>*
>* This software consists of voluntary contributions made by many
>* individuals on behalf of the Apache Software Foundation. For more
>* information on the Apache Software Foundation, please see
>* <http://www.apache.org/>.
>*
>*/
>package org.apache.commons.collections;
>
>/**
>* This interface provides an abstract Object factory.
>* @author Chris Marshall
>* @version 1.0
>*/
>
>public interface ObjectFactory {
> /**
> * return an Object.
> */
> public Object create();
>}
>
>*******************************************************************
>
>
>/*
>*
>* The Apache Software License, Version 1.1
>*
>* Copyright (c) 1999-2002 The Apache Software Foundation. All rights
>* reserved.
>*
>* Redistribution and use in source and binary forms, with or without
>* modification, are permitted provided that the following conditions
>* are met:
>*
>* 1. Redistributions of source code must retain the above copyright
>* notice, this list of conditions and the following disclaimer.
>*
>* 2. Redistributions in binary form must reproduce the above copyright
>* notice, this list of conditions and the following disclaimer in
>* the documentation and/or other materials provided with the
>* distribution.
>*
>* 3. The end-user documentation included with the redistribution, if
>* any, must include the following acknowlegement:
>* "This product includes software developed by the
>* Apache Software Foundation (http://www.apache.org/)."
>* Alternately, this acknowlegement may appear in the software itself,
>* if and wherever such third-party acknowlegements normally appear.
>*
>* 4. The names "The Jakarta Project", "Commons", and "Apache Software
>* Foundation" must not be used to endorse or promote products derived
>* from this software without prior written permission. For written
>* permission, please contact [EMAIL PROTECTED]
>*
>* 5. Products derived from this software may not be called "Apache"
>* nor may "Apache" appear in their names without prior written
>* permission of the Apache Group.
>*
>* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
>* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
>* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
>* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
>* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
>* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
>* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
>* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
>* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
>* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
>* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
>* SUCH DAMAGE.
>* ====================================================================
>*
>* This software consists of voluntary contributions made by many
>* individuals on behalf of the Apache Software Foundation. For more
>* information on the Apache Software Foundation, please see
>* <http://www.apache.org/>.
>*
>*/
>package org.apache.commons.collections;
>
>import java.lang.ref.*;
>import java.lang.reflect.*;
>import java.util.*;
>
>/** <p>
> * HashMap with SoftReference links to values which allows the values of
>the Map
> * to be garbage collected by the JVM if it becomes low on memory.
> * Derive from this class and
> * override the factory method <code>createReference()</code> method to
>make
> * a Map wrapped in other types of Reference.
> * </p>
> *
> * <p>
> * A synchronized version can be obtained with:
> * <code>Collections.synchronizedMap( theMapToSynchronize )</code>
> * </p>
> *
> * <p>
> * <b>WARNING</b> the values() and entrySet() methods require optimisation
> * like the standard {@link HashMap} implementations so that iteration
> * over this Map is efficient.
> * </p>
> *
> * @author James.Dodd
> * @author <a href="mailto:[EMAIL PROTECTED]">James Strachan</a>
> */
>public class SoftRefHashMap implements Map {
>
> /** The wrapped HashMap */
> private Map hashMap = new HashMap();
> private ReferenceQueue queue = new ReferenceQueue();
>
> // Inner Classes
> //
>---------------------------------------------------------------------
>
> /**
> * Internal factory class to return <tt>Reference</tt>
> * implementation.
> */
> private static class RefFactory {
> private static Reference reference(
> Object o,
> Object key,
> ReferenceQueue queue) {
> return SoftKey.create(o, key, queue);
> }
> }
>/**
> * Internal extension of <tt>SoftReference</tt>
> * to be stored as the <tt>value</tt> in the
> * underlying map.
> */
> static private class SoftKey extends SoftReference {
> private int hash;
> private Object key;
> /* Hashcode of key, stored here since the key
> may be tossed by the GC */
>
> private SoftKey(Object k, Object key, ReferenceQueue queue) {
> super(k, queue);
> this.key = key;
> hash = k.hashCode();
> }
>
> private static SoftKey create(Object k, Object key, ReferenceQueue
>queue)
>{
> if (k == null)
> return null;
> else
> return new SoftKey(k, key, queue);
> }
>
> /* A SoftKey is equal to another WeakKey iff they both refer to objects
> that are, in turn, equal according to their own equals methods */
> public boolean equals(Object o) {
> if (this == o)
> return true;
> if (!(o instanceof SoftKey))
> return false;
> Object t = this.get();
> Object u = ((SoftKey) o).get();
> if ((t == null) || (u == null))
> return false;
> if (t == u)
> return true;
> return t.equals(u);
> }
> public Object getKey() {
> return this.key;
> }
>
> public int hashCode() {
> return hash;
> }
>
> public String toString() {
> return "SoftKey(" + get() + ")";
> }
> }
>
> /**
> * Internal implementation of <tt>Map.Entry</tt>
> * for presentation through the <tt>EntryIterator</tt>
> */
> private class SoftKeyEntry implements Map.Entry {
> private Object key;
> private Reference value;
>
> private Map.Entry underlying;
> private SoftKeyEntry(Object key, Reference value) {
> this.key = key;
> this.value = value;
> }
>
> private SoftKeyEntry(Map.Entry e) {
> this.underlying = e;
> this.key = e.getKey();
> if (e instanceof SoftKeyEntry)
> this.value = ((SoftKeyEntry) e).getRef();
> else if (e.getValue() instanceof Reference)
> this.value = (Reference) e.getValue();
> else
> this.value = RefFactory.reference(e.getValue(), e.getKey(),
>queue);
> }
> public Object getKey() {
> return this.key;
> }
> public Object getValue() {
> return this.value.get();
> }
> public Object setValue(Object o) {
> if (underlying != null)
> underlying.setValue(o);
> Object oldValue = value.get();
> this.value = RefFactory.reference(o, getKey(), queue);
> return oldValue;
> }
>
> private Reference getRef() {
> return this.value;
> }
> }
>
> /**
> * Internal <tt>Iterator</tt> representation to iterate
> * Through the underlying <tt>values()</tt>
> */
> private class ValueIterator implements Iterator {
> private Iterator iterator;
> private ValueIterator() {
> this.iterator = getMap().values().iterator();
> }
> public boolean hasNext() {
> return this.iterator.hasNext();
> }
>
> public Object next() {
> if (!this.iterator.hasNext())
> throw new NoSuchElementException();
> Object o = null;
> while (o == null && this.iterator.hasNext()) {
> Reference ref = (Reference) this.iterator.next();
> if ((o = ref.get()) == null)
> this.iterator.remove();
> }
> return o;
> }
>
> public void remove() {
> this.iterator.remove();
> }
> }
>
> /**
> * Internal <tt>Iterator</tt> representation to
> * iterate through the underlying <tt>entrySet()</tt>.
> */
> private class EntryIterator implements Iterator {
> private Iterator iterator;
> private EntryIterator() {
> this.iterator = getMap().entrySet().iterator();
> }
>
> public boolean hasNext() {
> return this.iterator.hasNext();
> }
>
> public Object next() {
> if (!this.iterator.hasNext())
> throw new NoSuchElementException();
> Map.Entry entry = null;
> while (entry == null && this.iterator.hasNext()) {
> Map.Entry e = (Map.Entry) this.iterator.next();
> Reference ref = (Reference) e.getValue();
> if (ref.get() == null)
> this.iterator.remove();
> else
> entry = new SoftKeyEntry(e);
> }
> return entry;
> }
>
> public void remove() {
> this.iterator.remove();
> }
> }
> public SoftRefHashMap() {
> }
>
>
> /**
> * Removes References that have had their referents garbage collected
> * @deprecated
> */
> public void purge() {
> processQueue();
> }
>
> /**
> * Retrieves the referent of the Referenced value
> * @param key The key with which to retrieve the value
> */
> public Object get(Object key) {
> SoftReference ref = (SoftReference) getMap().get(key);
> if (ref == null)
> return null;
> Object o = null;
> if ((o = ref.get()) == null)
> remove(key);
> return o;
> }
>
> /**
> * Adds a key-value mapping, wrapping the value in a Reference
> */
> public Object put(Object key, Object value) {
> processQueue();
> return getMap().put(key, RefFactory.reference(value, key, queue));
> }
>
> /**
> * Process the reference queue to clean up.
> * Creation date: (07/05/02 10:13:59)
> */
> private void processQueue() {
> Reference r = null;
> while ((r = queue.poll()) != null)
> getMap().remove(((SoftKey) r).getKey());
> }
> /**
> * Returns a collection of the Referenced values
> */
> public java.util.Collection values() {
> return new AbstractCollection() {
> public int size() {
> return getMap().size();
> }
>
> public Iterator iterator() {
> return new ValueIterator();
>
> }
> };
>
> }
>
> /**
> * Returns the value to which this map maps the specified key. If the
> * map does not contain the key, or if the value has been cleared by
>the
> * GarbageCollector, the supplied <tt>ObjectFactory</tt> is used to
>place
> * a new value in the map foir the key. This new value is returned.
> *
> * @param key key whose associated value is to be returned.
> * @param objectFactory factory to create the Object with which to put
> * the <tt>Map</tt>
> * @return the value to which this map maps the specified key, or
> * <tt>objectFactory.create()</tt> if the map contains no
> * mapping (or the mapping has been cleared) for this key.
> *
> * @throws ClassCastException if the key is of an inappropriate type
>for
> * this map.
> * @throws NullPointerException key is <tt>null</tt> and this map does
>not
> * not permit <tt>null</tt> keys.
> * @throws Exception if the <tt>ObjectFactory</tt>'s
> * <tt>create()</tt> method throws an Exception
> *
> * @see #containsKey(Object)
> */
> public Object getIfAbsentPut(Object key,
>org.apache.commons.collections.ObjectFactory objectFactory)
> throws Exception {
> Object o = get(key);
> if (o == null)
> put(key, o = objectFactory.create());
> return o;
> }
> /**
> * Answers whether the argument is in the domain of the mappings
> */
> public boolean containsKey( Object key ) {
> return getMap().containsKey( key );
> }
>
> /**
> * Answers whether the argument is a Referenced value
> */
> public boolean containsValue( Object value ) {
> Collection values = (Collection) getMap().values();
> if ( values == null ) {
> return false;
> }
> for ( Iterator i = values.iterator(); i.hasNext(); ) {
> Reference ref = (Reference) i.next();
> if ( ref == null ) {
> continue;
> }
> Object target = ref.get();
> if ( target == value ) {
> return true;
> }
> }
> return false;
> }
>
> /**
> * Put all of the mappings in the argument into this wrapped map
> */
> public void putAll(java.util.Map map) {
> if ( map == null || map.size() == 0 ) {
> return;
> }
> for (Iterator it = map.entrySet().iterator(); it.hasNext();) {
> Map.Entry e = (Map.Entry) it.next();
> put(e.getKey(), e.getValue());
> }
> }
>
> /**
> * Returns a set view of the mappings in the wrapped map
> */
> public java.util.Set entrySet() {
> return new AbstractSet() {
>
> public int size() {
> return getMap().size();
> }
>
> public Iterator iterator() {
> return new EntryIterator();
> }
> };
> }
>
> /**
> * Removes a mapping from this map
> */
> public Object remove(Object key) {
> SoftReference ref = (SoftReference) getMap().remove(key);
> if (ref != null) {
> return ref.get();
> }
> return null;
> }
>
> /**
> * Clears all mappings
> */
> public void clear() {
> getMap().clear();
> }
>
> /**
> * Calculates the hash code for this map
> */
> public int hashCode() {
> return getMap().hashCode();
> }
>
> /**
> * Returns the domain of the mappings
> */
> public Set keySet() {
> return getMap().keySet();
> }
>
> /**
> * Answers whether there are any mappings
> */
> public boolean isEmpty() {
> return getMap().isEmpty();
> }
>
> /**
> * Answers whether this map and the argument are 'the same'
> */
> public boolean equals( final Object object ) {
> return getMap().equals( object );
> }
>
> /**
> * Returns the number of mappings in this map
> */
> public int size() {
> return getMap().size();
> }
>
>
> /**
> * Retrieves the wrapped HashMap
> * @return The wrapped HashMap
> */
> protected Map getMap() {
> return hashMap;
> }
>}
>
>_________________________________________________________________
>Chat with friends online, try MSN Messenger: http://messenger.msn.com
>
>
>--
>To unsubscribe, e-mail:
><mailto:[EMAIL PROTECTED]>
>For additional commands, e-mail:
><mailto:[EMAIL PROTECTED]>
>
_________________________________________________________________
Send and receive Hotmail on your mobile device: http://mobile.msn.com
--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>