1 /* |
|
2 * Copyright 2008 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 JMXNamespaceViewTest.java |
|
26 * @summary Test the JMXNamespaceView class. |
|
27 * @bug 5072476 |
|
28 * @author Daniel Fuchs |
|
29 * @run clean JMXNamespaceViewTest Wombat WombatMBean |
|
30 * @run build JMXNamespaceViewTest Wombat WombatMBean |
|
31 * @run main JMXNamespaceViewTest |
|
32 */ |
|
33 |
|
34 |
|
35 import java.lang.management.ManagementFactory; |
|
36 import java.net.ServerSocket; |
|
37 import java.rmi.registry.LocateRegistry; |
|
38 import java.util.ArrayList; |
|
39 import java.util.Arrays; |
|
40 import java.util.HashMap; |
|
41 import java.util.HashSet; |
|
42 import java.util.List; |
|
43 import java.util.Map; |
|
44 import java.util.Set; |
|
45 import javax.management.ClientContext; |
|
46 import javax.management.JMException; |
|
47 import javax.management.MBeanRegistration; |
|
48 import javax.management.MBeanServer; |
|
49 import javax.management.MBeanServerFactory; |
|
50 import javax.management.ObjectInstance; |
|
51 import javax.management.ObjectName; |
|
52 import javax.management.namespace.JMXNamespace; |
|
53 import javax.management.namespace.JMXNamespaceView; |
|
54 import javax.management.namespace.JMXNamespaces; |
|
55 import javax.management.namespace.JMXRemoteNamespace; |
|
56 import javax.management.remote.JMXConnectorServer; |
|
57 import javax.management.remote.JMXConnectorServerFactory; |
|
58 import javax.management.remote.JMXServiceURL; |
|
59 |
|
60 /** |
|
61 * A simple test to test the JMXNamespaceViewTest... |
|
62 * @author dfuchs |
|
63 */ |
|
64 public class JMXNamespaceViewTest { |
|
65 |
|
66 /** |
|
67 * Describe the configuration of a namespace |
|
68 */ |
|
69 public static class NamespaceConfig { |
|
70 /** name of the namespace - no // allowed **/ |
|
71 public String name; |
|
72 /** |
|
73 * JMXServiceURL through which the namespace is exported, if it |
|
74 * is a remote namespace. {@code null} if the namespace is local. |
|
75 * This is an inpur URL - eg: new JMXServiceURL("rmi",null,0).toString() |
|
76 * is acceptable here. |
|
77 */ |
|
78 public String jmxurl; |
|
79 /** |
|
80 * Values of the name= key for each WombatMBean contained in the |
|
81 * namespace. |
|
82 */ |
|
83 public String[] wombats; |
|
84 /** list of child namespace **/ |
|
85 public NamespaceConfig[] children; |
|
86 } |
|
87 |
|
88 /** |
|
89 * Creates a NamespaceConfig record for a local namespace. |
|
90 * @param name name of the namespace |
|
91 * @param wombats names of WombatMBean it should contain. |
|
92 * @return a NamespaceConfig. |
|
93 */ |
|
94 public static NamespaceConfig config(String name, String[] wombats) { |
|
95 return config(name,null,wombats); |
|
96 } |
|
97 |
|
98 /** |
|
99 * Creates a NamespaceConfig record for a remote namespace. |
|
100 * @param name name of the namespace |
|
101 * @param jmxurl input JMXServiceURL for creating the JMXConnectorServer |
|
102 * @param wombats names of WombatMBean it should contain. |
|
103 * @return a NamespaceConfig. |
|
104 */ |
|
105 public static NamespaceConfig config(String name, String jmxurl, |
|
106 String[] wombats) { |
|
107 return config(name,jmxurl,wombats,(NamespaceConfig[])null); |
|
108 } |
|
109 |
|
110 /** |
|
111 * Creates a NamespaceConfig record for a local namespace. |
|
112 * @param name name of the namespace |
|
113 * @param wombats names of WombatMBean it should contain. |
|
114 * @param children list of sub namespaces. |
|
115 * @return a NamespaceConfig. |
|
116 */ |
|
117 public static NamespaceConfig config(String name, String[] wombats, |
|
118 NamespaceConfig... children) { |
|
119 return config(name,null,wombats,children); |
|
120 } |
|
121 |
|
122 /** |
|
123 * Creates a NamespaceConfig record for a remote namespace. |
|
124 * @param name name of the namespace |
|
125 * @param jmxurl input JMXServiceURL for creating the JMXConnectorServer |
|
126 * @param wombats names of WombatMBean it should contain. |
|
127 * @param children list of sub namespaces. |
|
128 * @return a NamespaceConfig. |
|
129 */ |
|
130 static NamespaceConfig config(String name, String jmxurl, String[] wombats, |
|
131 NamespaceConfig... children) { |
|
132 final NamespaceConfig cfg = new NamespaceConfig(); |
|
133 cfg.name=name; cfg.jmxurl=jmxurl; cfg.wombats=wombats; |
|
134 cfg.children=children; |
|
135 return cfg; |
|
136 } |
|
137 |
|
138 /** |
|
139 * Returns the given names. This is a utility method to ease code |
|
140 * reading. |
|
141 * @param names names of Wombat MBeans. |
|
142 * @return the given names. |
|
143 */ |
|
144 static String[] wombats(String... names) { |
|
145 return names; |
|
146 } |
|
147 |
|
148 /** |
|
149 * Creates a JMXServiceURL string for the given protocol. |
|
150 * This is also a utility method to ease code reading. |
|
151 * @param protocol The protocol name (e.g. "rmi") |
|
152 * @return A JMXServiceURL string. |
|
153 * @throws Exception if creation of the JMXServiceURL fails. |
|
154 */ |
|
155 static String url(String protocol) throws Exception { |
|
156 return new JMXServiceURL(protocol,null,0).toString(); |
|
157 } |
|
158 |
|
159 /** |
|
160 * Creates a config for a hierarchy of namespaces, mixing local namespaces |
|
161 * and remote namespaces using the given protocol. |
|
162 * @param protocol The protocol that should be used for remote namespaces. |
|
163 * @return A namespace config hierarchy. |
|
164 * @throws java.lang.Exception |
|
165 */ |
|
166 public static NamespaceConfig[] makeConfig(String protocol) |
|
167 throws Exception { |
|
168 final NamespaceConfig[] config = { |
|
169 // Top level namespace "top1" (local) |
|
170 config("top1",wombats("wchief","w1","w2","w3"), |
|
171 // top1//local1 |
|
172 config("local1",wombats("wchief","ww1","ww2")), |
|
173 // top1//local2 |
|
174 config("local2",wombats("wchief","ww4","ww5","ww6"), |
|
175 // top1//local2//local3 |
|
176 config("local3",wombats("wchief","www1","www2")), |
|
177 // top1//local2//rmi1 |
|
178 config("rmi1",url(protocol),wombats("wchief","www3","www4","www5"))), |
|
179 // top1//rmi2 |
|
180 config("rmi2",url(protocol),wombats("wchief","ww7","ww8","ww9"), |
|
181 // top1//rmi2//local4 |
|
182 config("local4",wombats("wchief","www6","www7")), |
|
183 // top1//rmi2//rmi3 |
|
184 config("rmi3",url(protocol),wombats("wchief","www3","www4","www5"), |
|
185 // top1//rmi2//rmi3//local5 |
|
186 config("local5",wombats("wchief","wwww1"))))), |
|
187 // Top level namespace "top2" (local) |
|
188 config("top2",wombats("wchief","w21","w22","w23"), |
|
189 // top2//local21 |
|
190 config("local21",wombats("wchief","ww21","ww22")), |
|
191 // top2//rmi22 |
|
192 config("rmi22",url(protocol),wombats("wchief","ww27","ww28","ww29"), |
|
193 // top2//rmi22//local24 |
|
194 config("local24",wombats("wchief","www26","www27")), |
|
195 // top2//rmi22//rmi23 |
|
196 config("rmi23",url(protocol),wombats("wchief","www23","www24","www25"), |
|
197 // top2//rmi22//rmi23//local25 |
|
198 config("local25",wombats("wchief","wwww21"))))), |
|
199 // Top level namespace "top3" (remote) |
|
200 config("top3",url(protocol),wombats("wchief","w31","w32","w33"), |
|
201 // top3//local31 |
|
202 config("local31",wombats("wchief","ww31","ww32")), |
|
203 // top3//rmi32 |
|
204 config("rmi32",url(protocol),wombats("wchief","ww37","ww38","ww39"), |
|
205 // top3//rmi32//local34 |
|
206 config("local34",wombats("wchief","www36","www37")), |
|
207 // top3//rmi32//rmi33 |
|
208 config("rmi33",url(protocol),wombats("wchief","www33","www34","www35"), |
|
209 // top3//rmi32//local35 |
|
210 config("local35",wombats("wchief","wwww31"))))), |
|
211 }; |
|
212 return config; |
|
213 } |
|
214 |
|
215 /** |
|
216 * Close all connector servers in the list. |
|
217 * @param cslist List of connector servers to close. |
|
218 */ |
|
219 public static void closeAll(List<JMXConnectorServer> cslist) { |
|
220 for (JMXConnectorServer cs : cslist) { |
|
221 try { |
|
222 cs.stop(); |
|
223 } catch (Exception xx) { |
|
224 System.err.println("Failed to stop connector: " + xx); |
|
225 } |
|
226 } |
|
227 } |
|
228 |
|
229 public static class MBeanServerConfigCreator { |
|
230 public MBeanServer createMBeanServerFor(NamespaceConfig config) { |
|
231 return MBeanServerFactory.newMBeanServer(); |
|
232 } |
|
233 } |
|
234 |
|
235 /** |
|
236 * Load the given namespace configuration inside the given MBeanServer. |
|
237 * Return a list of connector servers created in the process. |
|
238 * @param server The MBeanServer in which the namespaces must |
|
239 * be created. |
|
240 * @param namespaces The list of namespaces to create. |
|
241 * @return a list of started connector servers. |
|
242 * @throws java.lang.Exception failed to create the specified namespaces. |
|
243 */ |
|
244 public static List<JMXConnectorServer> load(MBeanServer server, |
|
245 MBeanServerConfigCreator factory, |
|
246 NamespaceConfig... namespaces) throws Exception { |
|
247 final List<JMXConnectorServer> cslist = |
|
248 new ArrayList<JMXConnectorServer>(); |
|
249 try { |
|
250 final ObjectName creator = |
|
251 new ObjectName("jmx.creator:type=JMXNamespaceCreator"); |
|
252 if (System.getProperty("jmx.wait")!=null |
|
253 && !server.isRegistered(creator)) { |
|
254 server.registerMBean(new JMXNamespaceCreator(),creator); |
|
255 } |
|
256 for (NamespaceConfig cfg : namespaces) { |
|
257 final MBeanServer srv = factory.createMBeanServerFor(cfg); |
|
258 if (System.getProperty("jmx.wait")!=null |
|
259 && !srv.isRegistered(creator)) { |
|
260 srv.registerMBean(new JMXNamespaceCreator(),creator); |
|
261 } |
|
262 if (cfg.wombats != null) { |
|
263 for (String w : cfg.wombats) { |
|
264 final ObjectName n = |
|
265 new ObjectName("wombat:type=Wombat,name=" + w); |
|
266 final WombatMBean ww = new Wombat(); |
|
267 srv.registerMBean(ww, n); |
|
268 } |
|
269 } |
|
270 if (cfg.children != null) { |
|
271 cslist.addAll(load(srv, factory, cfg.children)); |
|
272 } |
|
273 JMXNamespace nm; |
|
274 if (cfg.jmxurl == null) { |
|
275 nm = new JMXNamespace(srv); |
|
276 } else { |
|
277 JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(new JMXServiceURL(cfg.jmxurl), |
|
278 null, srv); |
|
279 srv.registerMBean(cs, |
|
280 new ObjectName("jmx.remote:type=JMXConnectorServer")); |
|
281 cs.start(); |
|
282 cslist.add(cs); |
|
283 nm = JMXRemoteNamespace. |
|
284 newJMXRemoteNamespace(cs.getAddress(), |
|
285 null); |
|
286 } |
|
287 server.registerMBean(nm, |
|
288 JMXNamespaces.getNamespaceObjectName(cfg.name)); |
|
289 if (nm instanceof JMXRemoteNamespace) { |
|
290 server.invoke( |
|
291 JMXNamespaces.getNamespaceObjectName(cfg.name), |
|
292 "connect", null, null); |
|
293 } |
|
294 } |
|
295 } catch (Exception x) { |
|
296 closeAll(cslist); |
|
297 throw x; |
|
298 } |
|
299 return cslist; |
|
300 } |
|
301 |
|
302 /** |
|
303 * Add an entry {@code <path,NamespaceConfig>} in the map for the given |
|
304 * namespace and its subnamespaces. |
|
305 * @param map A {@code Map<path,NamespaceConfig>}. |
|
306 * @param parent The path of the parent workspace. |
|
307 * @param cfg The NamespaceConfig hierarchy to index in the map. |
|
308 */ |
|
309 public static void fillMap(Map<String,NamespaceConfig> map, String parent, |
|
310 NamespaceConfig cfg) { |
|
311 |
|
312 final String where; |
|
313 if (parent == null || parent.equals("")) |
|
314 where=cfg.name; |
|
315 else |
|
316 where=parent+JMXNamespaces.NAMESPACE_SEPARATOR+cfg.name; |
|
317 map.put(where,cfg); |
|
318 if (cfg.children==null) return; |
|
319 for(NamespaceConfig child:cfg.children) { |
|
320 fillMap(map,where,child); |
|
321 } |
|
322 } |
|
323 |
|
324 /** |
|
325 * Compare a list of namespace names obtained from JMXNamespaceView.list() |
|
326 * with the expected clildren list of the corresponding NamespaceConfig. |
|
327 * @param list A list of namespace names |
|
328 * @param children A list of NamespaceConfig correspondng to expected |
|
329 * namespace. |
|
330 * @param fail If true and the comparison yields false, throws an |
|
331 * exception instead of simply returning false. |
|
332 * @return true if OK, false if NOK. |
|
333 */ |
|
334 private static boolean compare(String[] list, NamespaceConfig[] children, |
|
335 boolean fail) { |
|
336 final List<String> found = new ArrayList<String>(Arrays.asList(list)); |
|
337 if (found.contains(ClientContext.NAMESPACE)) |
|
338 found.remove(ClientContext.NAMESPACE); |
|
339 |
|
340 if (children == null && found.size()==0) return true; |
|
341 if (children == null && fail == false) return false; |
|
342 if (children == null) throw new RuntimeException( |
|
343 "No child expected. Found "+Arrays.toString(list)); |
|
344 final Set<String> names = new HashSet<String>(); |
|
345 for (NamespaceConfig cfg : children) { |
|
346 names.add(cfg.name); |
|
347 if (found.contains(cfg.name)) continue; |
|
348 if (!fail) return false; |
|
349 throw new RuntimeException(cfg.name+" not found in "+ |
|
350 found); |
|
351 } |
|
352 found.removeAll(names); |
|
353 if (found.size()==0) return true; |
|
354 if (fail==false) return false; |
|
355 throw new RuntimeException("found additional namespaces: "+ |
|
356 found); |
|
357 } |
|
358 |
|
359 /** |
|
360 * Compares the result of queryNames(null,null) with a set of expected |
|
361 * wombats. |
|
362 * @param where The path of the namespace that was queried. |
|
363 * @param list The set of ObjectNames found. |
|
364 * @param wombats The expected list of wombats. |
|
365 * @param fail If true and the comparison yields false, throws an |
|
366 * exception instead of simply returning false. |
|
367 * @return true if OK, false if NOK. |
|
368 * @throws java.lang.Exception something went wrong. |
|
369 */ |
|
370 private static boolean compare(String where, |
|
371 Set<ObjectName>list, String[] wombats, |
|
372 boolean fail) throws Exception { |
|
373 final Set<ObjectName> found = new HashSet<ObjectName>(); |
|
374 final Set<ObjectName> expected = new HashSet<ObjectName>(); |
|
375 for (ObjectName n : list) { |
|
376 if ("Wombat".equals(n.getKeyProperty("type"))) |
|
377 found.add(n); |
|
378 } |
|
379 for(String w : wombats) { |
|
380 final ObjectName n = |
|
381 new ObjectName("wombat:type=Wombat,name=" + w); |
|
382 expected.add(n); |
|
383 if (found.contains(n)) continue; |
|
384 if (fail == false) return false; |
|
385 throw new RuntimeException(where+ |
|
386 ": Wombat "+w+" not found in "+found); |
|
387 } |
|
388 found.removeAll(expected); |
|
389 if (found.size()==0) { |
|
390 System.out.println(where+": found all expected: "+expected); |
|
391 return true; |
|
392 } |
|
393 if (fail==false) return false; |
|
394 throw new RuntimeException(where+": found additional MBeans: "+ |
|
395 found); |
|
396 } |
|
397 |
|
398 /** |
|
399 * A generic test to test JMXNamespaceView over a namespace configuration. |
|
400 * @param server The MBeanServer in which to load the namespace |
|
401 * config. |
|
402 * @param namespaces The namespace config to run the test over... |
|
403 * @throws java.lang.Exception |
|
404 */ |
|
405 public static void doTest(MBeanServer server, NamespaceConfig... namespaces) |
|
406 throws Exception { |
|
407 List<JMXConnectorServer> cslist = load(server, |
|
408 new MBeanServerConfigCreator(), namespaces); |
|
409 Map<String,NamespaceConfig> inputMap = |
|
410 new HashMap<String,NamespaceConfig>(); |
|
411 |
|
412 for (NamespaceConfig cfg : namespaces) { |
|
413 fillMap(inputMap,"",cfg); |
|
414 } |
|
415 try { |
|
416 final JMXNamespaceView root = new JMXNamespaceView(server); |
|
417 List<JMXNamespaceView> vlist = new ArrayList<JMXNamespaceView>(); |
|
418 vlist.add(root); |
|
419 |
|
420 while (!vlist.isEmpty()) { |
|
421 JMXNamespaceView v = vlist.remove(0); |
|
422 final String where = v.isRoot()?"root":v.where(); |
|
423 System.out.println(where+": "+ |
|
424 v.getMBeanServerConnection().queryNames(null,null)); |
|
425 for (String ns : v.list()) { |
|
426 final JMXNamespaceView down = v.down(ns); |
|
427 vlist.add(down); |
|
428 if (!down.where().equals(v.isRoot()?ns:where+ |
|
429 JMXNamespaces.NAMESPACE_SEPARATOR+ns)) { |
|
430 throw new RuntimeException("path of "+down.where()+ |
|
431 " should be "+(v.isRoot()?ns:where+ |
|
432 JMXNamespaces.NAMESPACE_SEPARATOR+ns)); |
|
433 } |
|
434 if (down.up().equals(v)) continue; |
|
435 throw new RuntimeException("parent of "+down.where()+ |
|
436 " should be "+where); |
|
437 } |
|
438 final NamespaceConfig[] children; |
|
439 final NamespaceConfig cfg; |
|
440 if (v.isRoot()) { |
|
441 children=namespaces; |
|
442 cfg = null; |
|
443 } else { |
|
444 cfg = inputMap.get(where); |
|
445 children = cfg==null?null:cfg.children; |
|
446 } |
|
447 compare(v.list(),children,true); |
|
448 if (!v.isRoot()) { |
|
449 if (where.endsWith(ClientContext.NAMESPACE)) { |
|
450 System.out.println(where+": skipping queryNames analysis"); |
|
451 continue; |
|
452 } |
|
453 //System.out.println(where+": cfg is: "+cfg); |
|
454 compare(where,v.getMBeanServerConnection(). |
|
455 queryNames(null, null),cfg.wombats,true); |
|
456 } |
|
457 } |
|
458 |
|
459 exportAndWaitIfNeeded(server); |
|
460 } finally { |
|
461 closeAll(cslist); |
|
462 } |
|
463 } |
|
464 |
|
465 public static interface JMXNamespaceCreatorMBean { |
|
466 public ObjectInstance createLocalNamespace(String namespace) |
|
467 throws JMException ; |
|
468 public void removeLocalNamespace(String namespace) |
|
469 throws JMException; |
|
470 } |
|
471 |
|
472 public static class JMXNamespaceCreator |
|
473 implements MBeanRegistration, |
|
474 JMXNamespaceCreatorMBean { |
|
475 |
|
476 private volatile MBeanServer mbeanServer; |
|
477 |
|
478 public ObjectInstance createLocalNamespace(String namespace) |
|
479 throws JMException { |
|
480 return mbeanServer.registerMBean( |
|
481 new JMXNamespace(MBeanServerFactory.newMBeanServer()), |
|
482 JMXNamespaces.getNamespaceObjectName(namespace)); |
|
483 } |
|
484 |
|
485 public void removeLocalNamespace(String namespace) |
|
486 throws JMException { |
|
487 mbeanServer.unregisterMBean( |
|
488 JMXNamespaces.getNamespaceObjectName(namespace)); |
|
489 } |
|
490 |
|
491 public ObjectName preRegister(MBeanServer server, ObjectName name) |
|
492 throws Exception { |
|
493 mbeanServer = server; |
|
494 return name; |
|
495 } |
|
496 |
|
497 public void postRegister(Boolean registrationDone) { |
|
498 } |
|
499 |
|
500 public void preDeregister() throws Exception { |
|
501 } |
|
502 |
|
503 public void postDeregister() { |
|
504 } |
|
505 |
|
506 } |
|
507 |
|
508 public static void exportAndWaitIfNeeded(MBeanServer server) |
|
509 throws Exception { |
|
510 if (System.getProperty("jmx.wait")!=null) { |
|
511 final int port = getPortFor("rmi"); |
|
512 LocateRegistry.createRegistry(port); |
|
513 final JMXServiceURL url = |
|
514 new JMXServiceURL("rmi",null,port, |
|
515 "/jndi/rmi://localhost:"+port+"/jmxrmi"); |
|
516 final JMXConnectorServer cs = |
|
517 JMXConnectorServerFactory. |
|
518 newJMXConnectorServer(url, null, server); |
|
519 cs.start(); |
|
520 try { |
|
521 System.out.println("RMI Server waiting at: "+cs.getAddress()); |
|
522 System.in.read(); |
|
523 } finally { |
|
524 cs.stop(); |
|
525 } |
|
526 } |
|
527 } |
|
528 |
|
529 public static int getPortFor(String protocol) throws Exception { |
|
530 final int aport = |
|
531 Integer.valueOf(System.getProperty("jmx."+protocol+".port","0")); |
|
532 if (aport > 0) return aport; |
|
533 final ServerSocket s = new ServerSocket(0); |
|
534 try { |
|
535 final int port = s.getLocalPort(); |
|
536 return port; |
|
537 } finally { |
|
538 s.close(); |
|
539 } |
|
540 } |
|
541 |
|
542 public static void main(String[] args) throws Exception { |
|
543 doTest(ManagementFactory.getPlatformMBeanServer(),makeConfig("rmi")); |
|
544 } |
|
545 |
|
546 } |
|