|
1 /* |
|
2 * Copyright 2003 Sun Microsystems, Inc. All Rights Reserved. |
|
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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
|
20 * CA 95054 USA or visit www.sun.com if you need additional information or |
|
21 * have any questions. |
|
22 */ |
|
23 |
|
24 /* |
|
25 * @test |
|
26 * @bug 4865397 |
|
27 * @summary Tests remote JMX connections |
|
28 * @author Eamonn McManus |
|
29 * @run clean ConnectionTest |
|
30 * @run build ConnectionTest |
|
31 * @run main ConnectionTest |
|
32 */ |
|
33 |
|
34 import java.io.IOException; |
|
35 import java.net.MalformedURLException; |
|
36 import java.util.Collections; |
|
37 import java.util.HashMap; |
|
38 import java.util.HashSet; |
|
39 import java.util.Iterator; |
|
40 import java.util.LinkedList; |
|
41 import java.util.List; |
|
42 import java.util.Map; |
|
43 import java.util.Set; |
|
44 import java.util.StringTokenizer; |
|
45 |
|
46 import java.security.Principal; |
|
47 import javax.security.auth.Subject; |
|
48 |
|
49 import javax.management.MBeanServer; |
|
50 import javax.management.MBeanServerConnection; |
|
51 import javax.management.MBeanServerFactory; |
|
52 import javax.management.Notification; |
|
53 import javax.management.NotificationListener; |
|
54 import javax.management.ObjectName; |
|
55 |
|
56 import javax.management.remote.JMXAuthenticator; |
|
57 import javax.management.remote.JMXConnectionNotification; |
|
58 import javax.management.remote.JMXConnector; |
|
59 import javax.management.remote.JMXConnectorFactory; |
|
60 import javax.management.remote.JMXConnectorServer; |
|
61 import javax.management.remote.JMXConnectorServerFactory; |
|
62 import javax.management.remote.JMXPrincipal; |
|
63 import javax.management.remote.JMXServiceURL; |
|
64 |
|
65 public class ConnectionTest { |
|
66 |
|
67 public static void main(String[] args) { |
|
68 // System.setProperty("java.util.logging.config.file", |
|
69 // "../../../../logging.properties"); |
|
70 // // we are in <workspace>/build/test/JTwork/scratch |
|
71 // java.util.logging.LogManager.getLogManager().readConfiguration(); |
|
72 boolean ok = true; |
|
73 String[] protocols = {"rmi", "iiop", "jmxmp"}; |
|
74 if (args.length > 0) |
|
75 protocols = args; |
|
76 for (int i = 0; i < protocols.length; i++) { |
|
77 final String proto = protocols[i]; |
|
78 System.out.println("Testing for protocol " + proto); |
|
79 try { |
|
80 ok &= test(proto); |
|
81 } catch (Exception e) { |
|
82 System.err.println("Unexpected exception: " + e); |
|
83 e.printStackTrace(); |
|
84 ok = false; |
|
85 } |
|
86 } |
|
87 |
|
88 if (ok) |
|
89 System.out.println("Test passed"); |
|
90 else { |
|
91 System.out.println("TEST FAILED"); |
|
92 System.exit(1); |
|
93 } |
|
94 } |
|
95 |
|
96 private static boolean test(String proto) throws Exception { |
|
97 ObjectName serverName = ObjectName.getInstance("d:type=server"); |
|
98 MBeanServer mbs = MBeanServerFactory.newMBeanServer(); |
|
99 JMXAuthenticator authenticator = new BogusAuthenticator(); |
|
100 Map env = Collections.singletonMap("jmx.remote.authenticator", |
|
101 authenticator); |
|
102 JMXServiceURL url = new JMXServiceURL("service:jmx:" + proto + "://"); |
|
103 JMXConnectorServer server; |
|
104 try { |
|
105 server = |
|
106 JMXConnectorServerFactory.newJMXConnectorServer(url, env, |
|
107 null); |
|
108 } catch (MalformedURLException e) { |
|
109 System.out.println("Protocol " + proto + |
|
110 " not supported, ignoring"); |
|
111 return true; |
|
112 } |
|
113 System.out.println("Created connector server"); |
|
114 mbs.registerMBean(server, serverName); |
|
115 System.out.println("Registered connector server in MBean server"); |
|
116 mbs.addNotificationListener(serverName, logListener, null, null); |
|
117 mbs.invoke(serverName, "start", null, null); |
|
118 System.out.println("Started connector server"); |
|
119 JMXServiceURL address = |
|
120 (JMXServiceURL) mbs.getAttribute(serverName, "Address"); |
|
121 System.out.println("Retrieved address: " + address); |
|
122 |
|
123 if (address.getHost().length() == 0) { |
|
124 System.out.println("Generated address has empty hostname"); |
|
125 return false; |
|
126 } |
|
127 |
|
128 JMXConnector client = JMXConnectorFactory.connect(address); |
|
129 System.out.println("Client connected"); |
|
130 |
|
131 String clientConnId = client.getConnectionId(); |
|
132 System.out.println("Got connection ID on client: " + clientConnId); |
|
133 boolean ok = checkConnectionId(proto, clientConnId); |
|
134 if (!ok) |
|
135 return false; |
|
136 System.out.println("Connection ID is OK"); |
|
137 |
|
138 // 4901826: connection ids need some time to be updated using jmxmp |
|
139 // we don't get the notif immediately either |
|
140 // this was originally timeout 1ms, which was not enough |
|
141 Notification notif = waitForNotification(1000); |
|
142 System.out.println("Server got notification: " + notif); |
|
143 |
|
144 ok = mustBeConnectionNotification(notif, clientConnId, |
|
145 JMXConnectionNotification.OPENED); |
|
146 if (!ok) |
|
147 return false; |
|
148 |
|
149 client.close(); |
|
150 System.out.println("Closed client"); |
|
151 |
|
152 notif = waitForNotification(1000); |
|
153 System.out.println("Got notification: " + notif); |
|
154 |
|
155 ok = mustBeConnectionNotification(notif, clientConnId, |
|
156 JMXConnectionNotification.CLOSED); |
|
157 if (!ok) |
|
158 return false; |
|
159 |
|
160 client = JMXConnectorFactory.connect(address); |
|
161 System.out.println("Second client connected"); |
|
162 |
|
163 String clientConnId2 = client.getConnectionId(); |
|
164 if (clientConnId.equals(clientConnId2)) { |
|
165 System.out.println("Same connection ID for two connections: " + |
|
166 clientConnId2); |
|
167 return false; |
|
168 } |
|
169 System.out.println("Second client connection ID is different"); |
|
170 |
|
171 notif = waitForNotification(1); |
|
172 ok = mustBeConnectionNotification(notif, clientConnId2, |
|
173 JMXConnectionNotification.OPENED); |
|
174 if (!ok) |
|
175 return false; |
|
176 |
|
177 MBeanServerConnection mbsc = client.getMBeanServerConnection(); |
|
178 Map attrs = (Map) mbsc.getAttribute(serverName, "Attributes"); |
|
179 System.out.println("Server attributes received by client: " + attrs); |
|
180 |
|
181 server.stop(); |
|
182 System.out.println("Server stopped"); |
|
183 |
|
184 notif = waitForNotification(1000); |
|
185 System.out.println("Server got connection-closed notification: " + |
|
186 notif); |
|
187 |
|
188 ok = mustBeConnectionNotification(notif, clientConnId2, |
|
189 JMXConnectionNotification.CLOSED); |
|
190 if (!ok) |
|
191 return false; |
|
192 |
|
193 try { |
|
194 mbsc.getDefaultDomain(); |
|
195 System.out.println("Connection still working but should not be"); |
|
196 return false; |
|
197 } catch (IOException e) { |
|
198 System.out.println("Connection correctly got exception: " + e); |
|
199 } |
|
200 |
|
201 try { |
|
202 client = JMXConnectorFactory.connect(address); |
|
203 System.out.println("Connector server still working but should " + |
|
204 "not be"); |
|
205 return false; |
|
206 } catch (IOException e) { |
|
207 System.out.println("New connection correctly got exception: " + e); |
|
208 } |
|
209 |
|
210 return true; |
|
211 } |
|
212 |
|
213 private static boolean |
|
214 mustBeConnectionNotification(Notification notif, |
|
215 String requiredConnId, |
|
216 String requiredType) { |
|
217 |
|
218 if (!(notif instanceof JMXConnectionNotification)) { |
|
219 System.out.println("Should have been a " + |
|
220 "JMXConnectionNotification: " + |
|
221 notif.getClass()); |
|
222 return false; |
|
223 } |
|
224 |
|
225 JMXConnectionNotification cnotif = (JMXConnectionNotification) notif; |
|
226 if (!cnotif.getType().equals(requiredType)) { |
|
227 System.out.println("Wrong type notif: is \"" + cnotif.getType() + |
|
228 "\", should be \"" + requiredType + "\""); |
|
229 return false; |
|
230 } |
|
231 |
|
232 if (!cnotif.getConnectionId().equals(requiredConnId)) { |
|
233 System.out.println("Wrong connection id: is \"" + |
|
234 cnotif.getConnectionId() + "\", should be \"" + |
|
235 requiredConnId); |
|
236 return false; |
|
237 } |
|
238 |
|
239 return true; |
|
240 } |
|
241 |
|
242 private static boolean checkConnectionId(String proto, String clientConnId) |
|
243 throws Exception { |
|
244 StringTokenizer tok = new StringTokenizer(clientConnId, " ", true); |
|
245 String s; |
|
246 s = tok.nextToken(); |
|
247 if (!s.startsWith(proto + ":")) { |
|
248 System.out.println("Expected \"" + proto + ":\", found \"" + s + |
|
249 "\""); |
|
250 return false; |
|
251 } |
|
252 s = tok.nextToken(); |
|
253 if (!s.equals(" ")) { |
|
254 System.out.println("Expected \" \", found \"" + s + "\""); |
|
255 return false; |
|
256 } |
|
257 s = tok.nextToken(); |
|
258 StringTokenizer tok2 = new StringTokenizer(s, ";", true); |
|
259 Set principalNames = new HashSet(); |
|
260 String s2; |
|
261 s2 = tok2.nextToken(); |
|
262 if (s2.equals(";")) { |
|
263 System.out.println("In identity \"" + s + |
|
264 "\", expected name, found \";\""); |
|
265 return false; |
|
266 } |
|
267 principalNames.add(s2); |
|
268 s2 = tok2.nextToken(); |
|
269 if (!s2.equals(";")) |
|
270 throw new Exception("Can't happen"); |
|
271 s2 = tok2.nextToken(); |
|
272 if (s2.equals(";")) { |
|
273 System.out.println("In identity \"" + s + |
|
274 "\", expected name, found \";\""); |
|
275 return false; |
|
276 } |
|
277 principalNames.add(s2); |
|
278 if (tok2.hasMoreTokens()) { |
|
279 System.out.println("In identity \"" + s + "\", too many tokens"); |
|
280 return false; |
|
281 } |
|
282 if (principalNames.size() != bogusPrincipals.size()) { |
|
283 System.out.println("Wrong number of principal names: " + |
|
284 principalNames.size() + " != " + |
|
285 bogusPrincipals.size()); |
|
286 return false; |
|
287 } |
|
288 for (Iterator it = bogusPrincipals.iterator(); it.hasNext(); ) { |
|
289 Principal p = (Principal) it.next(); |
|
290 if (!principalNames.contains(p.getName())) { |
|
291 System.out.println("Principal names don't contain \"" + |
|
292 p.getName() + "\""); |
|
293 return false; |
|
294 } |
|
295 } |
|
296 s = tok.nextToken(); |
|
297 if (!s.equals(" ")) { |
|
298 System.out.println("Expected \" \", found \"" + s + "\""); |
|
299 return false; |
|
300 } |
|
301 return true; |
|
302 } |
|
303 |
|
304 private static Notification waitForNotification(long timeout) |
|
305 throws InterruptedException { |
|
306 synchronized (log) { |
|
307 if (log.isEmpty()) { |
|
308 long remainingTime = timeout; |
|
309 final long startTime = System.currentTimeMillis(); |
|
310 |
|
311 while (log.isEmpty() && remainingTime >0) { |
|
312 log.wait(remainingTime); |
|
313 remainingTime = timeout - (System.currentTimeMillis() - startTime); |
|
314 } |
|
315 |
|
316 if (log.isEmpty()) { |
|
317 throw new InterruptedException("Timed out waiting for " + |
|
318 "notification!"); |
|
319 } |
|
320 } |
|
321 return (Notification) log.remove(0); |
|
322 } |
|
323 } |
|
324 |
|
325 private static class LogListener implements NotificationListener { |
|
326 LogListener(List log) { |
|
327 this.log = log; |
|
328 } |
|
329 |
|
330 public void handleNotification(Notification n, Object h) { |
|
331 synchronized (log) { |
|
332 log.add(n); |
|
333 log.notifyAll(); |
|
334 } |
|
335 } |
|
336 |
|
337 private final List log; |
|
338 } |
|
339 |
|
340 private static List log = new LinkedList(); |
|
341 private static NotificationListener logListener = new LogListener(log); |
|
342 |
|
343 private static class BogusAuthenticator implements JMXAuthenticator { |
|
344 public Subject authenticate(Object credentials) { |
|
345 Subject subject = |
|
346 new Subject(true, bogusPrincipals, |
|
347 Collections.EMPTY_SET, Collections.EMPTY_SET); |
|
348 System.out.println("Authenticator returns: " + subject); |
|
349 return subject; |
|
350 } |
|
351 } |
|
352 |
|
353 private static final Set bogusPrincipals = new HashSet(); |
|
354 static { |
|
355 bogusPrincipals.add(new JMXPrincipal("foo")); |
|
356 bogusPrincipals.add(new JMXPrincipal("bar")); |
|
357 } |
|
358 } |