blob: 67b3b59e34cb4001255742bd813e7d90e1a37cb0 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
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
34import java.io.IOException;
35import java.net.MalformedURLException;
36import java.util.Collections;
37import java.util.HashMap;
38import java.util.HashSet;
39import java.util.Iterator;
40import java.util.LinkedList;
41import java.util.List;
42import java.util.Map;
43import java.util.Set;
44import java.util.StringTokenizer;
45
46import java.security.Principal;
47import javax.security.auth.Subject;
48
49import javax.management.MBeanServer;
50import javax.management.MBeanServerConnection;
51import javax.management.MBeanServerFactory;
52import javax.management.Notification;
53import javax.management.NotificationListener;
54import javax.management.ObjectName;
55
56import javax.management.remote.JMXAuthenticator;
57import javax.management.remote.JMXConnectionNotification;
58import javax.management.remote.JMXConnector;
59import javax.management.remote.JMXConnectorFactory;
60import javax.management.remote.JMXConnectorServer;
61import javax.management.remote.JMXConnectorServerFactory;
62import javax.management.remote.JMXPrincipal;
63import javax.management.remote.JMXServiceURL;
64
65public 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}