1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
|
/* Glazed Lists (c) 2003-2006 */
/* http://publicobject.com/glazedlists/ publicobject.com,*/
/* O'Dell Engineering Ltd.*/
package ca.odell.glazedlists.impl;
// the core Glazed Lists package
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.event.ListEvent;
import ca.odell.glazedlists.event.ListEventListener;
import java.lang.ref.WeakReference;
/**
* This class is a proxy to another ListEventListener that may go out of scope
* without explicitly removing itself from the source list's set of listeners.
*
* <p>WeakReferenceProxy exists to solve a garbage collection problem. Suppose
* there exists an EventList <i>L</i> and with an iterator <i>I</i>. <i>I</i>
* must listen to <i>L</i> for change events in order to remain consistent.
* Therefore <i>I</i> will register itself as a listener to <i>L</i>. When
* <i>I</i> goes out of scope (as they typically do), it will remain registered
* as a listener of <i>L</i>. This prevents <i>I</i> from ever being garbage
* collected! But <i>I</i> can never used again. Because iterators are expected
* to be used very frequently, this will cause an unacceptable memory leak.
*
* <p>This problem is solved by WeakReferenceProxy. Instead of adding <i>I</i>
* as a direct listener of <i>L</i>, add a proxy instead. The proxy will retain
* a <code>WeakReference</code> to <i>I</i> and forward events to <i>I</i> as
* long as it is reachable. When <i>I</i> is no longer reachable, the proxy
* will remove itself from the list of listeners for <i>L</i> and all garbage
* is available for collection.
*
* <p>Specifically, the proxy stops listening to <i>L</i> the
* <strong>next</strong> time any of the following occurs:
*
* <ul>
* <li> another ListEventListener is registered with the same EventList
* <li> another ListEventListener is deregistered with the same EventList
* <li> another ListEvent is broadcast for the same EventList
* </ul>
*
* @see java.lang.ref.WeakReference
* @see <a href="https://glazedlists.dev.java.net/issues/show_bug.cgi?id=21">Bug 21</a>
*
* @author <a href="mailto:jesse@swank.ca">Jesse Wilson</a>
* @author James Lemieux
*/
public final class WeakReferenceProxy<E> implements ListEventListener<E> {
/** a weak reference the target ListEventListener */
private final WeakReference<ListEventListener<E>> proxyTargetReference;
/** the list to remove this listener from when done */
private EventList<E> source;
/**
* Creates a new WeakReferenceProxy that listens for events from the
* specified list and forwards them to the specified listener.
*/
public WeakReferenceProxy(EventList<E> source, ListEventListener<E> proxyTarget) {
if (source == null)
throw new IllegalArgumentException("source may not be null");
if (proxyTarget == null)
throw new IllegalArgumentException("proxyTarget may not be null");
this.source = source;
this.proxyTargetReference = new WeakReference<ListEventListener<E>>(proxyTarget);
}
/**
* Accepts notification for the changes and forwards them to the proxy
* target if it has not yet been garbage collected.
*/
public void listChanged(ListEvent<E> listChanges) {
// if this listener has already been cleaned up, ignore ListEvents
if (source == null) return;
// fetch the underlying ListEventListener
final ListEventListener<E> proxyTarget = getReferent();
// test to see if the underlying ListEventListener still exists
if (proxyTarget == null) {
// it doesn't so clean it up
source.removeListEventListener(this);
dispose();
} else {
// it does, so notify it of the ListEvent
proxyTarget.listChanged(listChanges);
}
}
/**
* Returns the underlying ListEventListener or <code>null</code> if it has
* been garbage collected.
*/
public ListEventListener<E> getReferent() {
return proxyTargetReference.get();
}
/**
* A callback to notify this WeakReferenceProxy that it has been
* unregistered from the EventList to which it was listening. The
* WeakReferenceProxy responds by cleaning up internal references to the
* EventList and ensuring that any future ListEvents it receives are
* ignored.
*/
public void dispose() {
source = null;
}
}
|