author | alanb |
Thu, 01 Dec 2016 08:57:53 +0000 | |
changeset 42338 | a60f280f803c |
parent 40699 | df9fab06ba9d |
child 43503 | bc7f8619ab70 |
permissions | -rw-r--r-- |
7170 | 1 |
/* |
40699
df9fab06ba9d
8164609: javax/management/remote/mandatory/notif/DeadListenerTest.java fails with Assertion Error
vtewari
parents:
31455
diff
changeset
|
2 |
* Copyright (c) 2010, 2016, Oracle and/or its affiliates. All rights reserved. |
7170 | 3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 |
* |
|
5 |
* This code is free software; you can redistribute it and/or modify it |
|
6 |
* under the terms of the GNU General Public License version 2 only, as |
|
7 |
* published by the Free Software Foundation. |
|
8 |
* |
|
9 |
* This code is distributed in the hope that it will be useful, but WITHOUT |
|
10 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
11 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
12 |
* version 2 for more details (a copy is included in the LICENSE file that |
|
13 |
* accompanied this code). |
|
14 |
* |
|
15 |
* You should have received a copy of the GNU General Public License version |
|
16 |
* 2 along with this work; if not, write to the Free Software Foundation, |
|
17 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
18 |
* |
|
19 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
20 |
* or visit www.oracle.com if you need additional information or have any |
|
21 |
* questions. |
|
22 |
*/ |
|
23 |
||
24 |
/* |
|
25 |
* @test |
|
26 |
* @bug 6957378 |
|
27 |
* @summary Test that a listener can be removed remotely from an MBean that no longer exists. |
|
42338
a60f280f803c
8169069: Module system implementation refresh (11/2016)
alanb
parents:
40699
diff
changeset
|
28 |
* @modules java.management/javax.management.remote.rmi:open |
a60f280f803c
8169069: Module system implementation refresh (11/2016)
alanb
parents:
40699
diff
changeset
|
29 |
* java.management/com.sun.jmx.remote.internal:+open |
7170 | 30 |
* @author Eamonn McManus |
31455
412ba247f8df
8085973: The targeted processes in javax/management tests should be launched with -XX:+UsePerfData flag in order to work on embedded platforms
ykantser
parents:
30376
diff
changeset
|
31 |
* @run main/othervm -XX:+UsePerfData DeadListenerTest |
7170 | 32 |
*/ |
33 |
||
34 |
import com.sun.jmx.remote.internal.ServerNotifForwarder; |
|
35 |
import java.io.IOException; |
|
36 |
import java.lang.management.ManagementFactory; |
|
15536
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
37 |
import java.lang.ref.WeakReference; |
7170 | 38 |
import java.lang.reflect.Field; |
39 |
import java.lang.reflect.Method; |
|
40 |
import java.util.ArrayList; |
|
41 |
import java.util.HashMap; |
|
42 |
import java.util.List; |
|
43 |
import java.util.Map; |
|
44 |
import java.util.Set; |
|
45 |
import java.util.concurrent.atomic.AtomicInteger; |
|
46 |
import javax.management.ListenerNotFoundException; |
|
47 |
import javax.management.MBeanServer; |
|
48 |
import javax.management.MBeanServerConnection; |
|
49 |
import javax.management.MBeanServerDelegate; |
|
50 |
import javax.management.Notification; |
|
51 |
import javax.management.NotificationBroadcasterSupport; |
|
52 |
import javax.management.NotificationFilterSupport; |
|
53 |
import javax.management.NotificationListener; |
|
54 |
import javax.management.ObjectName; |
|
55 |
import javax.management.remote.JMXConnector; |
|
56 |
import javax.management.remote.JMXConnectorFactory; |
|
57 |
import javax.management.remote.JMXServiceURL; |
|
58 |
import javax.management.remote.rmi.RMIConnection; |
|
59 |
import javax.management.remote.rmi.RMIConnectionImpl; |
|
60 |
import javax.management.remote.rmi.RMIConnectorServer; |
|
61 |
import javax.management.remote.rmi.RMIJRMPServerImpl; |
|
62 |
import javax.security.auth.Subject; |
|
63 |
||
64 |
public class DeadListenerTest { |
|
65 |
public static void main(String[] args) throws Exception { |
|
66 |
final ObjectName delegateName = MBeanServerDelegate.DELEGATE_NAME; |
|
67 |
||
68 |
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); |
|
69 |
Noddy mbean = new Noddy(); |
|
70 |
ObjectName name = new ObjectName("d:k=v"); |
|
71 |
mbs.registerMBean(mbean, name); |
|
72 |
||
73 |
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///"); |
|
74 |
SnoopRMIServerImpl rmiServer = new SnoopRMIServerImpl(); |
|
75 |
RMIConnectorServer cs = new RMIConnectorServer(url, null, rmiServer, mbs); |
|
76 |
cs.start(); |
|
77 |
JMXServiceURL addr = cs.getAddress(); |
|
78 |
assertTrue("No connections in new connector server", rmiServer.connections.isEmpty()); |
|
79 |
||
80 |
JMXConnector cc = JMXConnectorFactory.connect(addr); |
|
81 |
MBeanServerConnection mbsc = cc.getMBeanServerConnection(); |
|
82 |
assertTrue("One connection on server after client connect", rmiServer.connections.size() == 1); |
|
83 |
RMIConnectionImpl connection = rmiServer.connections.get(0); |
|
84 |
Method getServerNotifFwdM = RMIConnectionImpl.class.getDeclaredMethod("getServerNotifFwd"); |
|
85 |
getServerNotifFwdM.setAccessible(true); |
|
86 |
ServerNotifForwarder serverNotifForwarder = (ServerNotifForwarder) getServerNotifFwdM.invoke(connection); |
|
87 |
Field listenerMapF = ServerNotifForwarder.class.getDeclaredField("listenerMap"); |
|
88 |
listenerMapF.setAccessible(true); |
|
89 |
@SuppressWarnings("unchecked") |
|
90 |
Map<ObjectName, Set<?>> listenerMap = (Map<ObjectName, Set<?>>) listenerMapF.get(serverNotifForwarder); |
|
91 |
assertTrue("Server listenerMap initially empty", mapWithoutKey(listenerMap, delegateName).isEmpty()); |
|
92 |
||
15536
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
93 |
final AtomicInteger count1Val = new AtomicInteger(); |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
94 |
CountListener count1 = new CountListener(count1Val); |
7170 | 95 |
mbsc.addNotificationListener(name, count1, null, null); |
15536
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
96 |
WeakReference<CountListener> count1Ref = new WeakReference<>(count1); |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
97 |
count1 = null; |
7170 | 98 |
|
15536
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
99 |
final AtomicInteger count2Val = new AtomicInteger(); |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
100 |
CountListener count2 = new CountListener(count2Val); |
7170 | 101 |
NotificationFilterSupport dummyFilter = new NotificationFilterSupport(); |
102 |
dummyFilter.enableType(""); |
|
103 |
mbsc.addNotificationListener(name, count2, dummyFilter, "noddy"); |
|
15536
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
104 |
WeakReference<CountListener> count2Ref = new WeakReference<>(count2); |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
105 |
count2 = null; |
7170 | 106 |
|
107 |
assertTrue("One entry in listenerMap for two listeners on same MBean", mapWithoutKey(listenerMap, delegateName).size() == 1); |
|
108 |
Set<?> set = listenerMap.get(name); |
|
109 |
assertTrue("Set in listenerMap for MBean has two elements", set != null && set.size() == 2); |
|
110 |
||
15536
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
111 |
assertTrue("Initial value of count1 == 0", count1Val.get() == 0); |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
112 |
assertTrue("Initial value of count2 == 0", count2Val.get() == 0); |
7170 | 113 |
|
114 |
Notification notif = new Notification("type", name, 0); |
|
115 |
||
116 |
mbean.sendNotification(notif); |
|
117 |
||
118 |
// Make sure notifs are working normally. |
|
40699
df9fab06ba9d
8164609: javax/management/remote/mandatory/notif/DeadListenerTest.java fails with Assertion Error
vtewari
parents:
31455
diff
changeset
|
119 |
while ((count1Val.get() != 1 || count2Val.get() != 1) ) { |
df9fab06ba9d
8164609: javax/management/remote/mandatory/notif/DeadListenerTest.java fails with Assertion Error
vtewari
parents:
31455
diff
changeset
|
120 |
Thread.sleep(20); |
7170 | 121 |
} |
15536
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
122 |
assertTrue("New value of count1 == 1", count1Val.get() == 1); |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
123 |
assertTrue("Initial value of count2 == 1", count2Val.get() == 1); |
7170 | 124 |
|
125 |
// Make sure that removing a nonexistent listener from an existent MBean produces ListenerNotFoundException |
|
126 |
CountListener count3 = new CountListener(); |
|
127 |
try { |
|
128 |
mbsc.removeNotificationListener(name, count3); |
|
129 |
assertTrue("Remove of nonexistent listener succeeded but should not have", false); |
|
130 |
} catch (ListenerNotFoundException e) { |
|
131 |
// OK: expected |
|
132 |
} |
|
133 |
||
134 |
// Make sure that removing a nonexistent listener from a nonexistent MBean produces ListenerNotFoundException |
|
135 |
ObjectName nonexistent = new ObjectName("foo:bar=baz"); |
|
136 |
assertTrue("Nonexistent is nonexistent", !mbs.isRegistered(nonexistent)); |
|
137 |
try { |
|
138 |
mbsc.removeNotificationListener(nonexistent, count3); |
|
139 |
assertTrue("Remove of listener from nonexistent MBean succeeded but should not have", false); |
|
140 |
} catch (ListenerNotFoundException e) { |
|
141 |
// OK: expected |
|
142 |
} |
|
143 |
||
144 |
// Now unregister our MBean, and check that notifs it sends no longer go anywhere. |
|
145 |
mbs.unregisterMBean(name); |
|
146 |
mbean.sendNotification(notif); |
|
147 |
Thread.sleep(200); |
|
15536
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
148 |
|
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
149 |
assertTrue("New value of count1 == 1", count1Val.get() == 1); |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
150 |
assertTrue("Initial value of count2 == 1", count2Val.get() == 1); |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
151 |
|
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
152 |
// wait for the listener cleanup to take place upon processing notifications |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
153 |
int countdown = 50; // waiting max. 5 secs |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
154 |
while (countdown-- > 0 && |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
155 |
(count1Ref.get() != null || |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
156 |
count2Ref.get() != null)) { |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
157 |
System.gc(); |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
158 |
Thread.sleep(100); |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
159 |
System.gc(); |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
160 |
} |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
161 |
// listener has been removed or the wait has timed out |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
162 |
|
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
163 |
assertTrue("count1 notification listener has not been cleaned up", count1Ref.get() == null); |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
164 |
assertTrue("count2 notification listener has not been cleaned up", count2Ref.get() == null); |
7170 | 165 |
|
166 |
// Check that there is no trace of the listeners any more in ServerNotifForwarder.listenerMap. |
|
167 |
// THIS DEPENDS ON JMX IMPLEMENTATION DETAILS. |
|
168 |
// If the JMX implementation changes, the code here may have to change too. |
|
169 |
Set<?> setForUnreg = listenerMap.get(name); |
|
170 |
assertTrue("No trace of unregistered MBean: " + setForUnreg, setForUnreg == null); |
|
171 |
} |
|
172 |
||
173 |
private static <K, V> Map<K, V> mapWithoutKey(Map<K, V> map, K key) { |
|
174 |
Map<K, V> copy = new HashMap<K, V>(map); |
|
175 |
copy.remove(key); |
|
176 |
return copy; |
|
177 |
} |
|
178 |
||
179 |
public static interface NoddyMBean {} |
|
180 |
||
181 |
public static class Noddy extends NotificationBroadcasterSupport implements NoddyMBean {} |
|
182 |
||
183 |
public static class CountListener implements NotificationListener { |
|
184 |
final AtomicInteger count; |
|
185 |
||
15536
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
186 |
public CountListener(AtomicInteger i) { |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
187 |
count = i; |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
188 |
} |
ac6c0f4a8898
7170447: Intermittent DeadListenerTest.java failure
jbachorik
parents:
7170
diff
changeset
|
189 |
|
7170 | 190 |
public CountListener() { |
191 |
this.count = new AtomicInteger(); |
|
192 |
} |
|
193 |
||
194 |
int count() { |
|
195 |
return count.get(); |
|
196 |
} |
|
197 |
||
198 |
public void handleNotification(Notification notification, Object handback) { |
|
199 |
count.incrementAndGet(); |
|
200 |
} |
|
201 |
} |
|
202 |
||
203 |
private static void assertTrue(String what, boolean cond) { |
|
204 |
if (!cond) { |
|
205 |
throw new AssertionError("Assertion failed: " + what); |
|
206 |
} |
|
207 |
} |
|
208 |
||
209 |
private static class SnoopRMIServerImpl extends RMIJRMPServerImpl { |
|
210 |
final List<RMIConnectionImpl> connections = new ArrayList<RMIConnectionImpl>(); |
|
211 |
SnoopRMIServerImpl() throws IOException { |
|
212 |
super(0, null, null, null); |
|
213 |
} |
|
214 |
||
215 |
@Override |
|
216 |
protected RMIConnection makeClient(String id, Subject subject) throws IOException { |
|
217 |
RMIConnectionImpl conn = (RMIConnectionImpl) super.makeClient(id, subject); |
|
218 |
connections.add(conn); |
|
219 |
return conn; |
|
220 |
} |
|
221 |
} |
|
222 |
} |