WeakHashtable.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.logging.impl;

  18. import java.lang.ref.ReferenceQueue;
  19. import java.lang.ref.WeakReference;
  20. import java.util.ArrayList;
  21. import java.util.Collection;
  22. import java.util.Enumeration;
  23. import java.util.HashSet;
  24. import java.util.Hashtable;
  25. import java.util.List;
  26. import java.util.Map;
  27. import java.util.Objects;
  28. import java.util.Set;

  29. /**
  30.  * Implements {@code Hashtable} to use {@code WeakReference}'s
  31.  * to hold its keys thus allowing them to be reclaimed by the garbage collector.
  32.  * The associated values are retained using strong references.
  33.  * <p>
  34.  * This class follows the semantics of {@code Hashtable} as closely as
  35.  * possible. It therefore does not accept null values or keys.
  36.  * </p>
  37.  * <p>
  38.  * <strong>Note:</strong>
  39.  * This is <em>not</em> intended to be a general purpose hash table replacement.
  40.  * This implementation is also tuned towards a particular purpose: for use as a replacement
  41.  * for {@code Hashtable} in {@code LogFactory}. This application requires
  42.  * good liveliness for {@code get} and {@code put}. Various tradeoffs
  43.  * have been made with this in mind.
  44.  * </p>
  45.  * <p>
  46.  * <strong>Usage:</strong> typical use case is as a drop-in replacement
  47.  * for the {@code Hashtable} used in {@code LogFactory} for J2EE environments
  48.  * running 1.3+ JVMs. Use of this class <em>in most cases</em> (see below) will
  49.  * allow class loaders to be collected by the garbage collector without the need
  50.  * to call {@link org.apache.commons.logging.LogFactory#release(ClassLoader) LogFactory.release(ClassLoader)}.
  51.  * </p>
  52.  * <p>
  53.  * {@code org.apache.commons.logging.LogFactory} checks whether this class
  54.  * can be supported by the current JVM, and if so then uses it to store
  55.  * references to the {@code LogFactory} implementation it loads
  56.  * (rather than using a standard Hashtable instance).
  57.  * Having this class used instead of {@code Hashtable} solves
  58.  * certain issues related to dynamic reloading of applications in J2EE-style
  59.  * environments. However this class requires Java 1.3 or later (due to its use
  60.  * of {@link java.lang.ref.WeakReference} and associates).
  61.  * And by the way, this extends {@code Hashtable} rather than {@code HashMap}
  62.  * for backwards compatibility reasons. See the documentation
  63.  * for method {@code LogFactory.createFactoryStore} for more details.
  64.  * </p>
  65.  * <p>
  66.  * The reason all this is necessary is due to a issue which
  67.  * arises during hot deploy in a J2EE-like containers.
  68.  * Each component running in the container owns one or more class loaders; when
  69.  * the component loads a LogFactory instance via the component class loader
  70.  * a reference to it gets stored in the static LogFactory.factories member,
  71.  * keyed by the component's class loader so different components don't
  72.  * stomp on each other. When the component is later unloaded, the container
  73.  * sets the component's class loader to null with the intent that all the
  74.  * component's classes get garbage-collected. However there's still a
  75.  * reference to the component's class loader from a key in the "global"
  76.  * {@code LogFactory}'s factories member! If {@code LogFactory.release()}
  77.  * is called whenever component is unloaded, the class loaders will be correctly
  78.  * garbage collected; this <em>should</em> be done by any container that
  79.  * bundles commons-logging by default. However, holding the class loader
  80.  * references weakly ensures that the class loader will be garbage collected
  81.  * without the container performing this step.
  82.  * </p>
  83.  * <p>
  84.  * <strong>Limitations:</strong>
  85.  * There is still one (unusual) scenario in which a component will not
  86.  * be correctly unloaded without an explicit release. Though weak references
  87.  * are used for its keys, it is necessary to use strong references for its values.
  88.  * </p>
  89.  * <p>
  90.  * If the abstract class {@code LogFactory} is
  91.  * loaded by the container class loader but a subclass of
  92.  * {@code LogFactory} [LogFactory1] is loaded by the component's
  93.  * class loader and an instance stored in the static map associated with the
  94.  * base LogFactory class, then there is a strong reference from the LogFactory
  95.  * class to the LogFactory1 instance (as normal) and a strong reference from
  96.  * the LogFactory1 instance to the component class loader via
  97.  * {@code getClass().getClassLoader()}. This chain of references will prevent
  98.  * collection of the child class loader.
  99.  * </p>
  100.  * <p>
  101.  * Such a situation occurs when the commons-logging.jar is
  102.  * loaded by a parent class loader (e.g. a server level class loader in a
  103.  * servlet container) and a custom {@code LogFactory} implementation is
  104.  * loaded by a child class loader (e.g. a web app class loader).
  105.  * </p>
  106.  * <p>
  107.  * To avoid this scenario, ensure
  108.  * that any custom LogFactory subclass is loaded by the same class loader as
  109.  * the base {@code LogFactory}. Creating custom LogFactory subclasses is,
  110.  * however, rare. The standard LogFactoryImpl class should be sufficient
  111.  * for most or all users.
  112.  * </p>
  113.  *
  114.  * @since 1.1
  115.  * @deprecated No longer used, will be removed in 2.0.
  116.  */
  117. @Deprecated
  118. public final class WeakHashtable extends Hashtable {

  119.     /** Entry implementation */
  120.     private final static class Entry implements Map.Entry {

  121.         private final Object key;
  122.         private final Object value;

  123.         private Entry(final Object key, final Object value) {
  124.             this.key = key;
  125.             this.value = value;
  126.         }

  127.         @Override
  128.         public boolean equals(final Object o) {
  129.             boolean result = false;
  130.             if (o instanceof Map.Entry) {
  131.                 final Map.Entry entry = (Map.Entry) o;
  132.                 result =    (getKey()==null ?
  133.                                             entry.getKey() == null :
  134.                                             getKey().equals(entry.getKey())) &&
  135.                             (getValue()==null ?
  136.                                             entry.getValue() == null :
  137.                                             getValue().equals(entry.getValue()));
  138.             }
  139.             return result;
  140.         }

  141.         @Override
  142.         public Object getKey() {
  143.             return key;
  144.         }

  145.         @Override
  146.         public Object getValue() {
  147.             return value;
  148.         }

  149.         @Override
  150.         public int hashCode() {
  151.             return (getKey()==null ? 0 : getKey().hashCode()) ^
  152.                 (getValue()==null ? 0 : getValue().hashCode());
  153.         }

  154.         @Override
  155.         public Object setValue(final Object value) {
  156.             throw new UnsupportedOperationException("Entry.setValue is not supported.");
  157.         }
  158.     }

  159.     /** Wrapper giving correct symantics for equals and hash code */
  160.     private final static class Referenced {

  161.         private final WeakReference reference;
  162.         private final int           hashCode;

  163.         /**
  164.          *
  165.          * @throws NullPointerException if referant is {@code null}
  166.          */
  167.         private Referenced(final Object referant) {
  168.             reference = new WeakReference(referant);
  169.             // Calc a permanent hashCode so calls to Hashtable.remove()
  170.             // work if the WeakReference has been cleared
  171.             hashCode  = referant.hashCode();
  172.         }

  173.         /**
  174.          *
  175.          * @throws NullPointerException if key is {@code null}
  176.          */
  177.         private Referenced(final Object key, final ReferenceQueue queue) {
  178.             reference = new WeakKey(key, queue, this);
  179.             // Calc a permanent hashCode so calls to Hashtable.remove()
  180.             // work if the WeakReference has been cleared
  181.             hashCode  = key.hashCode();

  182.         }

  183.         @Override
  184.         public boolean equals(final Object o) {
  185.             boolean result = false;
  186.             if (o instanceof Referenced) {
  187.                 final Referenced otherKey = (Referenced) o;
  188.                 final Object thisKeyValue = getValue();
  189.                 final Object otherKeyValue = otherKey.getValue();
  190.                 if (thisKeyValue == null) {
  191.                     result = otherKeyValue == null;

  192.                     // Since our hash code was calculated from the original
  193.                     // non-null referant, the above check breaks the
  194.                     // hash code/equals contract, as two cleared Referenced
  195.                     // objects could test equal but have different hash codes.
  196.                     // We can reduce (not eliminate) the chance of this
  197.                     // happening by comparing hash codes.
  198.                     result = result && hashCode() == otherKey.hashCode();
  199.                     // In any case, as our constructor does not allow null referants
  200.                     // and Hashtable does not do equality checks between
  201.                     // existing keys, normal hash table operations should never
  202.                     // result in an equals comparison between null referants
  203.                 }
  204.                 else
  205.                 {
  206.                     result = thisKeyValue.equals(otherKeyValue);
  207.                 }
  208.             }
  209.             return result;
  210.         }

  211.         private Object getValue() {
  212.             return reference.get();
  213.         }

  214.         @Override
  215.         public int hashCode() {
  216.             return hashCode;
  217.         }
  218.     }

  219.     /**
  220.      * WeakReference subclass that holds a hard reference to an
  221.      * associated {@code value} and also makes accessible
  222.      * the Referenced object holding it.
  223.      */
  224.     private final static class WeakKey extends WeakReference {

  225.         private final Referenced referenced;

  226.         private WeakKey(final Object key,
  227.                         final ReferenceQueue queue,
  228.                         final Referenced referenced) {
  229.             super(key, queue);
  230.             this.referenced = referenced;
  231.         }

  232.         private Referenced getReferenced() {
  233.             return referenced;
  234.         }
  235.      }

  236.     /** Serializable version identifier. */
  237.     private static final long serialVersionUID = -1546036869799732453L;

  238.     /**
  239.      * The maximum number of times put() or remove() can be called before
  240.      * the map will be purged of all cleared entries.
  241.      */
  242.     private static final int MAX_CHANGES_BEFORE_PURGE = 100;

  243.     /**
  244.      * The maximum number of times put() or remove() can be called before
  245.      * the map will be purged of one cleared entry.
  246.      */
  247.     private static final int PARTIAL_PURGE_COUNT     = 10;

  248.     /** ReferenceQueue we check for GC'd keys. */
  249.     private final transient ReferenceQueue queue = new ReferenceQueue();

  250.     /** Counter used to control how often we purge gc'd entries. */
  251.     private int changeCount;

  252.     /**
  253.      * Constructs a WeakHashtable with the Hashtable default
  254.      * capacity and load factor.
  255.      */
  256.     public WeakHashtable() {}

  257.     /**
  258.      *@see Hashtable
  259.      */
  260.     @Override
  261.     public boolean containsKey(final Object key) {
  262.         // purge should not be required
  263.         final Referenced referenced = new Referenced(key);
  264.         return super.containsKey(referenced);
  265.     }

  266.     /**
  267.      *@see Hashtable
  268.      */
  269.     @Override
  270.     public Enumeration elements() {
  271.         purge();
  272.         return super.elements();
  273.     }

  274.     /**
  275.      *@see Hashtable
  276.      */
  277.     @Override
  278.     public Set entrySet() {
  279.         purge();
  280.         final Set referencedEntries = super.entrySet();
  281.         final Set unreferencedEntries = new HashSet();
  282.         for (final Object referencedEntry : referencedEntries) {
  283.             final Map.Entry entry = (Map.Entry) referencedEntry;
  284.             final Referenced referencedKey = (Referenced) entry.getKey();
  285.             final Object key = referencedKey.getValue();
  286.             final Object value = entry.getValue();
  287.             if (key != null) {
  288.                 final Entry dereferencedEntry = new Entry(key, value);
  289.                 unreferencedEntries.add(dereferencedEntry);
  290.             }
  291.         }
  292.         return unreferencedEntries;
  293.     }

  294.     /**
  295.      *@see Hashtable
  296.      */
  297.     @Override
  298.     public Object get(final Object key) {
  299.         // for performance reasons, no purge
  300.         final Referenced referenceKey = new Referenced(key);
  301.         return super.get(referenceKey);
  302.     }

  303.     /**
  304.      *@see Hashtable
  305.      */
  306.     @Override
  307.     public boolean isEmpty() {
  308.         purge();
  309.         return super.isEmpty();
  310.     }

  311.     /**
  312.      *@see Hashtable
  313.      */
  314.     @Override
  315.     public Enumeration keys() {
  316.         purge();
  317.         final Enumeration enumer = super.keys();
  318.         return new Enumeration() {
  319.             @Override
  320.             public boolean hasMoreElements() {
  321.                 return enumer.hasMoreElements();
  322.             }
  323.             @Override
  324.             public Object nextElement() {
  325.                  final Referenced nextReference = (Referenced) enumer.nextElement();
  326.                  return nextReference.getValue();
  327.             }
  328.         };
  329.     }

  330.     /**
  331.      *@see Hashtable
  332.      */
  333.     @Override
  334.     public Set keySet() {
  335.         purge();
  336.         final Set referencedKeys = super.keySet();
  337.         final Set unreferencedKeys = new HashSet();
  338.         for (final Object referencedKey : referencedKeys) {
  339.             final Referenced referenceKey = (Referenced) referencedKey;
  340.             final Object keyValue = referenceKey.getValue();
  341.             if (keyValue != null) {
  342.                 unreferencedKeys.add(keyValue);
  343.             }
  344.         }
  345.         return unreferencedKeys;
  346.     }

  347.     /**
  348.      * Purges all entries whose wrapped keys
  349.      * have been garbage collected.
  350.      */
  351.     private void purge() {
  352.         final List toRemove = new ArrayList();
  353.         synchronized (queue) {
  354.             WeakKey key;
  355.             while ((key = (WeakKey) queue.poll()) != null) {
  356.                 toRemove.add(key.getReferenced());
  357.             }
  358.         }

  359.         // LOGGING-119: do the actual removal of the keys outside the sync block
  360.         // to prevent deadlock scenarios as purge() may be called from
  361.         // non-synchronized methods too
  362.         final int size = toRemove.size();
  363.         for (int i = 0; i < size; i++) {
  364.             super.remove(toRemove.get(i));
  365.         }
  366.     }

  367.     /**
  368.      * Purges one entry whose wrapped key
  369.      * has been garbage collected.
  370.      */
  371.     private void purgeOne() {
  372.         synchronized (queue) {
  373.             final WeakKey key = (WeakKey) queue.poll();
  374.             if (key != null) {
  375.                 super.remove(key.getReferenced());
  376.             }
  377.         }
  378.     }

  379.     /**
  380.      *@see Hashtable
  381.      */
  382.     @Override
  383.     public synchronized Object put(final Object key, final Object value) {
  384.         // check for nulls, ensuring semantics match superclass
  385.         Objects.requireNonNull(key, "key");
  386.         Objects.requireNonNull(value, "value");

  387.         // for performance reasons, only purge every
  388.         // MAX_CHANGES_BEFORE_PURGE times
  389.         if (changeCount++ > MAX_CHANGES_BEFORE_PURGE) {
  390.             purge();
  391.             changeCount = 0;
  392.         }
  393.         // do a partial purge more often
  394.         else if (changeCount % PARTIAL_PURGE_COUNT == 0) {
  395.             purgeOne();
  396.         }

  397.         final Referenced keyRef = new Referenced(key, queue);
  398.         return super.put(keyRef, value);
  399.     }

  400.     /**
  401.      *@see Hashtable
  402.      */
  403.     @Override
  404.     public void putAll(final Map t) {
  405.         if (t != null) {
  406.             final Set entrySet = t.entrySet();
  407.             for (final Object element : entrySet) {
  408.                 final Map.Entry entry = (Map.Entry) element;
  409.                 put(entry.getKey(), entry.getValue());
  410.             }
  411.         }
  412.     }

  413.     /**
  414.      * @see Hashtable
  415.      */
  416.     @Override
  417.     protected void rehash() {
  418.         // purge here to save the effort of rehashing dead entries
  419.         purge();
  420.         super.rehash();
  421.     }

  422.     /**
  423.      *@see Hashtable
  424.      */
  425.     @Override
  426.     public synchronized Object remove(final Object key) {
  427.         // for performance reasons, only purge every
  428.         // MAX_CHANGES_BEFORE_PURGE times
  429.         if (changeCount++ > MAX_CHANGES_BEFORE_PURGE) {
  430.             purge();
  431.             changeCount = 0;
  432.         }
  433.         // do a partial purge more often
  434.         else if (changeCount % PARTIAL_PURGE_COUNT == 0) {
  435.             purgeOne();
  436.         }
  437.         return super.remove(new Referenced(key));
  438.     }

  439.     /**
  440.      *@see Hashtable
  441.      */
  442.     @Override
  443.     public int size() {
  444.         purge();
  445.         return super.size();
  446.     }

  447.     /**
  448.      *@see Hashtable
  449.      */
  450.     @Override
  451.     public String toString() {
  452.         purge();
  453.         return super.toString();
  454.     }

  455.     /**
  456.      *@see Hashtable
  457.      */
  458.     @Override
  459.     public Collection values() {
  460.         purge();
  461.         return super.values();
  462.     }
  463. }