jdk/test/javax/management/namespace/VirtualMBeanNotifTest.java
changeset 1156 bbc2d15aaf7a
child 1227 4546977d0d66
equal deleted inserted replaced
1155:a9a142fcf1b5 1156:bbc2d15aaf7a
       
     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 VirtualMBeanNotifTest.java
       
    26  * @bug 5108776
       
    27  * @build VirtualMBeanNotifTest Wombat WombatMBean
       
    28  * @summary Test that Virtual MBeans can be implemented and emit notifs.
       
    29  * @author  Daniel Fuchs
       
    30  */
       
    31 import java.lang.management.ManagementFactory;
       
    32 import java.lang.reflect.InvocationHandler;
       
    33 import java.lang.reflect.InvocationTargetException;
       
    34 import java.lang.reflect.Method;
       
    35 import java.lang.reflect.Proxy;
       
    36 import java.util.ArrayList;
       
    37 import java.util.Arrays;
       
    38 import java.util.HashMap;
       
    39 import java.util.HashSet;
       
    40 import java.util.List;
       
    41 import java.util.Map;
       
    42 import java.util.Set;
       
    43 import java.util.concurrent.ArrayBlockingQueue;
       
    44 import java.util.concurrent.BlockingQueue;
       
    45 import java.util.concurrent.CopyOnWriteArrayList;
       
    46 import java.util.concurrent.TimeUnit;
       
    47 import javax.management.Attribute;
       
    48 import javax.management.DynamicMBean;
       
    49 import javax.management.InstanceNotFoundException;
       
    50 import javax.management.JMException;
       
    51 import javax.management.MBeanAttributeInfo;
       
    52 import javax.management.MBeanInfo;
       
    53 import javax.management.MBeanNotificationInfo;
       
    54 import javax.management.MBeanServer;
       
    55 import javax.management.Notification;
       
    56 import javax.management.NotificationBroadcaster;
       
    57 import javax.management.NotificationEmitter;
       
    58 import javax.management.NotificationListener;
       
    59 import javax.management.ObjectName;
       
    60 import javax.management.RuntimeOperationsException;
       
    61 import javax.management.StandardMBean;
       
    62 import javax.management.event.EventSubscriber;
       
    63 import javax.management.namespace.VirtualEventManager;
       
    64 import javax.management.namespace.MBeanServerSupport;
       
    65 
       
    66 public class VirtualMBeanNotifTest {
       
    67 
       
    68     /**
       
    69      * An invocation handler that can implement DynamicMBean,
       
    70      * NotificationBroadcaster, NotificationEmitter.
       
    71      * The invocation handler works by forwarding all calls received from
       
    72      * the implemented interfaces through a wrapped MBeanServer object.
       
    73      */
       
    74     public static class DynamicWrapper
       
    75             implements InvocationHandler {
       
    76 
       
    77         /**
       
    78          * Inserts an additional class at the head of a signature array.
       
    79          * @param first The first class in the signature
       
    80          * @param rest  The other classes in the signature
       
    81          * @return A signature array, of length rest.length+1.
       
    82          */
       
    83         static Class[] concat(Class first, Class... rest) {
       
    84             if (rest == null || rest.length == 0) {
       
    85                 return new Class[] { first };
       
    86             }
       
    87             final Class[] sig = new Class[rest.length+1];
       
    88             sig[0] = first;
       
    89             System.arraycopy(rest, 0, sig, 1, rest.length);
       
    90             return sig;
       
    91         }
       
    92 
       
    93         /**
       
    94          * Inserts an additional object at the head of a parameters array.
       
    95          * @param first The first object in the parameter array.
       
    96          * @param rest  The other objects in the parameter array.
       
    97          * @return A parameter array, of length rest.length+1.
       
    98          */
       
    99         static Object[] concat(Object first, Object... rest) {
       
   100             if (rest == null || rest.length == 0) {
       
   101                 return new Object[] { first };
       
   102             }
       
   103             final Object[] params = new Object[rest.length+1];
       
   104             params[0] = first;
       
   105             System.arraycopy(rest, 0, params, 1, rest.length);
       
   106             return params;
       
   107         }
       
   108 
       
   109         /**
       
   110          * These two sets are used to check that all methods from
       
   111          * implemented interfaces are mapped.
       
   112          * unmapped is the set of methods that couldn't be mapped.
       
   113          * mapped is the set of methods that could be mapped.
       
   114          */
       
   115         final static Set<Method> unmapped = new HashSet<Method>();
       
   116         final static Set<Method> mapped = new HashSet<Method>();
       
   117 
       
   118         /**
       
   119          * For each method define in one of the interfaces intf, tries
       
   120          * to find a corresponding method in the reference class ref, where
       
   121          * the method in ref has the same name, and takes an additional
       
   122          * ObjectName as first parameter.
       
   123          *
       
   124          * So for instance, if ref is MBeanServer and intf is {DynamicMBean}
       
   125          * the result map is:
       
   126          *     DynamicMBean.getAttribute -> MBeanServer.getAttribute
       
   127          *     DynamicMBean.setAttribute -> MBeanServer.setAttribute
       
   128          *     etc...
       
   129          * If a method was mapped, it is additionally added to 'mapped'
       
   130          * If a method couldn't be mapped, it is added to 'unmmapped'.
       
   131          * In our example above, DynamicMBean.getNotificationInfo will end
       
   132          * up in 'unmapped'.
       
   133          *
       
   134          * @param ref   The reference class - to which calls will be forwarded
       
   135          *              with an additional ObjectName parameter inserted.
       
   136          * @param intf  The proxy interface classes - for which we must find an
       
   137          *              equivalent in 'ref'
       
   138          * @return A map mapping the methods from intfs to the method of ref.
       
   139          */
       
   140         static Map<Method,Method> makeMapFor(Class<?> ref, Class<?>... intf) {
       
   141             final Map<Method,Method> map = new HashMap<Method,Method>();
       
   142             for (Class<?> clazz : intf) {
       
   143                 for (Method m : clazz.getMethods()) {
       
   144                     try {
       
   145                         final Method m2 =
       
   146                             ref.getMethod(m.getName(),
       
   147                             concat(ObjectName.class,m.getParameterTypes()));
       
   148                         map.put(m,m2);
       
   149                         mapped.add(m);
       
   150                     } catch (Exception x) {
       
   151                         unmapped.add(m);
       
   152                     }
       
   153                 }
       
   154             }
       
   155             return map;
       
   156         }
       
   157 
       
   158         /**
       
   159          * Tries to map all methods from DynamicMBean.class and
       
   160          * NotificationEmitter.class to their equivalent in MBeanServer.
       
   161          * This should be all the methods except
       
   162          * DynamicMBean.getNotificationInfo.
       
   163          */
       
   164         static final Map<Method,Method> mbeanmap =
       
   165                 makeMapFor(MBeanServer.class,DynamicMBean.class,
       
   166                 NotificationEmitter.class);
       
   167         /**
       
   168          * Tries to map all methods from DynamicMBean.class and
       
   169          * NotificationEmitter.class to an equivalent in DynamicWrapper.
       
   170          * This time only DynamicMBean.getNotificationInfo will be mapped.
       
   171          */
       
   172         static final Map<Method,Method> selfmap =
       
   173                 makeMapFor(DynamicWrapper.class,DynamicMBean.class,
       
   174                 NotificationEmitter.class);
       
   175 
       
   176         /**
       
   177          * Now check that we have mapped all methods.
       
   178          */
       
   179         static {
       
   180             unmapped.removeAll(mapped);
       
   181             if (unmapped.size() > 0)
       
   182                 throw new ExceptionInInitializerError("Couldn't map "+ unmapped);
       
   183         }
       
   184 
       
   185         /**
       
   186          * The wrapped MBeanServer to which everything is delegated.
       
   187          */
       
   188         private final MBeanServer server;
       
   189 
       
   190         /**
       
   191          * The name of the MBean we're proxying.
       
   192          */
       
   193         private final ObjectName name;
       
   194         DynamicWrapper(MBeanServer server, ObjectName name) {
       
   195             this.server=server;
       
   196             this.name=name;
       
   197         }
       
   198 
       
   199         /**
       
   200          * Creates a new proxy for the given MBean. Implements
       
   201          * NotificationEmitter/NotificationBroadcaster if the proxied
       
   202          * MBean also does.
       
   203          * @param name    the name of the proxied MBean
       
   204          * @param server  the wrapped server
       
   205          * @return a DynamicMBean proxy
       
   206          * @throws javax.management.InstanceNotFoundException
       
   207          */
       
   208         public static DynamicMBean newProxy(ObjectName name, MBeanServer server)
       
   209             throws InstanceNotFoundException {
       
   210             if (server.isInstanceOf(name,
       
   211                     NotificationEmitter.class.getName())) {
       
   212                 // implements NotificationEmitter
       
   213                 return (DynamicMBean)
       
   214                         Proxy.newProxyInstance(
       
   215                         DynamicWrapper.class.getClassLoader(),
       
   216                         new Class[] {NotificationEmitter.class,
       
   217                         DynamicMBean.class},
       
   218                         new DynamicWrapper(server, name));
       
   219             }
       
   220             if (server.isInstanceOf(name,
       
   221                     NotificationBroadcaster.class.getName())) {
       
   222                 // implements NotificationBroadcaster
       
   223                 return (DynamicMBean)
       
   224                         Proxy.newProxyInstance(
       
   225                         DynamicWrapper.class.getClassLoader(),
       
   226                         new Class[] {NotificationBroadcaster.class,
       
   227                         DynamicMBean.class},
       
   228                         new DynamicWrapper(server, name));
       
   229             }
       
   230             // Only implements DynamicMBean.
       
   231             return (DynamicMBean)
       
   232                         Proxy.newProxyInstance(
       
   233                         DynamicWrapper.class.getClassLoader(),
       
   234                         new Class[] {DynamicMBean.class},
       
   235                         new DynamicWrapper(server, name));
       
   236         }
       
   237 
       
   238         public Object invoke(Object proxy, Method method, Object[] args)
       
   239                 throws Throwable {
       
   240             // Look for a method on this class (takes precedence)
       
   241             final Method self = selfmap.get(method);
       
   242             if (self != null)
       
   243                 return call(this,self,concat(name,args));
       
   244 
       
   245             // no method found on this class, look for the same method
       
   246             // on the wrapped MBeanServer
       
   247             final Method mbean = mbeanmap.get(method);
       
   248             if (mbean != null)
       
   249                 return call(server,mbean,concat(name,args));
       
   250 
       
   251             // This isn't a method that can be forwarded to MBeanServer.
       
   252             // If it's a method from Object, call it on this.
       
   253             if (method.getDeclaringClass().equals(Object.class))
       
   254                 return call(this,method,args);
       
   255             throw new NoSuchMethodException(method.getName());
       
   256         }
       
   257 
       
   258         // Call a method using reflection, unwraps invocation target exceptions
       
   259         public Object call(Object handle, Method m, Object[] args)
       
   260                 throws Throwable {
       
   261             try {
       
   262                 return m.invoke(handle, args);
       
   263             } catch (InvocationTargetException x) {
       
   264                throw x.getCause();
       
   265             }
       
   266         }
       
   267 
       
   268         // this method is called when DynamicMBean.getNotificationInfo() is
       
   269         // called. This is the method that should be mapped in
       
   270         // 'selfmap'
       
   271         public MBeanNotificationInfo[] getNotificationInfo(ObjectName name)
       
   272             throws JMException {
       
   273             return server.getMBeanInfo(name).getNotifications();
       
   274         }
       
   275     }
       
   276 
       
   277     /**
       
   278      * Just so that we can call the same test twice but with two
       
   279      * different implementations of VirtualMBeanServerSupport.
       
   280      */
       
   281     public static interface MBeanServerWrapperFactory {
       
   282         public MBeanServer wrapMBeanServer(MBeanServer wrapped);
       
   283     }
       
   284 
       
   285     /**
       
   286      * A VirtualMBeanServerSupport that wrapps an MBeanServer and does not
       
   287      * use VirtualEventManager.
       
   288      */
       
   289     public static class VirtualMBeanServerTest
       
   290             extends MBeanServerSupport {
       
   291 
       
   292         final MBeanServer wrapped;
       
   293 
       
   294         public VirtualMBeanServerTest(MBeanServer wrapped) {
       
   295             this.wrapped=wrapped;
       
   296         }
       
   297 
       
   298         @Override
       
   299         public DynamicMBean getDynamicMBeanFor(final ObjectName name)
       
   300                 throws InstanceNotFoundException {
       
   301             if (wrapped.isRegistered(name))
       
   302                 return DynamicWrapper.newProxy(name,wrapped);
       
   303             throw new InstanceNotFoundException(String.valueOf(name));
       
   304         }
       
   305 
       
   306         @Override
       
   307         protected Set<ObjectName> getNames() {
       
   308             return wrapped.queryNames(null, null);
       
   309         }
       
   310 
       
   311         public final static MBeanServerWrapperFactory factory =
       
   312                 new MBeanServerWrapperFactory() {
       
   313 
       
   314             public MBeanServer wrapMBeanServer(MBeanServer wrapped) {
       
   315                 return new VirtualMBeanServerTest(wrapped);
       
   316             }
       
   317             @Override
       
   318             public String toString() {
       
   319                 return VirtualMBeanServerTest.class.getName();
       
   320             }
       
   321         };
       
   322     }
       
   323 
       
   324      /**
       
   325      * A VirtualMBeanServerSupport that wrapps an MBeanServer and
       
   326      * uses a VirtualEventManager.
       
   327      */
       
   328     public static class VirtualMBeanServerTest2
       
   329             extends VirtualMBeanServerTest {
       
   330 
       
   331         final EventSubscriber sub;
       
   332         final NotificationListener nl;
       
   333         final VirtualEventManager  mgr;
       
   334 
       
   335         /**
       
   336          * We use an EventSubscriber to subscribe for all notifications from
       
   337          * the wrapped MBeanServer, and publish them through a
       
   338          * VirtualEventManager. Not a very efficient way of doing things.
       
   339          * @param wrapped
       
   340          */
       
   341         public VirtualMBeanServerTest2(MBeanServer wrapped) {
       
   342             super(wrapped);
       
   343             this.sub = EventSubscriber.getEventSubscriber(wrapped);
       
   344             this.mgr = new VirtualEventManager();
       
   345             this.nl = new NotificationListener() {
       
   346                 public void handleNotification(Notification notification, Object handback) {
       
   347                     mgr.publish((ObjectName)notification.getSource(), notification);
       
   348                 }
       
   349             };
       
   350             try {
       
   351                 sub.subscribe(ObjectName.WILDCARD, nl, null, null);
       
   352             } catch (RuntimeException x) {
       
   353                 throw x;
       
   354             } catch (Exception x) {
       
   355                 throw new IllegalStateException("can't subscribe for notifications!");
       
   356             }
       
   357         }
       
   358 
       
   359         @Override
       
   360         public NotificationEmitter
       
   361                 getNotificationEmitterFor(ObjectName name)
       
   362                 throws InstanceNotFoundException {
       
   363             final DynamicMBean mbean = getDynamicMBeanFor(name);
       
   364             if (mbean instanceof NotificationEmitter)
       
   365                 return mgr.getNotificationEmitterFor(name);
       
   366             return null;
       
   367         }
       
   368 
       
   369         public final static MBeanServerWrapperFactory factory =
       
   370                 new MBeanServerWrapperFactory() {
       
   371 
       
   372             public MBeanServer wrapMBeanServer(MBeanServer wrapped) {
       
   373                 return new VirtualMBeanServerTest2(wrapped);
       
   374             }
       
   375             @Override
       
   376             public String toString() {
       
   377                 return VirtualMBeanServerTest2.class.getName();
       
   378             }
       
   379         };
       
   380     }
       
   381 
       
   382 
       
   383     public static void test(MBeanServerWrapperFactory factory) throws Exception {
       
   384         final MBeanServer server = ManagementFactory.getPlatformMBeanServer();
       
   385 
       
   386         // names[] are NotificationEmitters
       
   387         final ObjectName[] emitters = new ObjectName[2];
       
   388         // shields[] have been shielded by wrapping them in a StandardMBean,
       
   389         // so although the resource is an MBean that implements
       
   390         // NotificationEmitter, the registered MBean (the wrapper) doesn't.
       
   391         final ObjectName[] shielded = new ObjectName[2];
       
   392 
       
   393         final List<ObjectName> registered = new ArrayList<ObjectName>(4);
       
   394 
       
   395         try {
       
   396             // register two MBeans before wrapping
       
   397             server.registerMBean(new Wombat(),
       
   398                     emitters[0] = new ObjectName("bush:type=Wombat,name=wom"));
       
   399             registered.add(emitters[0]);
       
   400 
       
   401             // we shield the second MBean in a StandardMBean so that it does
       
   402             // not appear as a NotificationEmitter.
       
   403             server.registerMBean(
       
   404                     new StandardMBean(new Wombat(), WombatMBean.class),
       
   405                     shielded[0] = new ObjectName("bush:type=Wombat,name=womshield"));
       
   406             registered.add(shielded[0]);
       
   407 
       
   408             final MBeanServer vserver = factory.wrapMBeanServer(server);
       
   409 
       
   410             // register two other MBeans after wrapping
       
   411             server.registerMBean(new Wombat(),
       
   412                     emitters[1] = new ObjectName("bush:type=Wombat,name=bat"));
       
   413             registered.add(emitters[1]);
       
   414 
       
   415             // we shield the second MBean in a StandardMBean so that it does
       
   416             // not appear as a NotificationEmitter.
       
   417             server.registerMBean(
       
   418                     new StandardMBean(new Wombat(), WombatMBean.class),
       
   419                     shielded[1] = new ObjectName("bush:type=Wombat,name=batshield"));
       
   420             registered.add(shielded[1]);
       
   421 
       
   422             // Call test with this config - we have two wombats who broadcast
       
   423             // notifs (emitters) and two wombats who don't (shielded).
       
   424             test(vserver, emitters, shielded);
       
   425 
       
   426             System.out.println("*** Test passed for: " + factory);
       
   427         } finally {
       
   428             // Clean up the platform mbean server for the next test...
       
   429             for (ObjectName n : registered) {
       
   430                 try {
       
   431                     server.unregisterMBean(n);
       
   432                 } catch (Exception x) {
       
   433                     x.printStackTrace();
       
   434                 }
       
   435             }
       
   436         }
       
   437     }
       
   438 
       
   439     /**
       
   440      * Perform the actual test.
       
   441      * @param vserver    A virtual MBeanServerSupport implementation
       
   442      * @param emitters   Names of NotificationBroadcaster MBeans
       
   443      * @param shielded   Names of non NotificationBroadcaster MBeans
       
   444      * @throws java.lang.Exception
       
   445      */
       
   446     public static void test(MBeanServer vserver, ObjectName[] emitters,
       
   447             ObjectName[] shielded) throws Exception {
       
   448 
       
   449         // To catch exception in NotificationListener
       
   450         final List<Exception> fail = new CopyOnWriteArrayList<Exception>();
       
   451 
       
   452         // A queue of received notifications
       
   453         final BlockingQueue<Notification> notifs =
       
   454                 new ArrayBlockingQueue<Notification>(50);
       
   455 
       
   456         // A notification listener that puts the notification it receives
       
   457         // in the queue.
       
   458         final NotificationListener handler = new NotificationListener() {
       
   459 
       
   460             public void handleNotification(Notification notification,
       
   461                     Object handback) {
       
   462                 try {
       
   463                     notifs.put(notification);
       
   464                 } catch (Exception x) {
       
   465                     fail.add(x);
       
   466                 }
       
   467             }
       
   468         };
       
   469 
       
   470         // A list of attribute names for which we might receive an
       
   471         // exception. If an exception is received when getting these
       
   472         // attributes - the test will not fail.
       
   473         final List<String> exceptions = Arrays.asList( new String[] {
       
   474            "UsageThresholdCount","UsageThreshold","UsageThresholdExceeded",
       
   475            "CollectionUsageThresholdCount","CollectionUsageThreshold",
       
   476            "CollectionUsageThresholdExceeded"
       
   477         });
       
   478 
       
   479         // This is just a sanity check. Get all attributes of all MBeans.
       
   480         for (ObjectName n : vserver.queryNames(null, null)) {
       
   481             final MBeanInfo m = vserver.getMBeanInfo(n);
       
   482             for (MBeanAttributeInfo mba : m.getAttributes()) {
       
   483                 // System.out.println(n+":");
       
   484                 Object val;
       
   485                 try {
       
   486                     val = vserver.getAttribute(n, mba.getName());
       
   487                 } catch (Exception x) {
       
   488                     // only accept exception for those attributes that
       
   489                     // have a valid reason to fail...
       
   490                     if (exceptions.contains(mba.getName())) val = x;
       
   491                     else throw new Exception("Failed to get " +
       
   492                             mba.getName() + " from " + n,x);
       
   493                 }
       
   494                 // System.out.println("\t "+mba.getName()+": "+ val);
       
   495             }
       
   496         }
       
   497 
       
   498         // The actual tests. Register for notifications with notif emitters
       
   499         for (ObjectName n : emitters) {
       
   500             vserver.addNotificationListener(n, handler, null, n);
       
   501         }
       
   502 
       
   503         // Trigger the emission of notifications, check that we received them.
       
   504         for (ObjectName n : emitters) {
       
   505             vserver.setAttribute(n,
       
   506                     new Attribute("Caption","I am a new wombat!"));
       
   507             final Notification notif = notifs.poll(4, TimeUnit.SECONDS);
       
   508             if (!notif.getSource().equals(n))
       
   509                 throw new Exception("Bad source for "+ notif);
       
   510             if (fail.size() > 0)
       
   511                 throw new Exception("Failed to handle notif",fail.remove(0));
       
   512         }
       
   513 
       
   514         // Check that we didn't get more notifs than expected
       
   515         if (notifs.size() > 0)
       
   516             throw new Exception("Extra notifications in queue: "+notifs);
       
   517 
       
   518         // Check that if the MBean doesn't exist, we get InstanceNotFound.
       
   519         try {
       
   520             vserver.addNotificationListener(new ObjectName("toto:toto=toto"),
       
   521                     handler, null, null);
       
   522             throw new Exception("toto:toto=toto doesn't throw INFE");
       
   523         } catch (InstanceNotFoundException x) {
       
   524             System.out.println("Received "+x+" as expected.");
       
   525         }
       
   526 
       
   527         // For those MBeans that shouldn't be NotificationEmitters, check that
       
   528         // we get IllegalArgumentException
       
   529         for (ObjectName n : shielded) {
       
   530             try {
       
   531                 vserver.addNotificationListener(n, handler, null, n);
       
   532             } catch (RuntimeOperationsException x) {
       
   533                 System.out.println("Received "+x+" as expected.");
       
   534                 System.out.println("Cause is: "+x.getCause());
       
   535                 if (!(x.getCause() instanceof IllegalArgumentException))
       
   536                     throw new Exception("was expecting IllegalArgumentException cause. Got "+x.getCause(),x);
       
   537             }
       
   538         }
       
   539 
       
   540         // Sanity check. Remove our listeners.
       
   541         for (ObjectName n : emitters) {
       
   542             vserver.removeNotificationListener(n, handler, null, n);
       
   543         }
       
   544 
       
   545         // That's it.
       
   546         // Sanity check: we shouldn't have received any new notif.
       
   547         if (notifs.size() > 0)
       
   548             throw new Exception("Extra notifications in queue: "+notifs);
       
   549         // The NotifListener shouldn't have logged any new exception.
       
   550         if (fail.size() > 0)
       
   551                 throw new Exception("Failed to handle notif",fail.remove(0));
       
   552     }
       
   553 
       
   554     public static void main(String[] args) throws Exception {
       
   555         // test with a regular MBeanServer (no VirtualMBeanServerSupport)
       
   556         final MBeanServerWrapperFactory identity =
       
   557                 new MBeanServerWrapperFactory() {
       
   558             public MBeanServer wrapMBeanServer(MBeanServer wrapped) {
       
   559                 return wrapped;
       
   560             }
       
   561         };
       
   562         test(identity);
       
   563         // test with no EventManager
       
   564         test(VirtualMBeanServerTest.factory);
       
   565         // test with VirtualEventManager
       
   566         test(VirtualMBeanServerTest2.factory);
       
   567     }
       
   568 }