jdk/test/javax/management/namespace/LazyDomainTest.java
changeset 4159 9e3aae7675f1
parent 4158 0b4d21bc8b5c
parent 4156 acaa49a2768a
child 4160 bda0a85afcb7
equal deleted inserted replaced
4158:0b4d21bc8b5c 4159:9e3aae7675f1
     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 }