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 LazyDomainTest.java |
|
26 * @bug 5072476 |
|
27 * @summary Basic test for Lazy Domains. |
|
28 * @author Daniel Fuchs |
|
29 * @run clean LazyDomainTest Wombat WombatMBean |
|
30 * @run build LazyDomainTest Wombat WombatMBean |
|
31 * @run main LazyDomainTest |
|
32 */ |
|
33 |
|
34 |
|
35 import java.lang.management.ClassLoadingMXBean; |
|
36 import java.lang.management.ManagementFactory; |
|
37 import java.lang.reflect.InvocationHandler; |
|
38 import java.lang.reflect.InvocationTargetException; |
|
39 import java.lang.reflect.Method; |
|
40 import java.lang.reflect.Proxy; |
|
41 import java.util.Arrays; |
|
42 import java.util.Collections; |
|
43 import java.util.HashMap; |
|
44 import java.util.HashSet; |
|
45 import java.util.concurrent.ArrayBlockingQueue; |
|
46 import java.util.concurrent.BlockingQueue; |
|
47 import java.util.concurrent.TimeUnit; |
|
48 import java.util.Map; |
|
49 import java.util.Set; |
|
50 import javax.management.JMX; |
|
51 import javax.management.ListenerNotFoundException; |
|
52 import javax.management.MBeanServer; |
|
53 import javax.management.MBeanServerBuilder; |
|
54 import javax.management.MBeanServerDelegate; |
|
55 import javax.management.MBeanServerFactory; |
|
56 import javax.management.MBeanServerNotification; |
|
57 import javax.management.Notification; |
|
58 import javax.management.NotificationFilter; |
|
59 import javax.management.NotificationListener; |
|
60 import javax.management.ObjectName; |
|
61 import javax.management.namespace.JMXDomain; |
|
62 import javax.management.remote.MBeanServerForwarder; |
|
63 |
|
64 /** |
|
65 * Test simple creation/registration of namespace. |
|
66 * |
|
67 */ |
|
68 public class LazyDomainTest { |
|
69 private static Map<String,Object> emptyEnvMap() { |
|
70 return Collections.emptyMap(); |
|
71 } |
|
72 |
|
73 |
|
74 public static interface MBeanServerLoader { |
|
75 public MBeanServer loadMBeanServer(); |
|
76 } |
|
77 |
|
78 |
|
79 public static class MBeanServerProxy implements InvocationHandler { |
|
80 |
|
81 private final static Map<Method,Method> localMap; |
|
82 static { |
|
83 localMap = new HashMap<Method, Method>(); |
|
84 for (Method m : MBeanServerForwarder.class.getDeclaredMethods()) { |
|
85 try { |
|
86 final Method loc = MBeanServerProxy.class. |
|
87 getMethod(m.getName(), m.getParameterTypes()); |
|
88 localMap.put(m, loc); |
|
89 } catch (Exception x) { |
|
90 // not defined... |
|
91 } |
|
92 } |
|
93 try { |
|
94 localMap.put(MBeanServer.class. |
|
95 getMethod("getMBeanCount", (Class[]) null), |
|
96 MBeanServerProxy.class. |
|
97 getMethod("getMBeanCount", (Class[]) null)); |
|
98 } catch (NoSuchMethodException x) { |
|
99 // OK. |
|
100 } |
|
101 } |
|
102 |
|
103 private final MBeanServerLoader loader; |
|
104 private MBeanServer server; |
|
105 private final Set<LazyDomain> domains; |
|
106 |
|
107 public MBeanServerProxy(MBeanServerLoader loader) { |
|
108 if (loader == null) |
|
109 throw new IllegalArgumentException("null loader"); |
|
110 this.loader = loader; |
|
111 this.server = null; |
|
112 domains = new HashSet<LazyDomain>(); |
|
113 } |
|
114 |
|
115 |
|
116 public Object invoke(Object proxy, Method method, Object[] args) |
|
117 throws Throwable { |
|
118 if (method.getDeclaringClass().equals(Object.class)) { |
|
119 return invokeMethod(this,method,args); |
|
120 } |
|
121 final Method local = localMap.get(method); |
|
122 if (local != null) { |
|
123 return invokeMethod(this,local,args); |
|
124 } |
|
125 if (method.getDeclaringClass().equals(MBeanServer.class)) { |
|
126 return invokeMethod(getMBeanServer(),method,args); |
|
127 } |
|
128 throw new NoSuchMethodException(method.getName()); |
|
129 } |
|
130 |
|
131 private Object invokeMethod(Object on, Method method, Object[] args) |
|
132 throws Throwable { |
|
133 try { |
|
134 return method.invoke(on, args); |
|
135 } catch (InvocationTargetException ex) { |
|
136 throw ex.getTargetException(); |
|
137 } |
|
138 } |
|
139 |
|
140 public synchronized MBeanServer getMBeanServer() { |
|
141 if (server == null) setMBeanServer(loader.loadMBeanServer()); |
|
142 return server; |
|
143 } |
|
144 |
|
145 public synchronized void setMBeanServer(MBeanServer mbs) { |
|
146 this.server = mbs; |
|
147 if (mbs != null) { |
|
148 for (LazyDomain dom : domains) dom.loaded(); |
|
149 domains.clear(); |
|
150 } |
|
151 } |
|
152 |
|
153 public synchronized boolean isLoaded() { |
|
154 return server != null; |
|
155 } |
|
156 |
|
157 public synchronized void add(LazyDomain dom) { |
|
158 if (isLoaded()) dom.loaded(); |
|
159 else domains.add(dom); |
|
160 } |
|
161 |
|
162 public synchronized boolean remove(LazyDomain dom) { |
|
163 return domains.remove(dom); |
|
164 } |
|
165 |
|
166 public Integer getMBeanCount() { |
|
167 if (isLoaded()) return server.getMBeanCount(); |
|
168 else return Integer.valueOf(0); |
|
169 } |
|
170 } |
|
171 |
|
172 public static class LazyDomain extends JMXDomain { |
|
173 public static MBeanServer makeProxyFor(MBeanServerProxy proxy) { |
|
174 return (MBeanServer) |
|
175 Proxy.newProxyInstance(LazyDomain.class.getClassLoader(), |
|
176 new Class[] {MBeanServer.class, MBeanServerForwarder.class}, |
|
177 proxy); |
|
178 } |
|
179 |
|
180 private final MBeanServerProxy proxy; |
|
181 private volatile NotificationListener listener; |
|
182 private volatile NotificationFilter filter; |
|
183 |
|
184 public LazyDomain(MBeanServerProxy proxy) { |
|
185 super(makeProxyFor(proxy)); |
|
186 this.proxy = proxy; |
|
187 } |
|
188 |
|
189 @Override |
|
190 public Integer getMBeanCount() { |
|
191 if (proxy.isLoaded()) |
|
192 return super.getMBeanCount(); |
|
193 return 0; |
|
194 } |
|
195 |
|
196 |
|
197 @Override |
|
198 public synchronized void addMBeanServerNotificationListener( |
|
199 NotificationListener listener, |
|
200 NotificationFilter filter) { |
|
201 if (proxy.isLoaded()) { |
|
202 super.addMBeanServerNotificationListener(listener, filter); |
|
203 } else { |
|
204 this.listener = listener; |
|
205 this.filter = filter; |
|
206 proxy.add(this); |
|
207 } |
|
208 } |
|
209 |
|
210 @Override |
|
211 public synchronized void removeMBeanServerNotificationListener( |
|
212 NotificationListener listener) |
|
213 throws ListenerNotFoundException { |
|
214 if (this.listener != listener) |
|
215 throw new ListenerNotFoundException(); |
|
216 this.listener = null; |
|
217 this.filter = null; |
|
218 if (proxy.isLoaded()) |
|
219 super.removeMBeanServerNotificationListener(listener); |
|
220 proxy.remove(this); |
|
221 } |
|
222 |
|
223 public synchronized void loaded() { |
|
224 if (listener != null) |
|
225 addMBeanServerNotificationListener(listener, filter); |
|
226 } |
|
227 |
|
228 } |
|
229 |
|
230 /** |
|
231 * This is a use case for e.g GlassFish: the LazyStarterDomain MBean |
|
232 * is a place holder that will unregister itself and autoload a set |
|
233 * of MBeans in place of its own domain when that domain is |
|
234 * accessed. |
|
235 * This is an abstract class, where the only abstract method |
|
236 * is loadMBeans(MBeanServer). |
|
237 * Subclasses should implement that method to register whatever MBeans |
|
238 * in the domain previously held by that LazyStarterDomain object. |
|
239 * In other words: the LazyStarterDomain MBean is 'replaced' by the |
|
240 * MBeans loaded by loadMBeans(); |
|
241 */ |
|
242 public static abstract class LazyStarterDomain extends LazyDomain { |
|
243 |
|
244 /** |
|
245 * This is a loader that will unregister the JMXDomain that |
|
246 * created it, and register a bunch of MBeans in its place |
|
247 * by calling LazyStarterDomain.loadMBeans |
|
248 * |
|
249 * That one gave me "la migraine". |
|
250 */ |
|
251 private static class HalfGrainLoader implements MBeanServerLoader { |
|
252 private volatile LazyStarterDomain domain; |
|
253 public MBeanServer loadMBeanServer() { |
|
254 if (domain == null) |
|
255 throw new IllegalStateException( |
|
256 "JMXDomain MBean not registered!"); |
|
257 final MBeanServer server = domain.getMBeanServer(); |
|
258 final ObjectName domainName = domain.getObjectName(); |
|
259 try { |
|
260 server.unregisterMBean(domainName); |
|
261 } catch (Exception x) { |
|
262 throw new IllegalStateException("Can't unregister " + |
|
263 "JMXDomain: "+x,x); |
|
264 } |
|
265 domain.loadMBeans(server,domainName.getDomain()); |
|
266 return server; |
|
267 } |
|
268 public void setDomain(LazyStarterDomain domain) { |
|
269 this.domain = domain; |
|
270 } |
|
271 } |
|
272 |
|
273 /** |
|
274 * This is an MBeanServerProxy which create a loader for the |
|
275 * LazyStarterDomain MBean. |
|
276 */ |
|
277 private static class DomainStarter extends MBeanServerProxy { |
|
278 |
|
279 public DomainStarter() { |
|
280 this(new HalfGrainLoader()); |
|
281 } |
|
282 |
|
283 private final HalfGrainLoader loader; |
|
284 private DomainStarter(HalfGrainLoader loader) { |
|
285 super(loader); |
|
286 this.loader = loader; |
|
287 } |
|
288 |
|
289 public void setDomain(LazyStarterDomain domain) { |
|
290 loader.setDomain(domain); |
|
291 } |
|
292 } |
|
293 |
|
294 /** |
|
295 * A new LazyStarterDomain. When the domain monitored by this |
|
296 * MBean is accessed, this MBean will unregister itself and call |
|
297 * the abstract loadMBeans(MBeanServer) method. |
|
298 * Subclasses need only to implement loadMBeans(). |
|
299 */ |
|
300 public LazyStarterDomain() { |
|
301 this(new DomainStarter()); |
|
302 } |
|
303 |
|
304 private LazyStarterDomain(DomainStarter starter) { |
|
305 super(starter); |
|
306 starter.setDomain(this); |
|
307 } |
|
308 |
|
309 // Contrarily to its LazyDomain superclass, this LazyDomain |
|
310 // doesn't wrapp another MBeanServer: it simply registers a bunch |
|
311 // of MBeans in its own MBeanServer. |
|
312 // Thus, there's no notifications to forward. |
|
313 // |
|
314 @Override |
|
315 public void addMBeanServerNotificationListener( |
|
316 NotificationListener listener, NotificationFilter filter) { |
|
317 // nothing to do. |
|
318 } |
|
319 |
|
320 // Contrarily to its LazyDomain superclass, this LazyDomain |
|
321 // doesn't wrapp another MBeanServer: it simply registers a bunch |
|
322 // of MBeans in its own MBeanServer. |
|
323 // Thus, there's no notifications to forward. |
|
324 // |
|
325 @Override |
|
326 public void removeMBeanServerNotificationListener( |
|
327 NotificationListener listener) throws ListenerNotFoundException { |
|
328 // nothing to do |
|
329 } |
|
330 |
|
331 // If this domain is registered, it contains no MBean. |
|
332 // If it is not registered, then it no longer contain any MBean. |
|
333 // The MBeanCount is thus always 0. |
|
334 @Override |
|
335 public Integer getMBeanCount() { |
|
336 return 0; |
|
337 } |
|
338 |
|
339 /** |
|
340 * Called when the domain is first accessed. |
|
341 * {@code server} is the server in which this MBean was registered. |
|
342 * A subclass must override this method in order to register |
|
343 * the MBeans that should be contained in domain. |
|
344 * |
|
345 * @param server the server in which to load the MBeans. |
|
346 * @param domain the domain in which the MBeans should be registered. |
|
347 */ |
|
348 protected abstract void loadMBeans(MBeanServer server, String domain); |
|
349 |
|
350 |
|
351 } |
|
352 |
|
353 private static MBeanServerNotification pop( |
|
354 BlockingQueue<Notification> queue, |
|
355 String type, |
|
356 ObjectName mbean, |
|
357 String test) |
|
358 throws InterruptedException { |
|
359 final Notification n = queue.poll(1, TimeUnit.SECONDS); |
|
360 if (!(n instanceof MBeanServerNotification)) |
|
361 fail(test+"expected MBeanServerNotification, got "+n); |
|
362 final MBeanServerNotification msn = (MBeanServerNotification)n; |
|
363 if (!type.equals(msn.getType())) |
|
364 fail(test+"expected "+type+", got "+msn.getType()); |
|
365 if (!mbean.apply(msn.getMBeanName())) |
|
366 fail(test+"expected "+mbean+", got "+msn.getMBeanName()); |
|
367 System.out.println(test+" got: "+msn); |
|
368 return msn; |
|
369 } |
|
370 private static MBeanServerNotification popADD( |
|
371 BlockingQueue<Notification> queue, |
|
372 ObjectName mbean, |
|
373 String test) |
|
374 throws InterruptedException { |
|
375 return pop(queue, MBeanServerNotification.REGISTRATION_NOTIFICATION, |
|
376 mbean, test); |
|
377 } |
|
378 |
|
379 private static MBeanServerNotification popREM( |
|
380 BlockingQueue<Notification> queue, |
|
381 ObjectName mbean, |
|
382 String test) |
|
383 throws InterruptedException { |
|
384 return pop(queue, MBeanServerNotification.UNREGISTRATION_NOTIFICATION, |
|
385 mbean, test); |
|
386 } |
|
387 |
|
388 |
|
389 private static void fail(String msg) { |
|
390 raise(new RuntimeException(msg)); |
|
391 } |
|
392 |
|
393 private static void fail(String msg, Throwable cause) { |
|
394 raise(new RuntimeException(msg,cause)); |
|
395 } |
|
396 |
|
397 private static void raise(RuntimeException x) { |
|
398 lastException = x; |
|
399 exceptionCount++; |
|
400 throw x; |
|
401 } |
|
402 |
|
403 private static volatile Exception lastException = null; |
|
404 private static volatile int exceptionCount = 0; |
|
405 |
|
406 // ZZZ need to add a test case with several LazyDomains, and |
|
407 // need to test that nothing is loaded until the lazy domains |
|
408 // are accessed... |
|
409 // |
|
410 |
|
411 private static void registerWombats(MBeanServer server, String domain, |
|
412 int count) { |
|
413 try { |
|
414 for (int i=0;i<count;i++) { |
|
415 final ObjectName name = |
|
416 new ObjectName(domain+":type=Wombat,name=wombat#"+i); |
|
417 server.createMBean("Wombat", name); |
|
418 } |
|
419 } catch (RuntimeException x) { |
|
420 throw x; |
|
421 } catch(Exception x) { |
|
422 throw new RuntimeException(x.toString(),x); |
|
423 } |
|
424 } |
|
425 |
|
426 public static void checkSize(String test, MBeanServer server, int size) { |
|
427 System.out.println("We have now "+server.getMBeanCount()+ |
|
428 " MBeans in "+Arrays.toString(server.getDomains())); |
|
429 if (server.getMBeanCount() != size) |
|
430 fail(test+"Expected "+size+ |
|
431 " MBeans, found " + server.getMBeanCount()); |
|
432 } |
|
433 |
|
434 private static MBeanServer newMBeanServer() { |
|
435 return MBeanServerFactory.newMBeanServer(); |
|
436 } |
|
437 |
|
438 public static void lazyTest() throws Exception { |
|
439 final String test = "lazyTest: "; |
|
440 System.out.println("" + |
|
441 "\nThis test checks that it is possible to perform lazy loading" + |
|
442 "\nof MBeans in a given domain by using a JMXDomain subclass" + |
|
443 "\nfor that domain."); |
|
444 |
|
445 System.out.println(test + " START"); |
|
446 |
|
447 // The "global" MBeanServer... |
|
448 final MBeanServer server = newMBeanServer(); |
|
449 |
|
450 // An MBeanServer proxy which makes it possible to `lazy load' |
|
451 // the platform MBeanServer domains inside the global MBeanServer. |
|
452 // |
|
453 final MBeanServerProxy platform = |
|
454 new MBeanServerProxy(new MBeanServerLoader() { |
|
455 |
|
456 public MBeanServer loadMBeanServer() { |
|
457 return ManagementFactory.getPlatformMBeanServer(); |
|
458 } |
|
459 }); |
|
460 |
|
461 |
|
462 // The list of domain from the platform MBeanServer that will be |
|
463 // lazily loaded in the global MBeanServer |
|
464 // |
|
465 final String[] platformDomains = { |
|
466 "java.lang", "com.sun.management", |
|
467 "java.util.logging", "java.nio" |
|
468 }; |
|
469 |
|
470 // We create a second MBeanServer, in which we will store some |
|
471 // custom MBeans. We will use this server to perform lazy loading |
|
472 // of two domains: custom.awomb and custom.bwomb. |
|
473 // |
|
474 // We use an MBeanServerBuilder here so that the MBeans registered |
|
475 // in our custom domain see all the MBeans in the global MBeanServer. |
|
476 // We do this by saying that the 'outer' MBeanServer is our global |
|
477 // servers. This means that the MBeans registered in the global |
|
478 // MBeanServer will see the MBeans from custom.awomb and custom.bwomb, |
|
479 // and the MBeans from custom.awomb and custom.bwomb will also see |
|
480 // the MBeans from the global MBeanServer, including those from |
|
481 // the platform domains. |
|
482 // |
|
483 final MBeanServerBuilder builder = new MBeanServerBuilder(); |
|
484 final MBeanServerDelegate delegate = builder.newMBeanServerDelegate(); |
|
485 final MBeanServer custom = builder.newMBeanServer("custom", |
|
486 server, delegate); |
|
487 |
|
488 // Number of MBean that we will put in each of the custom domain. |
|
489 // |
|
490 final int customCount = 10; |
|
491 |
|
492 // We use one MBeanServer proxy for each of the custom domains. |
|
493 // This makes it possible to load custom.awomb independently of |
|
494 // custom.bwomb. |
|
495 // |
|
496 // Here, the logic of the loader is to register MBeans in the loaded |
|
497 // domain as soon as the domain is loaded. |
|
498 // |
|
499 final MBeanServerProxy customa = |
|
500 new MBeanServerProxy(new MBeanServerLoader() { |
|
501 // A loader to register awomb MBeans in the custom MBeanServer. |
|
502 public MBeanServer loadMBeanServer() { |
|
503 registerWombats(custom, "custom.awomb", customCount); |
|
504 return custom; |
|
505 } |
|
506 }); |
|
507 final MBeanServerProxy customb = |
|
508 new MBeanServerProxy(new MBeanServerLoader() { |
|
509 // A loader to register bwomb MBeans in the custom MBeanServer. |
|
510 public MBeanServer loadMBeanServer() { |
|
511 registerWombats(custom, "custom.bwomb", customCount); |
|
512 return custom; |
|
513 } |
|
514 }); |
|
515 |
|
516 // A notification queue. |
|
517 final BlockingQueue<Notification> queue = |
|
518 new ArrayBlockingQueue<Notification>(100); |
|
519 |
|
520 // A listener that puts notifs in the queue. |
|
521 final NotificationListener l = new NotificationListener() { |
|
522 |
|
523 public void handleNotification(Notification notification, |
|
524 Object handback) { |
|
525 try { |
|
526 if (!queue.offer(notification, 5, TimeUnit.SECONDS)) { |
|
527 throw new RuntimeException("timeout exceeded"); |
|
528 } |
|
529 } catch (Exception x) { |
|
530 fail(test + "failed to handle notif", x); |
|
531 } |
|
532 } |
|
533 }; |
|
534 |
|
535 // Create a LazyDomain for each of the platform domain. |
|
536 // All platform domain share the same MBeanServer proxy, which means |
|
537 // that loading one domain will also load all the others. |
|
538 // |
|
539 Map<String,LazyDomain> domainsMap = new HashMap<String,LazyDomain>(); |
|
540 for (String dom : platformDomains) { |
|
541 domainsMap.put(dom, new LazyDomain(platform)); |
|
542 } |
|
543 domainsMap.put("custom.awomb", new LazyDomain(customa)); |
|
544 domainsMap.put("custom.bwomb", new LazyDomain(customb)); |
|
545 |
|
546 for (Map.Entry<String,LazyDomain> e : domainsMap.entrySet()) { |
|
547 server.registerMBean(e.getValue(), |
|
548 JMXDomain.getDomainObjectName(e.getKey())); |
|
549 } |
|
550 |
|
551 // check that lazy MBeans are not there... |
|
552 checkSize(test,server,domainsMap.size()+1); |
|
553 |
|
554 System.out.println(test+" registering listener with delegate."); |
|
555 server.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME, l, |
|
556 null, null); |
|
557 |
|
558 // check that lazy MBeans are not there... |
|
559 checkSize(test,server,domainsMap.size()+1); |
|
560 |
|
561 // force loading of custom.awomb. |
|
562 final ObjectName awombat = new ObjectName( |
|
563 "custom.awomb:type=Wombat,name=wombat#"+customCount/2); |
|
564 if (!server.isRegistered(awombat)) |
|
565 fail(test+"Expected "+awombat+" to be reggistered!"); |
|
566 |
|
567 final int oldCount = domainsMap.size()+1+customCount; |
|
568 checkSize(test,server,oldCount); |
|
569 |
|
570 if (queue.peek() != null) |
|
571 fail(test+"Received unexpected notifications: "+queue); |
|
572 |
|
573 |
|
574 System.out.println(test+"creating a proxy for ClassLoadingMXBean."); |
|
575 final ClassLoadingMXBean cl = |
|
576 JMX.newMXBeanProxy(server, |
|
577 new ObjectName(ManagementFactory.CLASS_LOADING_MXBEAN_NAME), |
|
578 ClassLoadingMXBean.class); |
|
579 |
|
580 checkSize(test,server,oldCount); |
|
581 |
|
582 System.out.println(test+"Loaded classes: "+cl.getLoadedClassCount()); |
|
583 |
|
584 final int newCount = server.getMBeanCount(); |
|
585 if (newCount < oldCount+6) |
|
586 fail(test+"Expected at least "+(oldCount+6)+ |
|
587 " MBeans. Found "+newCount); |
|
588 |
|
589 final ObjectName jwombat = new ObjectName("java.lang:type=Wombat"); |
|
590 server.createMBean("Wombat", jwombat); |
|
591 System.out.println(test+"Created "+jwombat); |
|
592 checkSize(test,server,newCount+1); |
|
593 |
|
594 popADD(queue, jwombat, test); |
|
595 if (queue.peek() != null) |
|
596 fail(test+"Received unexpected notifications: "+queue); |
|
597 |
|
598 |
|
599 int platcount = 0; |
|
600 for (String dom : platformDomains) { |
|
601 final Set<ObjectName> found = |
|
602 server.queryNames(new ObjectName(dom+":*"),null); |
|
603 final int jcount = found.size(); |
|
604 System.out.println(test+"Found "+jcount+" MBeans in "+dom+ |
|
605 ": "+found); |
|
606 checkSize(test,server,newCount+1); |
|
607 platcount += (jcount-1); |
|
608 } |
|
609 checkSize(test,server,oldCount+platcount); |
|
610 |
|
611 final ObjectName owombat = new ObjectName("custom:type=Wombat"); |
|
612 server.createMBean("Wombat", owombat); |
|
613 System.out.println(test+"Created "+owombat); |
|
614 checkSize(test,server,newCount+2); |
|
615 popADD(queue, owombat, test); |
|
616 if (queue.peek() != null) |
|
617 fail(test+"Received unexpected notifications: "+queue); |
|
618 |
|
619 final Set<ObjectName> jwombatView = (Set<ObjectName>) |
|
620 server.invoke(jwombat, "listMatching", new Object[] {null}, |
|
621 new String[] {ObjectName.class.getName()}); |
|
622 System.out.println(test+jwombat+" sees: "+jwombatView); |
|
623 checkSize(test, server, newCount+2); |
|
624 if (jwombatView.size() != (platcount+1)) |
|
625 fail(test+jwombat+" sees "+jwombatView.size()+" MBeans - should" + |
|
626 " have seen "+(platcount+1)); |
|
627 |
|
628 final Set<ObjectName> platformMBeans = |
|
629 ManagementFactory.getPlatformMBeanServer(). |
|
630 queryNames(null, null); |
|
631 if (!platformMBeans.equals(jwombatView)) |
|
632 fail(test+jwombat+" should have seen "+platformMBeans); |
|
633 |
|
634 // check that awombat triggers loading of bwombats |
|
635 final Set<ObjectName> awombatView = (Set<ObjectName>) |
|
636 server.invoke(awombat, "listMatching", new Object[] {null}, |
|
637 new String[] {ObjectName.class.getName()}); |
|
638 System.out.println(test+awombat+" sees: "+awombatView); |
|
639 final int totalCount = newCount+2+customCount; |
|
640 checkSize(test, server, totalCount); |
|
641 if (awombatView.size() != totalCount) |
|
642 fail(test+jwombat+" sees "+jwombatView.size()+" MBeans - should" + |
|
643 " have seen "+totalCount); |
|
644 |
|
645 final Set<ObjectName> allMBeans = server. |
|
646 queryNames(null, null); |
|
647 if (!allMBeans.equals(awombatView)) |
|
648 fail(test+awombat+" should have seen "+allMBeans); |
|
649 |
|
650 System.out.println(test + " PASSED"); |
|
651 |
|
652 } |
|
653 |
|
654 |
|
655 public static void lazyStarterTest() throws Exception { |
|
656 final String test = "lazyStarterTest: "; |
|
657 System.out.println("" + |
|
658 "\nThis test checks that it is possible to perform lazy loading" + |
|
659 "\nof MBeans in a given domain by using a transient JMXDomain" + |
|
660 "\nsubclass for that domain. "); |
|
661 |
|
662 System.out.println(test + " START"); |
|
663 |
|
664 // The "global" MBeanServer... |
|
665 final MBeanServer platform = |
|
666 ManagementFactory.getPlatformMBeanServer(); |
|
667 |
|
668 // A notification queue. |
|
669 final BlockingQueue<Notification> queue = |
|
670 new ArrayBlockingQueue<Notification>(100); |
|
671 |
|
672 // A listener that puts notifs in the queue. |
|
673 final NotificationListener l = new NotificationListener() { |
|
674 |
|
675 public void handleNotification(Notification notification, |
|
676 Object handback) { |
|
677 try { |
|
678 if (!queue.offer(notification, 5, TimeUnit.SECONDS)) { |
|
679 throw new RuntimeException("timeout exceeded"); |
|
680 } |
|
681 } catch (Exception x) { |
|
682 fail(test + "failed to handle notif", x); |
|
683 } |
|
684 } |
|
685 }; |
|
686 |
|
687 System.out.println(test+" registering listener with delegate."); |
|
688 platform.addNotificationListener(MBeanServerDelegate.DELEGATE_NAME, l, |
|
689 null, null); |
|
690 |
|
691 final String ld1 = "lazy1"; |
|
692 final String ld2 = "lazy2"; |
|
693 final int wCount = 5; |
|
694 final LazyStarterDomain lazy1 = new LazyStarterDomain() { |
|
695 @Override |
|
696 protected void loadMBeans(MBeanServer server, String domain) { |
|
697 registerWombats(server, ld1, wCount); |
|
698 } |
|
699 }; |
|
700 final LazyStarterDomain lazy2 = new LazyStarterDomain() { |
|
701 @Override |
|
702 protected void loadMBeans(MBeanServer server, String domain) { |
|
703 registerWombats(server, ld2, wCount); |
|
704 } |
|
705 }; |
|
706 final ObjectName lo1 = JMXDomain.getDomainObjectName(ld1); |
|
707 final ObjectName lo2 = JMXDomain.getDomainObjectName(ld2); |
|
708 |
|
709 final int initial = platform.getMBeanCount(); |
|
710 |
|
711 platform.registerMBean(lazy1, lo1); |
|
712 System.out.println(test+"registered "+lo1); |
|
713 checkSize(test, platform, initial+1); |
|
714 popADD(queue, lo1, test); |
|
715 |
|
716 platform.registerMBean(lazy2, lo2); |
|
717 System.out.println(test+"registered "+lo2); |
|
718 checkSize(test, platform, initial+2); |
|
719 popADD(queue, lo2, test); |
|
720 |
|
721 |
|
722 final ObjectName awombat = new ObjectName( |
|
723 ld1+":type=Wombat,name=wombat#"+wCount/2); |
|
724 if (!platform.isRegistered(awombat)) |
|
725 fail(test+"Expected "+awombat+" to be reggistered!"); |
|
726 checkSize(test,platform,initial+wCount+1); |
|
727 popREM(queue, lo1, test); |
|
728 final ObjectName pat1 = |
|
729 new ObjectName(ld1+":type=Wombat,name=wombat#*"); |
|
730 for (int i=0;i<wCount;i++) { |
|
731 popADD(queue,pat1,test); |
|
732 } |
|
733 System.out.println(test+"Found: "+ |
|
734 platform.queryNames(pat1,null)); |
|
735 checkSize(test,platform,initial+wCount+1); |
|
736 |
|
737 final Set<ObjectName> all = platform.queryNames(null, null); |
|
738 popREM(queue, lo2, test); |
|
739 System.out.println(test+"Now found: "+all); |
|
740 checkSize(test,platform,initial+wCount+wCount); |
|
741 final ObjectName pat2 = |
|
742 new ObjectName(ld2+":type=Wombat,name=wombat#*"); |
|
743 for (int i=0;i<wCount;i++) { |
|
744 popADD(queue,pat2,test); |
|
745 } |
|
746 |
|
747 System.out.println(test+"check concurrent modification " + |
|
748 "of the DomainDispatcher."); |
|
749 System.out.println(test+"This will fail if the DomainDispatcher" + |
|
750 " doesn't allow concurrent modifications."); |
|
751 final HashMap<String,LazyStarterDomain> testConcurrent = |
|
752 new HashMap<String,LazyStarterDomain>(); |
|
753 for (int i=0;i<(100/wCount);i++) { |
|
754 final String ld = "concurrent.lazy"+i; |
|
755 final LazyStarterDomain lazy = new LazyStarterDomain() { |
|
756 @Override |
|
757 protected void loadMBeans(MBeanServer server, String domain) { |
|
758 registerWombats(server, ld, wCount-1); |
|
759 } |
|
760 }; |
|
761 testConcurrent.put(ld, lazy); |
|
762 final ObjectName lo = JMXDomain.getDomainObjectName(ld); |
|
763 platform.registerMBean(lazy, lo); |
|
764 popADD(queue, lo, test); |
|
765 } |
|
766 |
|
767 System.out.println(test+"Big autoload: "+ |
|
768 platform.queryNames(null,null)); |
|
769 System.out.println(test+"Big after load: "+ |
|
770 platform.queryNames(null,null)); |
|
771 if (!platform.queryNames(JMXDomain.getDomainObjectName("*"), null). |
|
772 isEmpty()) { |
|
773 fail(test+" some domains are still here: "+ |
|
774 platform.queryNames( |
|
775 JMXDomain.getDomainObjectName("*"), null)); |
|
776 } |
|
777 queue.clear(); |
|
778 System.out.println(test+"PASSED: The DomainDispatcher appears to be " + |
|
779 "resilient to concurrent modifications."); |
|
780 } |
|
781 |
|
782 public static void main(String... args) throws Exception { |
|
783 |
|
784 lazyTest(); |
|
785 lazyStarterTest(); |
|
786 |
|
787 if (lastException != null) |
|
788 throw lastException; |
|
789 } |
|
790 } |
|