1 19 20 package org.openide.util; 21 22 import java.beans.PropertyChangeListener ; 23 import java.lang.ref.Reference ; 24 import java.lang.ref.WeakReference ; 25 import java.util.ArrayList ; 26 import java.util.Arrays ; 27 import java.util.List ; 28 import java.util.logging.Level ; 29 import javax.swing.event.ChangeEvent ; 30 import javax.swing.event.ChangeListener ; 31 import org.netbeans.junit.MockServices; 32 import org.netbeans.junit.NbTestCase; 33 import org.openide.ErrorManager; 34 35 public class WeakListenersTest extends NbTestCase { 36 37 private static Thread activeQueueThread; 38 39 private ErrorManager log; 40 41 public WeakListenersTest(String testName) { 42 super(testName); 43 } 44 45 protected int timeOut() { 46 return 5000; 47 } 48 49 protected Level logLevel() { 50 return Level.ALL; 51 } 52 53 protected void setUp () throws Exception { 54 MockServices.setServices(ErrManager.class); 55 log = ErrorManager.getDefault().getInstance("TEST-" + getName()); 56 57 if (activeQueueThread == null) { 58 class WR extends WeakReference <Object > implements Runnable { 59 public WR (Object o) { 60 super (o, Utilities.activeReferenceQueue ()); 61 } 62 public synchronized void run () { 63 activeQueueThread = Thread.currentThread(); 64 notifyAll (); 65 } 66 } 67 68 Object obj = new Object (); 69 WR wr = new WR (obj); 70 synchronized (wr) { 71 obj = null; 72 assertGC ("Has to be cleared", wr); 73 while (activeQueueThread == null) { 75 wr.wait (); 76 } 77 } 78 } 79 } 80 81 protected void runTest() throws Throwable { 82 assertNotNull ("ErrManager has to be in lookup", org.openide.util.Lookup.getDefault ().lookup (ErrManager.class)); 83 ErrManager.messages.setLength(0); 84 85 try { 86 super.runTest(); 87 } catch (Throwable ex) { 88 throw new junit.framework.AssertionFailedError ( 89 ex.getMessage() + "\n" + ErrManager.messages.toString() 90 ).initCause(ex); 91 } 92 } 93 94 95 public void testOneCanCallHashCodeOrOnWeakListener () { 96 Listener l = new Listener (); 97 Object weak = WeakListeners.create (PropertyChangeListener .class, l, null); 98 weak.hashCode (); 99 } 100 101 102 interface X extends java.util.EventListener { 103 public void invoke (); 104 } 105 106 class XImpl implements X { 107 public int cnt; 108 public void invoke () { 109 cnt++; 110 } 111 } 112 public void testCallingMethodsWithNoArgumentWorks() { 113 XImpl l = new XImpl (); 114 log.log("XImpl created: " + l); 115 X weak = (X)WeakListeners.create (X.class, l, null); 116 log.log("weak created: " + weak); 117 weak.invoke (); 118 log.log("invoked"); 119 assertEquals ("One invocation", 1, l.cnt); 120 } 121 122 public void testReleaseOfListenerWithNullSource () throws Exception { 123 doTestReleaseOfListener (false); 124 } 125 126 public void testReleaseOfListenerWithSource () throws Exception { 127 doTestReleaseOfListener (true); 128 } 129 130 private void doTestReleaseOfListener (final boolean source) throws Exception { 131 Listener l = new Listener (); 132 133 class MyButton extends javax.swing.JButton { 134 private Thread removedBy; 135 private int cnt; 136 137 public synchronized void removePropertyChangeListener (PropertyChangeListener l) { 138 log.log("removePropertyChangeListener: " + source + " cnt: " + cnt); 140 if (source && cnt == 0) { 141 notifyAll (); 142 try { 143 log.log("wait for 1"); 145 wait (); 146 log.log("wait for 1 over"); 147 } catch (InterruptedException ex) { 148 fail ("Not happen"); 149 } 150 } 151 log.log("Super removePropertyChangeListener"); 152 super.removePropertyChangeListener (l); 153 log.log("Super over removePropertyChangeListener"); 154 removedBy = Thread.currentThread(); 155 cnt++; 156 notifyAll (); 157 } 158 159 public synchronized void waitListener () throws Exception { 160 int cnt = 0; 161 while (removedBy == null) { 162 log.log("waitListener, wait 500"); 163 wait (500); 164 log.log("waitListener 500 Over"); 165 if (cnt++ == 5) { 166 fail ("Time out: removePropertyChangeListener was not called at all"); 167 } else { 168 log.log("Forced gc"); 169 System.gc (); 170 System.runFinalization(); 171 log.log("after force runFinalization"); 172 } 173 } 174 } 175 } 176 177 MyButton button = new MyButton (); 178 log.log("Button is here"); 179 java.beans.PropertyChangeListener weakL = WeakListeners.propertyChange (l, source ? button : null); 180 log.log("WeakListeners created: " + weakL); 181 button.addPropertyChangeListener(weakL); 182 log.log("WeakListeners attached"); 183 assertTrue ("Weak listener is there", Arrays.asList (button.getPropertyChangeListeners()).indexOf (weakL) >= 0); 184 185 button.setText("Ahoj"); 186 log.log("setText changed to ahoj"); 187 assertEquals ("Listener called once", 1, l.cnt); 188 189 Reference <?> ref = new WeakReference <Object >(l); 190 log.log("Clearing listener"); 191 l = null; 192 193 194 synchronized (button) { 195 log.log("Before assertGC"); 196 assertGC ("Can disappear", ref); 197 log.log("assertGC ok"); 198 199 if (source) { 200 log.log("before wait"); 201 button.wait (); 202 log.log("after wait"); 203 button.setText ("Hoj"); 205 log.log("after setText - > hoj"); 206 button.notify (); 208 log.log("before wait listener"); 209 210 button.waitListener (); 211 log.log("after waitListener"); 212 } else { 213 log.log("before setText -> Hoj"); 216 button.setText ("Hoj"); 217 log.log("after setText -> Hoj"); 218 } 219 220 log.log("before 2 waitListener"); 221 button.waitListener (); 222 log.log("after 2 waitListener"); 223 Thread.sleep (500); 224 log.log("Thread.sleep over"); 225 } 226 227 assertEquals ("Weak listener has been removed", -1, Arrays.asList (button.getPropertyChangeListeners()).indexOf (weakL)); 228 assertEquals ("Button released from a thread", activeQueueThread, button.removedBy); 229 assertEquals ("Unregister called just once", 1, button.cnt); 230 231 Reference <?> weakRef = new WeakReference <Object >(weakL); 233 weakL = null; 234 log.log("Doing assertGC at the end"); 235 assertGC ("Weak listener can go away as well", weakRef); 236 } 237 238 239 public void testSourceCanBeGarbageCollected () { 240 javax.swing.JButton b = new javax.swing.JButton (); 241 Listener l = new Listener (); 242 243 b.addPropertyChangeListener (WeakListeners.propertyChange (l, b)); 244 245 Reference <?> ref = new WeakReference <Object >(b); 246 b = null; 247 248 assertGC ("Source can be GC", ref); 249 } 250 251 public void testNamingListenerBehaviour () throws Exception { 252 Listener l = new Listener (); 253 ImplEventContext c = new ImplEventContext (); 254 javax.naming.event.NamingListener weakL = (javax.naming.event.NamingListener )WeakListeners.create ( 255 javax.naming.event.ObjectChangeListener .class, 256 javax.naming.event.NamingListener .class, 257 l, 258 c 259 ); 260 261 c.addNamingListener("", javax.naming.event.EventContext.OBJECT_SCOPE, weakL); 262 assertEquals ("Weak listener is there", weakL, c.listener); 263 264 Reference <?> ref = new WeakReference <Object >(l); 265 l = null; 266 267 synchronized (c) { 268 assertGC ("Can disappear", ref); 269 c.waitListener (); 270 } 271 assertNull ("Listener removed", c.listener); 272 } 273 274 public void testExceptionIllegalState () { 275 Listener l = new Listener (); 276 try { 277 WeakListeners.create ((Class )PropertyChangeListener .class, (Class )javax.naming.event.NamingListener .class, l, null); 278 fail ("This shall not be allowed as NamingListener is not superclass of PropertyChangeListener"); 279 } catch (IllegalArgumentException ex) { 280 } 282 283 try { 284 WeakListeners.create ((Class )Object .class, l, null); 285 fail ("Not interface, it should fail"); 286 } catch (IllegalArgumentException ex) { 287 } 289 290 try { 291 WeakListeners.create ((Class )Object .class, (Class )Object .class, l, null); 292 fail ("Not interface, it should fail"); 293 } catch (IllegalArgumentException ex) { 294 } 296 297 try { 298 WeakListeners.create (PropertyChangeListener .class, Object .class, l, null); 299 fail ("Not interface, it should fail"); 300 } catch (IllegalArgumentException ex) { 301 } 303 } 304 305 public void testHowBigIsWeakListener () throws Exception { 306 Listener l = new Listener (); 307 javax.swing.JButton button = new javax.swing.JButton (); 308 ImplEventContext c = new ImplEventContext (); 309 310 Object [] ignore = new Object [] { 311 l, 312 button, 313 c, 314 Utilities.activeReferenceQueue() 315 }; 316 317 318 PropertyChangeListener pcl = WeakListeners.propertyChange(l, button); 319 assertSize ("Not too big (plus 32 from ReferenceQueue)", java.util.Collections.singleton (pcl), 112, ignore); 320 321 Object ocl = WeakListeners.create (javax.naming.event.ObjectChangeListener .class, javax.naming.event.NamingListener .class, l, c); 322 assertSize ("A bit bigger (plus 32 from ReferenceQueue)", java.util.Collections.singleton (ocl), 128, ignore); 323 324 Object nl = WeakListeners.create (javax.naming.event.NamingListener .class, l, c); 325 assertSize ("The same (plus 32 from ReferenceQueue)", java.util.Collections.singleton (nl), 128, ignore); 326 327 } 328 329 public void testPrivateRemoveMethod() throws Exception { 330 PropChBean bean = new PropChBean(); 331 Listener listener = new Listener (); 332 PCL weakL = (PCL) WeakListeners.create(PCL.class, listener, bean); 333 Reference <?> ref = new WeakReference <Object >(listener); 334 335 bean.addPCL(weakL); 336 337 bean.listeners.firePropertyChange (null, null, null); 338 assertEquals ("One call to the listener", 1, listener.cnt); 339 listener.cnt = 0; 340 341 listener = null; 342 assertGC("Listener wasn't GCed", ref); 343 344 ref = new WeakReference <Object >(weakL); 345 weakL = null; 346 assertGC("WeakListener wasn't GCed", ref); 347 348 bean.listeners.firePropertyChange (null, null, null); 350 351 assertEquals ("No listeners", 0, bean.listeners.getPropertyChangeListeners ().length); 352 } 353 354 public void testStaticRemoveMethod() throws Exception { 355 ChangeListener l = new ChangeListener () {public void stateChanged(ChangeEvent e) {}}; 356 Singleton.addChangeListener(WeakListeners.change(l, Singleton.class)); 357 assertEquals(1, Singleton.listeners.size()); 358 Reference <?> r = new WeakReference <Object >(l); 359 l = null; 360 assertGC("could collect listener", r); 361 assertEquals("called remove method", 0, Singleton.listeners.size()); 362 } 363 public static class Singleton { 364 public static List <ChangeListener > listeners = new ArrayList <ChangeListener >(); 365 public static void addChangeListener(ChangeListener l) { 366 listeners.add(l); 367 } 368 public static void removeChangeListener(ChangeListener l) { 369 listeners.remove(l); 370 } 371 } 372 373 private static final class Listener 374 implements PCL, java.beans.PropertyChangeListener , javax.naming.event.ObjectChangeListener { 375 public int cnt; 376 377 public void propertyChange (java.beans.PropertyChangeEvent ev) { 378 cnt++; 379 } 380 381 public void namingExceptionThrown(javax.naming.event.NamingExceptionEvent evt) { 382 cnt++; 383 } 384 385 public void objectChanged(javax.naming.event.NamingEvent evt) { 386 cnt++; 387 } 388 } 390 private static final class ImplEventContext extends javax.naming.InitialContext 391 implements javax.naming.event.EventContext { 392 public javax.naming.event.NamingListener listener; 393 394 public ImplEventContext () throws Exception { 395 } 396 397 public void addNamingListener(javax.naming.Name target, int scope, javax.naming.event.NamingListener l) throws javax.naming.NamingException { 398 assertNull (listener); 399 listener = l; 400 } 401 402 public void addNamingListener(String target, int scope, javax.naming.event.NamingListener l) throws javax.naming.NamingException { 403 assertNull (listener); 404 listener = l; 405 } 406 407 public synchronized void removeNamingListener(javax.naming.event.NamingListener l) throws javax.naming.NamingException { 408 assertEquals ("Removing the same listener", listener, l); 409 listener = null; 410 notifyAll (); 411 } 412 413 public boolean targetMustExist() throws javax.naming.NamingException { 414 return false; 415 } 416 417 public synchronized void waitListener () throws Exception { 418 int cnt = 0; 419 while (listener != null) { 420 wait (500); 421 if (cnt++ == 5) { 422 fail ("Time out: removeNamingListener was not called at all"); 423 } else { 424 System.gc (); 425 System.runFinalization(); 426 } 427 } 428 } 429 430 } 431 432 private static class PropChBean { 433 private java.beans.PropertyChangeSupport listeners = new java.beans.PropertyChangeSupport (this); 434 private void addPCL(PCL l) { listeners.addPropertyChangeListener (l); } 435 private void removePCL(PCL l) { listeners.removePropertyChangeListener (l); } 436 } 438 private static interface PCL extends PropertyChangeListener { 440 } 442 public static final class ErrManager extends org.openide.ErrorManager { 446 public static final StringBuffer messages = new StringBuffer (); 447 448 private String prefix; 449 450 public ErrManager () { 451 this (null); 452 } 453 public ErrManager (String prefix) { 454 this.prefix = prefix; 455 } 456 457 public static ErrManager get () { 458 return (ErrManager)org.openide.util.Lookup.getDefault ().lookup (ErrManager.class); 459 } 460 461 public Throwable annotate (Throwable t, int severity, String message, String localizedMessage, Throwable stackTrace, java.util.Date date) { 462 return t; 463 } 464 465 public Throwable attachAnnotations (Throwable t, org.openide.ErrorManager.Annotation[] arr) { 466 return t; 467 } 468 469 public org.openide.ErrorManager.Annotation[] findAnnotations (Throwable t) { 470 return null; 471 } 472 473 public org.openide.ErrorManager getInstance (String name) { 474 if ( 475 name.startsWith ("org.openide.util.RequestProcessor") || 476 name.startsWith("TEST") 477 ) { 478 return new ErrManager ('[' + name + ']'); 479 } else { 480 return new ErrManager (); 482 } 483 } 484 485 public void log (int severity, String s) { 486 lastSeverity = severity; 487 lastText = s; 488 if (this != get()) { 489 messages.append(prefix); 490 messages.append(s); 491 messages.append('\n'); 492 } 493 } 494 495 public void notify (int severity, Throwable t) { 496 lastThrowable = t; 497 lastSeverity = severity; 498 } 499 private static int lastSeverity; 500 private static Throwable lastThrowable; 501 private static String lastText; 502 503 public static void assertNotify (int sev, Throwable t) { 504 assertEquals ("Severity is same", sev, lastSeverity); 505 assertSame ("Throwable is the same", t, lastThrowable); 506 lastThrowable = null; 507 lastSeverity = -1; 508 } 509 510 public static void assertLog (int sev, String t) { 511 assertEquals ("Severity is same", sev, lastSeverity); 512 assertEquals ("Text is the same", t, lastText); 513 lastText = null; 514 lastSeverity = -1; 515 } 516 517 } 519 } 520 | Popular Tags |