rdonkin 2004/11/11 14:31:05 Modified: logging/optional/src/java/org/apache/commons/logging/impl WeakHashtable.java logging/optional/src/test/org/apache/commons/logging/impl WeakHashtableTest.java Log: Added code to purge entries who references have been collected. Test contributed by Brian Stansberry. Revision Changes Path 1.3 +63 -0 jakarta-commons/logging/optional/src/java/org/apache/commons/logging/impl/WeakHashtable.java Index: WeakHashtable.java =================================================================== RCS file: /home/cvs/jakarta-commons/logging/optional/src/java/org/apache/commons/logging/impl/WeakHashtable.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- WeakHashtable.java 11 Nov 2004 21:43:43 -0000 1.2 +++ WeakHashtable.java 11 Nov 2004 22:31:05 -0000 1.3 @@ -25,6 +25,11 @@ * to hold it's keys thus allowing them to be reclaimed by the garbage collector. * This class follows the symantics of <code>Hashtable</code> as closely as possible. * It therefore does not accept null values or keys. + * <p> + * 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 @@ -35,6 +40,8 @@ */ public final class WeakHashtable extends Hashtable { + /** Empty array of <code>Entry</code>'s */ + private static final Entry[] EMPTY_ENTRY_ARRAY = {}; public WeakHashtable() {} @@ -42,6 +49,7 @@ [EMAIL PROTECTED] Hashtable */ public boolean contains(Object value) { + // purge should not be required if (value instanceof Referenced) { return super.contains(value); } @@ -53,6 +61,7 @@ [EMAIL PROTECTED] Hashtable */ public boolean containsKey(Object key) { + // purge should not be required Referenced referenced = new Referenced(key); return super.containsKey(referenced); } @@ -61,6 +70,7 @@ [EMAIL PROTECTED] Hashtable */ public boolean containsValue(Object value) { + // purge should not be required if (value instanceof Referenced) { return super.contains(value); } @@ -72,6 +82,7 @@ [EMAIL PROTECTED] Hashtable */ public Enumeration elements() { + purge(); final Enumeration enum = super.elements(); return new Enumeration() { public boolean hasMoreElements() { @@ -88,6 +99,7 @@ [EMAIL PROTECTED] Hashtable */ public Set entrySet() { + purge(); Set referencedEntries = super.entrySet(); Set unreferencedEntries = new HashSet(); for (Iterator it=referencedEntries.iterator(); it.hasNext();) { @@ -108,6 +120,7 @@ [EMAIL PROTECTED] Hashtable */ public Object get(Object key) { + // for performance reasons, no purge Object result = null; Referenced referenceKey = new Referenced(key); Referenced referencedValue = (Referenced) super.get(referenceKey); @@ -121,6 +134,7 @@ [EMAIL PROTECTED] Hashtable */ public Enumeration keys() { + purge(); final Enumeration enum = super.keys(); return new Enumeration() { public boolean hasMoreElements() { @@ -138,6 +152,7 @@ [EMAIL PROTECTED] Hashtable */ public Set keySet() { + purge(); Set referencedKeys = super.keySet(); Set unreferencedKeys = new HashSet(); for (Iterator it=referencedKeys.iterator(); it.hasNext();) { @@ -154,6 +169,7 @@ [EMAIL PROTECTED] Hashtable */ public Object put(Object key, Object value) { + // for performance reasons, no purge // check for nulls, ensuring symantics match superclass if (key == null) { throw new NullPointerException("Null keys are not allowed"); @@ -187,6 +203,7 @@ [EMAIL PROTECTED] Hashtable */ public Collection values() { + purge(); Collection referencedValues = super.values(); ArrayList unreferencedValues = new ArrayList(); for (Iterator it = referencedValues.iterator(); it.hasNext();) { @@ -206,6 +223,52 @@ return super.remove(new Referenced(key)); } + /** + [EMAIL PROTECTED] Hashtable + */ + public boolean isEmpty() { + purge(); + return super.isEmpty(); + } + + /** + [EMAIL PROTECTED] Hashtable + */ + public int size() { + purge(); + return super.size(); + } + + /** + [EMAIL PROTECTED] Hashtable + */ + public String toString() { + purge(); + return super.toString(); + } + + /** + * Purges all entries whose wrapped keys or values + * have been garbage collected. + */ + private synchronized void purge() { + Set entrySet = super.entrySet(); + for (Iterator it=entrySet.iterator(); it.hasNext();) { + Map.Entry entry = (Map.Entry) it.next(); + Referenced referencedKey = (Referenced) entry.getKey(); + Referenced referencedValue = (Referenced) entry.getValue(); + + // test whether either referant has been collected + if (referencedKey.getValue() == null || referencedValue.getValue() == null) { + // if so, purge this entry + it.remove(); + } + } + } + + + + /** Entry implementation */ private final static class Entry implements Map.Entry { private Object key; 1.3 +3 -0 jakarta-commons/logging/optional/src/test/org/apache/commons/logging/impl/WeakHashtableTest.java Index: WeakHashtableTest.java =================================================================== RCS file: /home/cvs/jakarta-commons/logging/optional/src/test/org/apache/commons/logging/impl/WeakHashtableTest.java,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- WeakHashtableTest.java 11 Nov 2004 21:43:43 -0000 1.2 +++ WeakHashtableTest.java 11 Nov 2004 22:31:05 -0000 1.3 @@ -232,5 +232,8 @@ bytz = bytz * 2; } } + + // Test that the released objects are not taking space in the table + assertEquals("underlying table not emptied", 0, weakHashtable.size()); } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]