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