| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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.openide.util; |
| |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.beans.PropertyVetoException; |
| import java.beans.VetoableChangeListener; |
| import java.beans.VetoableChangeSupport; |
| import java.lang.ref.Reference; |
| import java.lang.ref.WeakReference; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import java.util.logging.Handler; |
| import java.util.logging.Level; |
| import java.util.logging.LogRecord; |
| import java.util.logging.Logger; |
| import java.util.prefs.NodeChangeEvent; |
| import java.util.prefs.NodeChangeListener; |
| import java.util.prefs.PreferenceChangeEvent; |
| import java.util.prefs.PreferenceChangeListener; |
| import java.util.prefs.Preferences; |
| import javax.swing.colorchooser.DefaultColorSelectionModel; |
| import javax.swing.event.ChangeEvent; |
| import javax.swing.event.ChangeListener; |
| import org.netbeans.junit.NbTestCase; |
| import org.netbeans.junit.RandomlyFails; |
| |
| public class WeakListenersTest extends NbTestCase { |
| |
| private static final Logger LOG = Logger.getLogger(WeakListenersTest.class.getName()); |
| |
| public WeakListenersTest(String testName) { |
| super(testName); |
| } |
| |
| @Override |
| protected int timeOut() { |
| return 45000; |
| } |
| |
| @Override |
| protected Level logLevel() { |
| return Level.ALL; |
| } |
| |
| /** Tests that exception is not logged when WeakListenerImpl tries to |
| * remove listener on already removed node (#151415). */ |
| public void testPreferenceChangeListener() throws Exception { |
| PreferenceChangeListener pcl = new PreferenceChangeListener() { |
| |
| public void preferenceChange(PreferenceChangeEvent evt) { |
| } |
| }; |
| NodeChangeListener ncl = new NodeChangeListener() { |
| |
| public void childAdded(NodeChangeEvent evt) { |
| } |
| |
| public void childRemoved(NodeChangeEvent evt) { |
| } |
| }; |
| Preferences nbp = NbPreferences.forModule(WeakListeners.class); |
| nbp.addPreferenceChangeListener(WeakListeners.create(PreferenceChangeListener.class, pcl, nbp)); |
| nbp.addNodeChangeListener(WeakListeners.create(NodeChangeListener.class, ncl, nbp)); |
| nbp.removeNode(); |
| |
| Logger logger = Logger.getLogger(WeakListenerImpl.class.getName()); |
| final AtomicBoolean nodeRemovedException = new AtomicBoolean(false); |
| logger.addHandler(new Handler() { |
| |
| @Override |
| public void publish(LogRecord record) { |
| Throwable t = record.getThrown(); |
| if (t != null) { |
| if (t.getCause() instanceof IllegalStateException && t.getCause().getMessage().equals("Node has been removed.")) { //NOI18N |
| nodeRemovedException.set(true); |
| } |
| } |
| } |
| |
| @Override |
| public void flush() { |
| } |
| |
| @Override |
| public void close() throws SecurityException { |
| } |
| }); |
| Reference<PreferenceChangeListener> ref = new WeakReference<PreferenceChangeListener>(pcl); |
| pcl = null; |
| assertGC("PreferenceChangeListener cannot be collected", ref); |
| Reference<NodeChangeListener> ref1 = new WeakReference<NodeChangeListener>(ncl); |
| ncl = null; |
| assertGC("NodeChangeListener cannot be collected", ref1); |
| Thread.sleep(2000); |
| assertFalse("The \"Node has been removed\" exception should not be thrown from WeakListenerImpl.", nodeRemovedException.get()); |
| } |
| |
| public void testOneCanCallHashCodeOrOnWeakListener () { |
| Listener l = new Listener (); |
| Object weak = WeakListeners.create (PropertyChangeListener.class, l, null); |
| weak.hashCode (); |
| } |
| |
| /** Useful for next test */ |
| interface X extends java.util.EventListener { |
| public void invoke (); |
| } |
| /** Useful for next test */ |
| class XImpl implements X { |
| public int cnt; |
| public void invoke () { |
| cnt++; |
| } |
| } |
| public void testCallingMethodsWithNoArgumentWorks() { |
| XImpl l = new XImpl (); |
| LOG.fine("XImpl created: " + l); |
| X weak = WeakListeners.create(X.class, l, null); |
| LOG.fine("weak created: " + weak); |
| weak.invoke (); |
| LOG.fine("invoked"); |
| assertEquals ("One invocation", 1, l.cnt); |
| } |
| |
| public void testReleaseOfListenerWithNullSource () throws Exception { |
| doTestReleaseOfListener (false); |
| } |
| |
| public void testReleaseOfListenerWithSource () throws Exception { |
| doTestReleaseOfListener (true); |
| } |
| |
| public void testReleaseOfPropNameListenerWithNullSource () throws Exception { |
| doTestReleaseOfListener (false, javax.swing.AbstractButton.TEXT_CHANGED_PROPERTY); |
| } |
| |
| public void testReleaseOfPropNameListenerWithSource () throws Exception { |
| doTestReleaseOfListener (true, javax.swing.AbstractButton.TEXT_CHANGED_PROPERTY); |
| } |
| |
| private void doTestReleaseOfListener (final boolean source) throws Exception { |
| doTestReleaseOfListener(source, null); |
| } |
| |
| private void doTestReleaseOfListener (final boolean source, final String propName) throws Exception { |
| Listener l = new Listener (); |
| |
| class MyButton extends javax.swing.JButton { |
| private Thread removedBy; |
| private int cnt; |
| private int cntNamed; |
| |
| @Override |
| public synchronized void removePropertyChangeListener (PropertyChangeListener l) { |
| // notify prior |
| LOG.fine("removePropertyChangeListener: " + source + " cnt: " + cnt); |
| if (source && cnt == 0) { |
| notifyAll (); |
| try { |
| // wait for 1 |
| LOG.fine("wait for 1"); |
| wait (); |
| LOG.fine("wait for 1 over"); |
| } catch (InterruptedException ex) { |
| fail ("Not happen"); |
| } |
| } |
| LOG.fine("Super removePropertyChangeListener"); |
| super.removePropertyChangeListener (l); |
| LOG.fine("Super over removePropertyChangeListener"); |
| removedBy = Thread.currentThread(); |
| cnt++; |
| notifyAll (); |
| } |
| |
| @Override |
| public synchronized void removePropertyChangeListener(String propertyName, PropertyChangeListener l) { |
| // notify prior |
| LOG.fine("removePropertyChangeListener("+propertyName+"): " + source + " cnt: " + cntNamed); |
| if (source && cntNamed == 0) { |
| notifyAll (); |
| try { |
| // wait for 1 |
| LOG.fine("wait for 1"); |
| wait (); |
| LOG.fine("wait for 1 over"); |
| } catch (InterruptedException ex) { |
| fail ("Not happen"); |
| } |
| } |
| assertEquals(propName, propertyName); |
| LOG.fine("Super removePropertyChangeListener"); |
| super.removePropertyChangeListener (propertyName, l); |
| LOG.fine("Super over removePropertyChangeListener"); |
| removedBy = Thread.currentThread(); |
| cntNamed++; |
| notifyAll (); |
| } |
| |
| public synchronized void waitListener () throws Exception { |
| int count = 0; |
| while (removedBy == null) { |
| LOG.fine("waitListener, wait 500"); |
| wait (500); |
| LOG.fine("waitListener 500 Over"); |
| if (count++ == 5) { |
| fail ("Time out: removePropertyChangeListener was not called at all"); |
| } else { |
| LOG.fine("Forced gc"); |
| System.gc (); |
| System.runFinalization(); |
| LOG.fine("after force runFinalization"); |
| } |
| } |
| } |
| } |
| |
| MyButton button = new MyButton (); |
| LOG.fine("Button is here"); |
| java.beans.PropertyChangeListener weakL; |
| if (propName == null) { |
| weakL = WeakListeners.propertyChange (l, source ? button : null); |
| } else { |
| weakL = WeakListeners.propertyChange (l, propName, source ? button : null); |
| } |
| LOG.fine("WeakListeners created: " + weakL); |
| if (propName == null) { |
| button.addPropertyChangeListener(weakL); |
| } else { |
| button.addPropertyChangeListener(propName, weakL); |
| } |
| LOG.fine("WeakListeners attached"); |
| if (propName == null) { |
| assertTrue ("Weak listener is there", Arrays.asList (button.getPropertyChangeListeners()).indexOf (weakL) >= 0); |
| } else { |
| assertTrue ("Weak listener is there", Arrays.asList (button.getPropertyChangeListeners(propName)).indexOf (weakL) >= 0); |
| } |
| |
| button.setText("Ahoj"); |
| LOG.fine("setText changed to ahoj"); |
| assertEquals ("Listener called once", 1, l.cnt); |
| |
| Reference<?> ref = new WeakReference<Object>(l); |
| LOG.fine("Clearing listener"); |
| l = null; |
| |
| |
| synchronized (button) { |
| LOG.fine("Before assertGC"); |
| assertGC ("Can disappear", ref); |
| LOG.fine("assertGC ok"); |
| |
| if (source) { |
| LOG.fine("before wait"); |
| button.wait (); |
| LOG.fine("after wait"); |
| // this should not remove the listener twice |
| button.setText ("Hoj"); |
| LOG.fine("after setText - > hoj"); |
| // go on (wait 1) |
| button.notify (); |
| LOG.fine("before wait listener"); |
| |
| button.waitListener (); |
| LOG.fine("after waitListener"); |
| } else { |
| // trigger the even firing so weak listener knows from |
| // where to unregister |
| LOG.fine("before setText -> Hoj"); |
| button.setText ("Hoj"); |
| LOG.fine("after setText -> Hoj"); |
| } |
| |
| LOG.fine("before 2 waitListener"); |
| button.waitListener (); |
| LOG.fine("after 2 waitListener"); |
| Thread.sleep (500); |
| LOG.fine("Thread.sleep over"); |
| } |
| |
| if (propName == null) { |
| assertEquals ("Weak listener has been removed", -1, Arrays.asList (button.getPropertyChangeListeners()).indexOf (weakL)); |
| } else { |
| assertEquals ("Weak listener has been removed", -1, Arrays.asList (button.getPropertyChangeListeners(propName)).indexOf (weakL)); |
| } |
| assertEquals ("Button released from a thread", "Active Reference Queue Daemon", button.removedBy.getName()); |
| if (propName == null) { |
| assertEquals ("Unregister called just once", 1, button.cnt); |
| assertEquals ("Unregister named not called", 0, button.cntNamed); |
| } else { |
| assertEquals ("Unregister called just once", 1, button.cntNamed); |
| assertEquals ("Unregister unnamed not called", 0, button.cnt); |
| } |
| |
| // and because it is not here, it can be GCed |
| Reference<?> weakRef = new WeakReference<Object>(weakL); |
| weakL = null; |
| LOG.fine("Doing assertGC at the end"); |
| assertGC ("Weak listener can go away as well", weakRef); |
| } |
| |
| public void testNameListenersCombined() { |
| Logger WL_LOG = Logger.getLogger(WeakListenerImpl.class.getName()); |
| class WLLogHandler extends Handler { |
| |
| private String warnings; |
| |
| @Override |
| public void publish(LogRecord record) { |
| if (Level.WARNING.intValue() <= record.getLevel().intValue()) { |
| if (warnings == null) { |
| warnings = record.toString(); |
| } else { |
| warnings += "\n" + record.toString(); |
| } |
| } |
| } |
| |
| @Override |
| public void flush() {} |
| |
| @Override |
| public void close() throws SecurityException {} |
| |
| String getWarnings() { |
| return warnings; |
| } |
| |
| } |
| WLLogHandler wlLogHandler = new WLLogHandler(); |
| WL_LOG.addHandler(wlLogHandler); |
| VetoableChangeSupport vcs = new VetoableChangeSupport(this); |
| DefaultColorSelectionModel dcsm = new DefaultColorSelectionModel(); |
| VListener v1 = new VListener (); |
| VListener v2 = new VListener (); |
| VListener v3 = new VListener (); |
| VListener v4 = new VListener (); |
| CListener c1 = new CListener(); |
| CListener c2 = new CListener(); |
| CListener c3 = new CListener(); |
| CListener c4 = new CListener(); |
| |
| vcs.addVetoableChangeListener (WeakListeners.vetoableChange(v1, vcs)); |
| vcs.addVetoableChangeListener ("name", WeakListeners.vetoableChange (v2, "name", vcs)); |
| vcs.addVetoableChangeListener ("name3", WeakListeners.vetoableChange (v3, "name3", vcs)); |
| vcs.addVetoableChangeListener (WeakListeners.vetoableChange (v4, vcs)); |
| dcsm.addChangeListener(WeakListeners.change(c1, dcsm)); |
| dcsm.addChangeListener(WeakListeners.change(c2, dcsm)); |
| dcsm.addChangeListener(WeakListeners.change(c3, dcsm)); |
| dcsm.addChangeListener(WeakListeners.change(c4, dcsm)); |
| |
| //System.err.println("V1:"); |
| Reference<?> refV1 = new WeakReference<Object>(v1); |
| v1 = null; |
| assertGC ("Listener can be GC", refV1); |
| |
| //System.err.println("C1:"); |
| Reference<?> refC1 = new WeakReference<Object>(c1); |
| c1 = null; |
| assertGC ("Listener can be GC", refC1); |
| |
| //System.err.println("V2:"); |
| Reference<?> refV2 = new WeakReference<Object>(v2); |
| v2 = null; |
| assertGC ("Listener can be GC", refV2); |
| |
| //System.err.println("C2:"); |
| Reference<?> refC2 = new WeakReference<Object>(c2); |
| c2 = null; |
| assertGC ("Listener can be GC", refC2); |
| |
| //System.err.println("V3:"); |
| Reference<?> refV3 = new WeakReference<Object>(v3); |
| v3 = null; |
| assertGC ("Listener can be GC", refV3); |
| |
| //System.err.println("V4:"); |
| Reference<?> refV4 = new WeakReference<Object>(v4); |
| v4 = null; |
| assertGC ("Listener can be GC", refV4); |
| |
| //System.err.println("C3:"); |
| Reference<?> refC3 = new WeakReference<Object>(c3); |
| c3 = null; |
| assertGC ("Listener can be GC", refC3); |
| |
| //System.err.println("ALL:"); |
| Reference<?> refVcs = new WeakReference<Object>(vcs); |
| vcs = null; |
| Reference<?> refDcsm = new WeakReference<Object>(dcsm); |
| dcsm = null; |
| |
| assertGC ("Source can be GC", refVcs); |
| assertGC ("Source can be GC", refDcsm); |
| |
| String warnings = wlLogHandler.getWarnings(); |
| if (warnings != null) { |
| System.err.println(warnings); |
| } |
| assertNull(warnings, warnings); |
| } |
| |
| |
| public void testSourceCanBeGarbageCollected () { |
| javax.swing.JButton b = new javax.swing.JButton (); |
| Listener l = new Listener (); |
| |
| b.addPropertyChangeListener (WeakListeners.propertyChange (l, b)); |
| |
| Reference<?> ref = new WeakReference<Object>(b); |
| b = null; |
| |
| assertGC ("Source can be GC", ref); |
| } |
| |
| public void testNamingListenerBehaviour () throws Exception { |
| Listener l = new Listener (); |
| ImplEventContext c = new ImplEventContext (); |
| javax.naming.event.NamingListener weakL = (javax.naming.event.NamingListener)WeakListeners.create ( |
| javax.naming.event.ObjectChangeListener.class, |
| javax.naming.event.NamingListener.class, |
| l, |
| c |
| ); |
| |
| c.addNamingListener("", javax.naming.event.EventContext.OBJECT_SCOPE, weakL); |
| assertEquals ("Weak listener is there", weakL, c.listener); |
| |
| Reference<?> ref = new WeakReference<Object>(l); |
| l = null; |
| |
| synchronized (c) { |
| assertGC ("Can disappear", ref); |
| c.waitListener (); |
| } |
| assertNull ("Listener removed", c.listener); |
| } |
| |
| public void testExceptionIllegalState () { |
| Listener l = new Listener (); |
| /* Will not even compile any more: |
| try { |
| WeakListeners.create(PropertyChangeListener.class, javax.naming.event.NamingListener.class, l, null); |
| fail ("This shall not be allowed as NamingListener is not superclass of PropertyChangeListener"); |
| } catch (IllegalArgumentException ex) { |
| // ok |
| } |
| |
| try { |
| WeakListeners.create(Object.class, l, null); |
| fail ("Not interface, it should fail"); |
| } catch (IllegalArgumentException ex) { |
| // ok |
| } |
| |
| try { |
| WeakListeners.create(Object.class, Object.class, l, null); |
| fail ("Not interface, it should fail"); |
| } catch (IllegalArgumentException ex) { |
| // ok |
| } |
| */ |
| |
| try { |
| WeakListeners.create (PropertyChangeListener.class, Object.class, l, null); |
| fail ("Not interface, it should fail"); |
| } catch (IllegalArgumentException ex) { |
| // ok |
| } |
| } |
| |
| public void testHowBigIsWeakListener () throws Exception { |
| Listener l = new Listener (); |
| javax.swing.JButton button = new javax.swing.JButton (); |
| ImplEventContext c = new ImplEventContext (); |
| |
| Object[] ignore = { |
| l, |
| button, |
| c, |
| BaseUtilities.activeReferenceQueue() |
| }; |
| |
| |
| PropertyChangeListener pcl = WeakListeners.propertyChange(l, button); |
| assertSize ("Not too big (plus 32 from ReferenceQueue)", java.util.Collections.singleton (pcl), 120, ignore); |
| |
| Object ocl = WeakListeners.create (javax.naming.event.ObjectChangeListener.class, javax.naming.event.NamingListener.class, l, c); |
| assertSize ("A bit bigger (plus 32 from ReferenceQueue)", java.util.Collections.singleton (ocl), 136, ignore); |
| |
| Object nl = WeakListeners.create (javax.naming.event.NamingListener.class, l, c); |
| assertSize ("The same (plus 32 from ReferenceQueue)", java.util.Collections.singleton (nl), 136, ignore); |
| |
| } |
| |
| public void testPrivateRemoveMethod() throws Exception { |
| PropChBean bean = new PropChBean(); |
| Listener listener = new Listener(); |
| PCL weakL = WeakListeners.create(PCL.class, listener, bean); |
| Reference<?> ref = new WeakReference<Object>(listener); |
| |
| bean.addPCL(weakL); |
| |
| bean.listeners.firePropertyChange (null, null, null); |
| assertEquals ("One call to the listener", 1, listener.cnt); |
| listener.cnt = 0; |
| |
| listener = null; |
| assertGC("Listener wasn't GCed", ref); |
| |
| ref = new WeakReference<Object>(weakL); |
| weakL = null; |
| assertGC("WeakListener wasn't GCed", ref); |
| |
| // this shall enforce the removal of the listener |
| bean.listeners.firePropertyChange (null, null, null); |
| |
| assertEquals ("No listeners", 0, bean.listeners.getPropertyChangeListeners ().length); |
| } |
| |
| @RandomlyFails // NB-Core-Build #1651 |
| public void testStaticRemoveMethod() throws Exception { |
| ChangeListener l = new ChangeListener() {public void stateChanged(ChangeEvent e) {}}; |
| Singleton.addChangeListener(WeakListeners.change(l, Singleton.class)); |
| assertEquals(1, Singleton.listeners.size()); |
| Reference<?> r = new WeakReference<Object>(l); |
| l = null; |
| assertGC("could collect listener", r); |
| assertEquals("called remove method", 0, Singleton.listeners.size()); |
| } |
| |
| public void testIssue156703() throws InterruptedException { |
| class Obj { |
| |
| Set<PropertyChangeListener> listeners = new LinkedHashSet<PropertyChangeListener>(); |
| private Thread removedBy; |
| int cnt; |
| |
| synchronized void addPropertyChangeListener(PropertyChangeListener listener) { |
| listeners.add(listener); |
| } |
| |
| synchronized void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { |
| listeners.remove(listener); |
| removedBy = Thread.currentThread(); |
| cnt++; |
| notifyAll(); |
| } |
| |
| synchronized void firePropertyChange(String propertyName, Object oldValue, Object newValue) { |
| PropertyChangeEvent evt = new PropertyChangeEvent(this, propertyName, oldValue, newValue); |
| for (PropertyChangeListener l : listeners) { |
| l.propertyChange(evt); |
| } |
| } |
| } |
| Listener l = new Listener(); |
| Obj obj = new Obj(); |
| java.beans.PropertyChangeListener weakL = WeakListeners.propertyChange(l, obj); |
| obj.addPropertyChangeListener(weakL); |
| obj.firePropertyChange("aa", null, null); |
| assertEquals("Listener called once", 1, l.cnt); |
| |
| Reference<?> ref = new WeakReference<Object>(l); |
| l = null; |
| |
| synchronized (obj) { |
| assertGC("Can disappear", ref); |
| obj.wait(); |
| } |
| |
| assertTrue("Weak listener has been removed", obj.listeners.isEmpty()); |
| assertEquals("Unregister called just once", 1, obj.cnt); |
| assertEquals("Button released from a thread", "Active Reference Queue Daemon", obj.removedBy.getName()); |
| |
| Reference<?> weakRef = new WeakReference<Object>(weakL); |
| weakL = null; |
| assertGC("Weak listener can go away as well", weakRef); |
| } |
| |
| public void testWrapOnlyCheckedExceptions() throws Exception { |
| try { |
| WeakListeners.create(ChangeListener.class, new ChangeListener() { |
| @Override public void stateChanged(ChangeEvent e) { |
| throw new IllegalStateException(); |
| } |
| }, null).stateChanged(null); |
| fail(); |
| } catch (IllegalStateException x) { |
| // OK |
| } |
| try { |
| WeakListeners.create(ChangeListener.class, new ChangeListener() { |
| @Override public void stateChanged(ChangeEvent e) { |
| throw new ThreadDeath(); |
| } |
| }, null).stateChanged(null); |
| fail(); |
| } catch (ThreadDeath x) { |
| // OK |
| } |
| } |
| |
| public static class Singleton { |
| public static List<ChangeListener> listeners = new ArrayList<ChangeListener>(); |
| public static void addChangeListener(ChangeListener l) { |
| listeners.add(l); |
| } |
| public static void removeChangeListener(ChangeListener l) { |
| listeners.remove(l); |
| } |
| } |
| |
| private static final class Listener |
| implements PCL, java.beans.PropertyChangeListener, javax.naming.event.ObjectChangeListener { |
| public int cnt; |
| |
| public void propertyChange (java.beans.PropertyChangeEvent ev) { |
| cnt++; |
| } |
| |
| public void namingExceptionThrown(javax.naming.event.NamingExceptionEvent evt) { |
| cnt++; |
| } |
| |
| public void objectChanged(javax.naming.event.NamingEvent evt) { |
| cnt++; |
| } |
| } // end of Listener |
| |
| private static final class VListener implements VetoableChangeListener { |
| |
| @Override |
| public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { |
| } |
| } |
| |
| private static final class CListener implements ChangeListener { |
| |
| @Override |
| public void stateChanged(ChangeEvent e) { |
| } |
| } |
| |
| private static final class ImplEventContext extends javax.naming.InitialContext |
| implements javax.naming.event.EventContext { |
| public javax.naming.event.NamingListener listener; |
| |
| public ImplEventContext () throws Exception { |
| } |
| |
| public void addNamingListener(javax.naming.Name target, int scope, javax.naming.event.NamingListener l) throws javax.naming.NamingException { |
| assertNull (listener); |
| listener = l; |
| } |
| |
| public void addNamingListener(String target, int scope, javax.naming.event.NamingListener l) throws javax.naming.NamingException { |
| assertNull (listener); |
| listener = l; |
| } |
| |
| public synchronized void removeNamingListener(javax.naming.event.NamingListener l) throws javax.naming.NamingException { |
| assertEquals ("Removing the same listener", listener, l); |
| listener = null; |
| notifyAll (); |
| } |
| |
| public boolean targetMustExist() throws javax.naming.NamingException { |
| return false; |
| } |
| |
| public synchronized void waitListener () throws Exception { |
| int cnt = 0; |
| while (listener != null) { |
| wait (500); |
| if (cnt++ == 5) { |
| fail ("Time out: removeNamingListener was not called at all"); |
| } else { |
| System.gc (); |
| System.runFinalization(); |
| } |
| } |
| } |
| |
| } |
| |
| private static class PropChBean { |
| private java.beans.PropertyChangeSupport listeners = new java.beans.PropertyChangeSupport (this); |
| private void addPCL(PCL l) { listeners.addPropertyChangeListener (l); } |
| private void removePCL(PCL l) { listeners.removePropertyChangeListener (l); } |
| } // End of PropChBean class |
| |
| // just a marker, its name will be used to construct the name of add/remove methods, e.g. addPCL, removePCL |
| private static interface PCL extends PropertyChangeListener { |
| } // End of PrivatePropL class |
| |
| } |