7170447: Intermittent DeadListenerTest.java failure
authorjbachorik
Tue, 05 Feb 2013 12:28:47 +0100
changeset 15536 ac6c0f4a8898
parent 15535 e5a24f321ab6
child 15537 cca8fab7313a
7170447: Intermittent DeadListenerTest.java failure Summary: Due to asynchronous nature of processing server notifications it may happen that an "unregister" notification ha$ Reviewed-by: sjiang
jdk/test/javax/management/remote/mandatory/notif/DeadListenerTest.java
--- a/jdk/test/javax/management/remote/mandatory/notif/DeadListenerTest.java	Mon Feb 04 22:37:44 2013 -0800
+++ b/jdk/test/javax/management/remote/mandatory/notif/DeadListenerTest.java	Tue Feb 05 12:28:47 2013 +0100
@@ -31,6 +31,7 @@
 import com.sun.jmx.remote.internal.ServerNotifForwarder;
 import java.io.IOException;
 import java.lang.management.ManagementFactory;
+import java.lang.ref.WeakReference;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
@@ -86,20 +87,26 @@
         Map<ObjectName, Set<?>> listenerMap = (Map<ObjectName, Set<?>>) listenerMapF.get(serverNotifForwarder);
         assertTrue("Server listenerMap initially empty", mapWithoutKey(listenerMap, delegateName).isEmpty());
 
-        CountListener count1 = new CountListener();
+        final AtomicInteger count1Val = new AtomicInteger();
+        CountListener count1 = new CountListener(count1Val);
         mbsc.addNotificationListener(name, count1, null, null);
+        WeakReference<CountListener> count1Ref = new WeakReference<>(count1);
+        count1 = null;
 
-        CountListener count2 = new CountListener();
+        final AtomicInteger count2Val = new AtomicInteger();
+        CountListener count2 = new CountListener(count2Val);
         NotificationFilterSupport dummyFilter = new NotificationFilterSupport();
         dummyFilter.enableType("");
         mbsc.addNotificationListener(name, count2, dummyFilter, "noddy");
+        WeakReference<CountListener> count2Ref = new WeakReference<>(count2);
+        count2 = null;
 
         assertTrue("One entry in listenerMap for two listeners on same MBean", mapWithoutKey(listenerMap, delegateName).size() == 1);
         Set<?> set = listenerMap.get(name);
         assertTrue("Set in listenerMap for MBean has two elements", set != null && set.size() == 2);
 
-        assertTrue("Initial value of count1 == 0", count1.count() == 0);
-        assertTrue("Initial value of count2 == 0", count2.count() == 0);
+        assertTrue("Initial value of count1 == 0", count1Val.get() == 0);
+        assertTrue("Initial value of count2 == 0", count2Val.get() == 0);
 
         Notification notif = new Notification("type", name, 0);
 
@@ -107,11 +114,11 @@
 
         // Make sure notifs are working normally.
         long deadline = System.currentTimeMillis() + 2000;
-        while ((count1.count() != 1 || count2.count() != 1) && System.currentTimeMillis() < deadline) {
+        while ((count1Val.get() != 1 || count2Val.get() != 1) && System.currentTimeMillis() < deadline) {
             Thread.sleep(10);
         }
-        assertTrue("New value of count1 == 1", count1.count() == 1);
-        assertTrue("Initial value of count2 == 1", count2.count() == 1);
+        assertTrue("New value of count1 == 1", count1Val.get() == 1);
+        assertTrue("Initial value of count2 == 1", count2Val.get() == 1);
 
         // Make sure that removing a nonexistent listener from an existent MBean produces ListenerNotFoundException
         CountListener count3 = new CountListener();
@@ -136,28 +143,29 @@
         mbs.unregisterMBean(name);
         mbean.sendNotification(notif);
         Thread.sleep(200);
-        assertTrue("New value of count1 == 1", count1.count() == 1);
-        assertTrue("Initial value of count2 == 1", count2.count() == 1);
+
+        assertTrue("New value of count1 == 1", count1Val.get() == 1);
+        assertTrue("Initial value of count2 == 1", count2Val.get() == 1);
+
+        // wait for the listener cleanup to take place upon processing notifications
+        int countdown = 50; // waiting max. 5 secs
+        while (countdown-- > 0 &&
+                (count1Ref.get() != null ||
+                 count2Ref.get() != null)) {
+            System.gc();
+            Thread.sleep(100);
+            System.gc();
+        }
+        // listener has been removed or the wait has timed out
+
+        assertTrue("count1 notification listener has not been cleaned up", count1Ref.get() == null);
+        assertTrue("count2 notification listener has not been cleaned up", count2Ref.get() == null);
 
         // Check that there is no trace of the listeners any more in ServerNotifForwarder.listenerMap.
         // THIS DEPENDS ON JMX IMPLEMENTATION DETAILS.
         // If the JMX implementation changes, the code here may have to change too.
         Set<?> setForUnreg = listenerMap.get(name);
         assertTrue("No trace of unregistered MBean: " + setForUnreg, setForUnreg == null);
-
-        // Remove attempts should fail.
-        try {
-            mbsc.removeNotificationListener(name, count1);
-            assertTrue("Remove of count1 listener should have failed", false);
-        } catch (ListenerNotFoundException e) {
-            // OK: expected
-        }
-        try {
-            mbsc.removeNotificationListener(name, count2, dummyFilter, "noddy");
-            assertTrue("Remove of count2 listener should have failed", false);
-        } catch (ListenerNotFoundException e) {
-            // OK: expected
-        }
     }
 
     private static <K, V> Map<K, V> mapWithoutKey(Map<K, V> map, K key) {
@@ -173,6 +181,10 @@
     public static class CountListener implements NotificationListener {
         final AtomicInteger count;
 
+        public CountListener(AtomicInteger i) {
+            count = i;
+        }
+
         public CountListener() {
             this.count = new AtomicInteger();
         }