diff options
Diffstat (limited to 'src/org/apache/commons/logging/impl/WeakHashtable.java')
-rw-r--r-- | src/org/apache/commons/logging/impl/WeakHashtable.java | 478 |
1 files changed, 0 insertions, 478 deletions
diff --git a/src/org/apache/commons/logging/impl/WeakHashtable.java b/src/org/apache/commons/logging/impl/WeakHashtable.java deleted file mode 100644 index e4749b6..0000000 --- a/src/org/apache/commons/logging/impl/WeakHashtable.java +++ /dev/null @@ -1,478 +0,0 @@ -/* - * Copyright 2004 The Apache Software Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package org.apache.commons.logging.impl; - -import java.lang.ref.ReferenceQueue; -import java.lang.ref.WeakReference; -import java.util.*; - -/** - * <p>Implementation of <code>Hashtable</code> that uses <code>WeakReference</code>'s - * to hold its keys thus allowing them to be reclaimed by the garbage collector. - * The associated values are retained using strong references.</p> - * - * <p>This class follows the symantics of <code>Hashtable</code> as closely as - * possible. It therefore does not accept null values or keys.</p> - * - * <p><strong>Note:</strong> - * This is <em>not</em> intended to be a general purpose hash table replacement. - * This implementation is also tuned towards a particular purpose: for use as a replacement - * for <code>Hashtable</code> in <code>LogFactory</code>. This application requires - * good liveliness for <code>get</code> and <code>put</code>. Various tradeoffs - * have been made with this in mind. - * </p> - * <p> - * <strong>Usage:</strong> typical use case is as a drop-in replacement - * for the <code>Hashtable</code> used in <code>LogFactory</code> for J2EE enviroments - * running 1.3+ JVMs. Use of this class <i>in most cases</i> (see below) will - * allow classloaders to be collected by the garbage collector without the need - * to call {@link org.apache.commons.logging.LogFactory#release(ClassLoader) LogFactory.release(ClassLoader)}. - * </p> - * - * <p><code>org.apache.commons.logging.LogFactory</code> checks whether this class - * can be supported by the current JVM, and if so then uses it to store - * references to the <code>LogFactory</code> implementationd it loads - * (rather than using a standard Hashtable instance). - * Having this class used instead of <code>Hashtable</code> solves - * certain issues related to dynamic reloading of applications in J2EE-style - * environments. However this class requires java 1.3 or later (due to its use - * of <code>java.lang.ref.WeakReference</code> and associates). - * And by the way, this extends <code>Hashtable</code> rather than <code>HashMap</code> - * for backwards compatibility reasons. See the documentation - * for method <code>LogFactory.createFactoryStore</code> for more details.</p> - * - * <p>The reason all this is necessary is due to a issue which - * arises during hot deploy in a J2EE-like containers. - * Each component running in the container owns one or more classloaders; when - * the component loads a LogFactory instance via the component classloader - * a reference to it gets stored in the static LogFactory.factories member, - * keyed by the component's classloader so different components don't - * stomp on each other. When the component is later unloaded, the container - * sets the component's classloader to null with the intent that all the - * component's classes get garbage-collected. However there's still a - * reference to the component's classloader from a key in the "global" - * <code>LogFactory</code>'s factories member! If <code>LogFactory.release()</code> - * is called whenever component is unloaded, the classloaders will be correctly - * garbage collected; this <i>should</i> be done by any container that - * bundles commons-logging by default. However, holding the classloader - * references weakly ensures that the classloader will be garbage collected - * without the container performing this step. </p> - * - * <p> - * <strong>Limitations:</strong> - * There is still one (unusual) scenario in which a component will not - * be correctly unloaded without an explicit release. Though weak references - * are used for its keys, it is necessary to use strong references for its values. - * </p> - * - * <p> If the abstract class <code>LogFactory</code> is - * loaded by the container classloader but a subclass of - * <code>LogFactory</code> [LogFactory1] is loaded by the component's - * classloader and an instance stored in the static map associated with the - * base LogFactory class, then there is a strong reference from the LogFactory - * class to the LogFactory1 instance (as normal) and a strong reference from - * the LogFactory1 instance to the component classloader via - * <code>getClass().getClassLoader()</code>. This chain of references will prevent - * collection of the child classloader.</p> - * - * <p> - * Such a situation occurs when the commons-logging.jar is - * loaded by a parent classloader (e.g. a server level classloader in a - * servlet container) and a custom <code>LogFactory</code> implementation is - * loaded by a child classloader (e.g. a web app classloader).</p> - * - * <p>To avoid this scenario, ensure - * that any custom LogFactory subclass is loaded by the same classloader as - * the base <code>LogFactory</code>. Creating custom LogFactory subclasses is, - * however, rare. The standard LogFactoryImpl class should be sufficient - * for most or all users.</p> - * - * - * @author Brian Stansberry - * - * @since 1.1 - */ -public final class WeakHashtable extends Hashtable { - - /** - * The maximum number of times put() or remove() can be called before - * the map will be purged of all cleared entries. - */ - private static final int MAX_CHANGES_BEFORE_PURGE = 100; - - /** - * The maximum number of times put() or remove() can be called before - * the map will be purged of one cleared entry. - */ - private static final int PARTIAL_PURGE_COUNT = 10; - - /* ReferenceQueue we check for gc'd keys */ - private ReferenceQueue queue = new ReferenceQueue(); - /* Counter used to control how often we purge gc'd entries */ - private int changeCount = 0; - - /** - * Constructs a WeakHashtable with the Hashtable default - * capacity and load factor. - */ - public WeakHashtable() {} - - - /** - *@see Hashtable - */ - public boolean containsKey(Object key) { - // purge should not be required - Referenced referenced = new Referenced(key); - return super.containsKey(referenced); - } - - /** - *@see Hashtable - */ - public Enumeration elements() { - purge(); - return super.elements(); - } - - /** - *@see Hashtable - */ - public Set entrySet() { - purge(); - Set referencedEntries = super.entrySet(); - Set unreferencedEntries = new HashSet(); - for (Iterator it=referencedEntries.iterator(); it.hasNext();) { - Map.Entry entry = (Map.Entry) it.next(); - Referenced referencedKey = (Referenced) entry.getKey(); - Object key = referencedKey.getValue(); - Object value = entry.getValue(); - if (key != null) { - Entry dereferencedEntry = new Entry(key, value); - unreferencedEntries.add(dereferencedEntry); - } - } - return unreferencedEntries; - } - - /** - *@see Hashtable - */ - public Object get(Object key) { - // for performance reasons, no purge - Referenced referenceKey = new Referenced(key); - return super.get(referenceKey); - } - - /** - *@see Hashtable - */ - public Enumeration keys() { - purge(); - final Enumeration enumer = super.keys(); - return new Enumeration() { - public boolean hasMoreElements() { - return enumer.hasMoreElements(); - } - public Object nextElement() { - Referenced nextReference = (Referenced) enumer.nextElement(); - return nextReference.getValue(); - } - }; - } - - - /** - *@see Hashtable - */ - public Set keySet() { - purge(); - Set referencedKeys = super.keySet(); - Set unreferencedKeys = new HashSet(); - for (Iterator it=referencedKeys.iterator(); it.hasNext();) { - Referenced referenceKey = (Referenced) it.next(); - Object keyValue = referenceKey.getValue(); - if (keyValue != null) { - unreferencedKeys.add(keyValue); - } - } - return unreferencedKeys; - } - - /** - *@see Hashtable - */ - public Object put(Object key, Object value) { - // check for nulls, ensuring symantics match superclass - if (key == null) { - throw new NullPointerException("Null keys are not allowed"); - } - if (value == null) { - throw new NullPointerException("Null values are not allowed"); - } - - // for performance reasons, only purge every - // MAX_CHANGES_BEFORE_PURGE times - if (changeCount++ > MAX_CHANGES_BEFORE_PURGE) { - purge(); - changeCount = 0; - } - // do a partial purge more often - else if ((changeCount % PARTIAL_PURGE_COUNT) == 0) { - purgeOne(); - } - - Object result = null; - Referenced keyRef = new Referenced(key, queue); - return super.put(keyRef, value); - } - - /** - *@see Hashtable - */ - public void putAll(Map t) { - if (t != null) { - Set entrySet = t.entrySet(); - for (Iterator it=entrySet.iterator(); it.hasNext();) { - Map.Entry entry = (Map.Entry) it.next(); - put(entry.getKey(), entry.getValue()); - } - } - } - - /** - *@see Hashtable - */ - public Collection values() { - purge(); - return super.values(); - } - - /** - *@see Hashtable - */ - public Object remove(Object key) { - // for performance reasons, only purge every - // MAX_CHANGES_BEFORE_PURGE times - if (changeCount++ > MAX_CHANGES_BEFORE_PURGE) { - purge(); - changeCount = 0; - } - // do a partial purge more often - else if ((changeCount % PARTIAL_PURGE_COUNT) == 0) { - purgeOne(); - } - return super.remove(new Referenced(key)); - } - - /** - *@see Hashtable - */ - public boolean isEmpty() { - purge(); - return super.isEmpty(); - } - - /** - *@see Hashtable - */ - public int size() { - purge(); - return super.size(); - } - - /** - *@see Hashtable - */ - public String toString() { - purge(); - return super.toString(); - } - - /** - * @see Hashtable - */ - protected void rehash() { - // purge here to save the effort of rehashing dead entries - purge(); - super.rehash(); - } - - /** - * Purges all entries whose wrapped keys - * have been garbage collected. - */ - private void purge() { - synchronized (queue) { - WeakKey key; - while ((key = (WeakKey) queue.poll()) != null) { - super.remove(key.getReferenced()); - } - } - } - - /** - * Purges one entry whose wrapped key - * has been garbage collected. - */ - private void purgeOne() { - - synchronized (queue) { - WeakKey key = (WeakKey) queue.poll(); - if (key != null) { - super.remove(key.getReferenced()); - } - } - } - - /** Entry implementation */ - private final static class Entry implements Map.Entry { - - private final Object key; - private final Object value; - - private Entry(Object key, Object value) { - this.key = key; - this.value = value; - } - - public boolean equals(Object o) { - boolean result = false; - if (o != null && o instanceof Map.Entry) { - Map.Entry entry = (Map.Entry) o; - result = (getKey()==null ? - entry.getKey() == null : - getKey().equals(entry.getKey())) - && - (getValue()==null ? - entry.getValue() == null : - getValue().equals(entry.getValue())); - } - return result; - } - - public int hashCode() { - - return (getKey()==null ? 0 : getKey().hashCode()) ^ - (getValue()==null ? 0 : getValue().hashCode()); - } - - public Object setValue(Object value) { - throw new UnsupportedOperationException("Entry.setValue is not supported."); - } - - public Object getValue() { - return value; - } - - public Object getKey() { - return key; - } - } - - - /** Wrapper giving correct symantics for equals and hashcode */ - private final static class Referenced { - - private final WeakReference reference; - private final int hashCode; - - /** - * - * @throws NullPointerException if referant is <code>null</code> - */ - private Referenced(Object referant) { - reference = new WeakReference(referant); - // Calc a permanent hashCode so calls to Hashtable.remove() - // work if the WeakReference has been cleared - hashCode = referant.hashCode(); - } - - /** - * - * @throws NullPointerException if key is <code>null</code> - */ - private Referenced(Object key, ReferenceQueue queue) { - reference = new WeakKey(key, queue, this); - // Calc a permanent hashCode so calls to Hashtable.remove() - // work if the WeakReference has been cleared - hashCode = key.hashCode(); - - } - - public int hashCode() { - return hashCode; - } - - private Object getValue() { - return reference.get(); - } - - public boolean equals(Object o) { - boolean result = false; - if (o instanceof Referenced) { - Referenced otherKey = (Referenced) o; - Object thisKeyValue = getValue(); - Object otherKeyValue = otherKey.getValue(); - if (thisKeyValue == null) { - result = (otherKeyValue == null); - - // Since our hashcode was calculated from the original - // non-null referant, the above check breaks the - // hashcode/equals contract, as two cleared Referenced - // objects could test equal but have different hashcodes. - // We can reduce (not eliminate) the chance of this - // happening by comparing hashcodes. - if (result == true) { - result = (this.hashCode() == otherKey.hashCode()); - } - // In any case, as our c'tor does not allow null referants - // and Hashtable does not do equality checks between - // existing keys, normal hashtable operations should never - // result in an equals comparison between null referants - } - else - { - result = thisKeyValue.equals(otherKeyValue); - } - } - return result; - } - } - - /** - * WeakReference subclass that holds a hard reference to an - * associated <code>value</code> and also makes accessible - * the Referenced object holding it. - */ - private final static class WeakKey extends WeakReference { - - private final Referenced referenced; - - private WeakKey(Object key, - ReferenceQueue queue, - Referenced referenced) { - super(key, queue); - this.referenced = referenced; - } - - private Referenced getReferenced() { - return referenced; - } - } -} |