alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.openide.util;

import java.awt.event.FocusListener;
import java.beans.PropertyChangeListener;
import java.beans.VetoableChangeListener;
import java.util.EventListener;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentListener;


/** A generic weak listener factory.
 * Creates a weak implementation of a listener of type lType.
 *
 * In the following examples, I'll use following naming:
* There are four objects involved in weak listener usage:
    *
  • The event source object *
  • The observer - object that wants to listen on source *
  • The listener - the implementation of the corresponding * *Listener interface, sometimes the observer itself but * often some observer's inner class delegating the events to the observer. *
  • The weak listener implementation. *
* The examples are written for ChangeListener. The Utilities * have factory methods for the most common listeners used in NetBeans * and also one universal factory method you can use for other listeners. * *

How to use it:

* Here is an example how to write a listener/observer and make it listen * on some source: *
 *  public class ListenerObserver implements ChangeListener {
 *      private void registerTo(Source source) {
 *          source.addChangeListener({@link
                #change(javax.swing.event.ChangeListener, java.lang.Object)
 *              WeakListeners.changeListener} (this, source));
 *      }
 *      
 *      public void stateChanged(ChangeEvent e) {
 *          doSomething();
 *      }
 *  }
 * 
* You can also factor out the listener implementation to some other class * if you don't want to expose the stateChanged method (better technique): *
 *  public class Observer {
 *      private Listener listener;
 *
 *      private void registerTo(Source source) {
 *          listener = new Listener();
 *          source.addChangeListener({@link
                #change(javax.swing.event.ChangeListener, java.lang.Object)
 *              WeakListeners.change} (listener, source));
 *      }
 *      
 *      private class Listener implements ChangeListener {
 *          public void stateChanged(ChangeEvent e) {
 *              doSomething();
 *          }
 *      }
 *  }
 * 
* Note: The observer keeps the reference to the listener, it won't work * otherwise, see below. * *

You can also use the universal factory for other listeners: *

 *  public class Observer implements SomeListener {
 *      private void registerTo(Source source) {
 *          source.addSomeListener((SomeListener){@link
 *              #create(java.lang.Class, java.util.EventListener, java.lang.Object)
 *              WeakListeners.create} (
 *                  SomeListener.class, this, source));
 *      }
 *      
 *      public void someEventHappened(SomeEvent e) {
 *          doSomething();
 *      }
 *  }
 * 
* *

How to not use it:

* Here are examples of a common mistakes done when using weak listener: *
 *  public class Observer {
 *      private void registerTo(Source source) {
 *          source.addChangeListener(WeakListeners.change(new Listener(), source));
 *      }
 *      
 *      private class Listener implements ChangeListener {
 *          public void stateChanged(ChangeEvent e) {
 *              doSomething();
 *          }
 *      }
 *  }
 * 
* Mistake: There is nobody holding strong reference to the Listener instance, * so it may be freed on the next GC cycle. * *
 *  public class ListenerObserver implements ChangeListener {
 *      private void registerTo(Source source) {
 *          source.addChangeListener(WeakListeners.change(this, null));
 *      }
 *      
 *      public void stateChanged(ChangeEvent e) {
 *          doSomething();
 *      }
 *  }
 * 
* Mistake: The weak listener is unable to unregister itself from the source * once the listener is freed. For explanation, read below. *

How does it work:

*

The weak listener is used as a reference-weakening wrapper * around the listener. It is itself strongly referenced from the implementation * of the source (e.g. from its EventListenerList) but it references * the listener only through WeakReference. It also weak-references * the source. Listener, on the other hand, usually strongly references * the observer (typically through the outer class reference). * * This means that:

    *
  1. If the listener is not strong-referenced from elsewhere, it can be * thrown away on the next GC cycle. This is why you can't use * WeakListeners.change(new MyListener(), ..) as the only reference * to the listener will be the weak one from the weak listener. *
  2. If the listener-observer pair is not strong-referenced from elsewhere * it can be thrown away on the next GC cycle. This is what the * weak listener was invented for. *
  3. If the source is not strong-referenced from anywhere, it can be * thrown away on the next GC cycle taking the weak listener with it, * but not the listener and the observer if they are still strong-referenced * (unusual case, but possible). *
* *

Now what happens when the listener/observer is removed from memory:

    *
  • The weak listener is notified that the reference to the listener was cleared. *
  • It tries to unregister itself from the source. This is why it needs * the reference to the source for the registration. The unregistration * is done using reflection, usually looking up the method * remove<listenerType> of the source and calling it. *
* *

This may fail if the source don't have the expected remove* * method and/or if you provide wrong reference to source. In that case * the weak listener instance will stay in memory and registered by the source, * while the listener and observer will be freed. * *

There is still one fallback method - if some event come to a weak listener * and the listener is already freed, the weak listener tries to unregister * itself from the object the event came from. * * @since 4.10 */ public final class WeakListeners { /** No instances. */ private WeakListeners () { } /** Generic factory method to create weak listener for any listener * interface. * * @param lType the type of listener to create. It can be any interface, * but only interfaces are allowed. * @param l the listener to delegate to, l must be an instance * of lType * @param source the source that the listener should detach from when * listener l is freed, can be null * @return an instance of lType delegating all the interface * calls to l. */ public static EventListener create (Class lType, EventListener l, Object source) { if (!lType.isInterface()) { throw new IllegalArgumentException ("Not interface: " + lType); } return WeakListenerImpl.create (lType, lType, l, source); } /** The most generic factory method to create weak listener for any listener * interface that moreover behaves like a listener of another type. * This can be useful to correctly remove listeners from a source when * hierarchies of listeners are used. *

* For example {@link javax.naming.event.EventContext} allows to add an * instance of {@link javax.naming.event.ObjectChangeListener} but using * method addNamingListener. Method removeNamingListener * is then used to remove it. To help the weak listener support to correctly * find the right method one have to use: *

     * ObjectChangeListener l = (ObjectChangeListener)WeakListeners.create (
     *   ObjectChangeListener.class, // the actual class of the returned listener
     *   NamingListener.class, // but it always will be used as NamingListener
     *   yourObjectListener,
     *   someContext
     * ); 
     * someContext.addNamingListener ("", 0, l);
     * 
* This will correctly create ObjectChangeListener * and unregister it by * calling removeNamingListener. * * @param lType the type the listener shall implement. It can be any interface, * but only interfaces are allowed. * @param apiType the interface the returned object will be used as. It * shall be equal to lType or its superinterface * @param l the listener to delegate to, l must be an instance * of lType * @param source the source that the listener should detach from when * listener l is freed, can be null * @return an instance of lType delegating all the interface * calls to l. * @since 4.12 */ public static EventListener create (Class lType, Class apiType, EventListener l, Object source) { if (!lType.isInterface()) { throw new IllegalArgumentException ("Not interface: " + lType); } if (!apiType.isInterface()) { throw new IllegalArgumentException ("Not interface: " + apiType); } if (!apiType.isAssignableFrom(lType)) { throw new IllegalArgumentException (apiType + " has to be assignableFrom " + lType); // NOI18N } return WeakListenerImpl.create (lType, apiType, l, source); } /** Creates a weak implementation of PropertyChangeListener. * * @param l the listener to delegate to * @param source the source that the listener should detach from when * listener l is freed, can be null * @return a PropertyChangeListener delegating to l. */ public static PropertyChangeListener propertyChange (PropertyChangeListener l, Object source) { WeakListenerImpl.PropertyChange wl = new WeakListenerImpl.PropertyChange (l); wl.setSource (source); return wl; } /** Creates a weak implementation of VetoableChangeListener. * * @param l the listener to delegate to * @param source the source that the listener should detach from when * listener l is freed, can be null * @return a VetoableChangeListener delegating to l. */ public static VetoableChangeListener vetoableChange (VetoableChangeListener l, Object source) { WeakListenerImpl.VetoableChange wl = new WeakListenerImpl.VetoableChange (l); wl.setSource (source); return wl; } /** Creates a weak implementation of DocumentListener. * * @param l the listener to delegate to * @param source the source that the listener should detach from when * listener l is freed, can be null * @return a DocumentListener delegating to l. */ public static DocumentListener document (DocumentListener l, Object source) { WeakListenerImpl.Document wl = new WeakListenerImpl.Document (l); wl.setSource (source); return wl; } /** Creates a weak implementation of ChangeListener. * * @param l the listener to delegate to * @param source the source that the listener should detach from when * listener l is freed, can be null * @return a ChangeListener delegating to l. */ public static ChangeListener change (ChangeListener l, Object source) { WeakListenerImpl.Change wl = new WeakListenerImpl.Change (l); wl.setSource (source); return wl; } }
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.