jdk/test/javax/management/namespace/VirtualMBeanTest.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 VirtualMBeanTest.java
       
    26  * @bug 5108776 5072476
       
    27  * @summary Test that Virtual MBeans can be implemented and emit notifs.
       
    28  * @author Eamonn McManus
       
    29  */
       
    30 
       
    31 import java.io.PrintWriter;
       
    32 import java.io.StringWriter;
       
    33 import java.util.Arrays;
       
    34 import java.util.Collections;
       
    35 import java.util.List;
       
    36 import java.util.Map;
       
    37 import java.util.Set;
       
    38 import java.util.TreeMap;
       
    39 import java.util.TreeSet;
       
    40 import java.util.concurrent.ArrayBlockingQueue;
       
    41 import java.util.concurrent.BlockingQueue;
       
    42 import java.util.concurrent.TimeUnit;
       
    43 import javax.management.DynamicMBean;
       
    44 import javax.management.InstanceNotFoundException;
       
    45 import javax.management.MBeanInfo;
       
    46 import javax.management.MBeanServer;
       
    47 import javax.management.MBeanServerFactory;
       
    48 import javax.management.MalformedObjectNameException;
       
    49 import javax.management.Notification;
       
    50 import javax.management.NotificationBroadcaster;
       
    51 import javax.management.NotificationBroadcasterSupport;
       
    52 import javax.management.NotificationEmitter;
       
    53 import javax.management.NotificationListener;
       
    54 import javax.management.ObjectName;
       
    55 import javax.management.RuntimeOperationsException;
       
    56 import javax.management.SendNotification;
       
    57 import javax.management.StandardEmitterMBean;
       
    58 import javax.management.StandardMBean;
       
    59 import javax.management.namespace.JMXNamespace;
       
    60 import javax.management.namespace.JMXNamespaces;
       
    61 import javax.management.namespace.VirtualEventManager;
       
    62 import javax.management.namespace.MBeanServerSupport;
       
    63 import javax.management.timer.TimerMBean;
       
    64 
       
    65 // In this test, we check that the two main use case types for
       
    66 // MBeanServerSupport work correctly:
       
    67 // (1) as a special-purpose implementation of MBeanServer for a fixed number
       
    68 //     of MBeans (e.g. for QueryNotificationFilter)
       
    69 // (2) as an MBeanServer supporting Virtual MBeans.
       
    70 // In each case we are particularly interested in the notification behaviour.
       
    71 // We check that the behaviour is correct when calling addNotificationListener
       
    72 // (a) for an MBean that does not exist; (b) for an MBean that does exist but
       
    73 // is not a NotificationEmitter; and (c) for an MBean that exists and is
       
    74 // a NotificationEmitter.  We also check the degenerate and usual case
       
    75 // where the MBeanServerSupport subclass does not support notifications
       
    76 // at all.
       
    77 //
       
    78 // Each subclass will have an MBean called test:type=NotEmitter that
       
    79 // does not support addNotificationListener. If it also has MBeans called
       
    80 // test:type=Emitter,* then they are expected to support addNL. No subclass
       
    81 // will have any other MBeans, so in particular no subclass will have
       
    82 // test:type=Nonexistent.
       
    83 //
       
    84 public class VirtualMBeanTest {
       
    85     static final ObjectName
       
    86             nonExistentName, notEmitterName, emitterName1, emitterName2;
       
    87     static {
       
    88         try {
       
    89             nonExistentName = new ObjectName("test:type=NonExistent");
       
    90             notEmitterName = new ObjectName("test:type=NotEmitter");
       
    91             emitterName1 = new ObjectName("test:type=Emitter,id=1");
       
    92             emitterName2 = new ObjectName("test:type=Emitter,id=2");
       
    93         } catch (MalformedObjectNameException e) {
       
    94             throw new AssertionError(e);
       
    95         }
       
    96     }
       
    97 
       
    98     static final StandardMBean.Options wrappedVisible = new StandardMBean.Options();
       
    99     static {
       
   100         wrappedVisible.setWrappedObjectVisible(true);
       
   101     }
       
   102 
       
   103     public static interface NothingMBean {}
       
   104     public static class Nothing implements NothingMBean {}
       
   105     public static class NothingNBS extends NotificationBroadcasterSupport
       
   106             implements NothingMBean {}
       
   107 
       
   108     // Class that has hardwired MBeans test:type=NotEmitter,
       
   109     // test:type=Broadcaster, and test:type=Emitter.
       
   110     private static class HardwiredMBS extends MBeanServerSupport
       
   111             implements SendNotification {
       
   112         private final DynamicMBean notEmitter =
       
   113                 new StandardMBean(new Nothing(), NothingMBean.class, wrappedVisible);
       
   114         private final StandardEmitterMBean emitter1, emitter2;
       
   115         {
       
   116             NothingNBS nnbs1 = new NothingNBS();
       
   117             emitter1 = new StandardEmitterMBean(
       
   118                     nnbs1, NothingMBean.class, wrappedVisible, nnbs1);
       
   119             NothingNBS nnbs2 = new NothingNBS();
       
   120             emitter2 = new StandardEmitterMBean(
       
   121                     nnbs2, NothingMBean.class, wrappedVisible, nnbs2);
       
   122         }
       
   123 
       
   124         private final Map<ObjectName, DynamicMBean> map =
       
   125                 new TreeMap<ObjectName, DynamicMBean>();
       
   126         {
       
   127             map.put(notEmitterName, notEmitter);
       
   128             map.put(emitterName1, emitter1);
       
   129             map.put(emitterName2, emitter2);
       
   130         }
       
   131 
       
   132 
       
   133         @Override
       
   134         public DynamicMBean getDynamicMBeanFor(ObjectName name)
       
   135                 throws InstanceNotFoundException {
       
   136             DynamicMBean mbean = map.get(name);
       
   137             if (mbean != null)
       
   138                 return mbean;
       
   139             else
       
   140                 throw new InstanceNotFoundException(name);
       
   141         }
       
   142 
       
   143         @Override
       
   144         protected Set<ObjectName> getNames() {
       
   145             return map.keySet();
       
   146         }
       
   147 
       
   148         @Override
       
   149         public String toString() {
       
   150             return "Hardwired MBeanServerSupport";
       
   151         }
       
   152 
       
   153         public void sendNotification(Notification notification) {
       
   154             emitter1.sendNotification(notification);
       
   155             emitter2.sendNotification(notification);
       
   156         }
       
   157     }
       
   158 
       
   159     // Class that has the notEmitter MBean but not either of the others, so does
       
   160     // not support listeners.
       
   161     private static class VirtualMBSWithoutListeners
       
   162             extends MBeanServerSupport {
       
   163         @Override
       
   164         public DynamicMBean getDynamicMBeanFor(ObjectName name)
       
   165                 throws InstanceNotFoundException {
       
   166             if (name.equals(notEmitterName)) {
       
   167                 return new StandardMBean(
       
   168                         new Nothing(), NothingMBean.class, wrappedVisible);
       
   169             } else
       
   170                 throw new InstanceNotFoundException(name);
       
   171         }
       
   172 
       
   173         @Override
       
   174         protected Set<ObjectName> getNames() {
       
   175             return Collections.singleton(notEmitterName);
       
   176         }
       
   177 
       
   178         @Override
       
   179         public String toString() {
       
   180             return "Virtual MBeanServerSupport without listener support";
       
   181         }
       
   182     }
       
   183 
       
   184     // Class that has the notEmitter and emitter MBeans as Virtual MBeans, using
       
   185     // VirtualEventManager to handle listeners for the emitter MBean.  We
       
   186     // implement the broadcaster MBean (which is a NotificationBroadcaster but
       
   187     // not a NotificationEmitter) even though it's very hard to imagine a real
       
   188     // use case where that would happen.
       
   189     private static class VirtualMBSWithListeners
       
   190             extends MBeanServerSupport implements SendNotification {
       
   191         private final VirtualEventManager vem = new VirtualEventManager();
       
   192 
       
   193         private static final List<ObjectName> names =
       
   194                 Arrays.asList(notEmitterName, emitterName1, emitterName2);
       
   195 
       
   196         @Override
       
   197         public DynamicMBean getDynamicMBeanFor(ObjectName name)
       
   198                 throws InstanceNotFoundException {
       
   199             if (names.contains(name)) {
       
   200                 return new StandardMBean(
       
   201                         new Nothing(), NothingMBean.class, wrappedVisible);
       
   202             } else
       
   203                 throw new InstanceNotFoundException(name);
       
   204         }
       
   205 
       
   206         @Override
       
   207         public NotificationEmitter getNotificationEmitterFor(
       
   208                 ObjectName name) throws InstanceNotFoundException {
       
   209             if (name.equals(emitterName1) || name.equals(emitterName2))
       
   210                 return vem.getNotificationEmitterFor(name);
       
   211             else if (name.equals(notEmitterName))
       
   212                 return null;
       
   213             else
       
   214                 throw new InstanceNotFoundException(name);
       
   215         }
       
   216 
       
   217         @Override
       
   218         protected Set<ObjectName> getNames() {
       
   219             return new TreeSet<ObjectName>(Arrays.asList(notEmitterName, emitterName2));
       
   220         }
       
   221 
       
   222         @Override
       
   223         public String toString() {
       
   224             return "Virtual MBeanServerSupport with listener support";
       
   225         }
       
   226 
       
   227         public void sendNotification(Notification notification) {
       
   228             vem.publish(emitterName1, notification);
       
   229             vem.publish(emitterName2, notification);
       
   230         }
       
   231     }
       
   232 
       
   233     private static final MBeanServer[] vmbsss = {
       
   234         new HardwiredMBS(),
       
   235         new VirtualMBSWithoutListeners(),
       
   236         new VirtualMBSWithListeners(),
       
   237     };
       
   238 
       
   239     public static void main(String[] args) throws Exception {
       
   240         Exception lastEx = null;
       
   241         for (MBeanServer vmbs : vmbsss) {
       
   242             String testName = "\"" + vmbs + "\"";
       
   243             System.out.println("===Test " + testName + "===");
       
   244             try {
       
   245                 test(vmbs);
       
   246             } catch (Exception e) {
       
   247                 System.out.println(
       
   248                         "===Test " + testName + " failed with exception " + e);
       
   249                 StringWriter sw = new StringWriter();
       
   250                 PrintWriter pw = new PrintWriter(sw);
       
   251                 e.printStackTrace(pw);
       
   252                 pw.flush();
       
   253                 String es = sw.toString();
       
   254                 System.out.println("......" + es.replace("\n", "\n......"));
       
   255                 lastEx = e;
       
   256             }
       
   257         }
       
   258         if (lastEx != null)
       
   259             throw lastEx;
       
   260         System.out.println("TEST PASSED");
       
   261     }
       
   262 
       
   263     private static class NothingListener implements NotificationListener {
       
   264         public void handleNotification(Notification notification,
       
   265                                        Object handback) {
       
   266             throw new UnsupportedOperationException("Not supported yet.");
       
   267         }
       
   268     }
       
   269 
       
   270     private static class QueueListener implements NotificationListener {
       
   271         final BlockingQueue<Notification> queue =
       
   272                 new ArrayBlockingQueue<Notification>(10);
       
   273 
       
   274         public void handleNotification(Notification notification,
       
   275                                        Object handback) {
       
   276             queue.add(notification);
       
   277         }
       
   278     }
       
   279 
       
   280     private static void test(MBeanServer vmbs) throws Exception {
       
   281         MBeanServer mmbs = MBeanServerFactory.newMBeanServer();
       
   282         ObjectName namespaceName = new ObjectName("test//:type=JMXNamespace");
       
   283         JMXNamespace namespace = new JMXNamespace(vmbs);
       
   284         mmbs.registerMBean(namespace, namespaceName);
       
   285         MBeanServer mbs = JMXNamespaces.narrowToNamespace(mmbs, "test");
       
   286 
       
   287         Set<ObjectName> names = mbs.queryNames(null, null);
       
   288         //names.remove(new ObjectName(":type=JMXNamespace"));
       
   289 
       
   290         // Make sure that notEmitterName exists according to query...
       
   291         System.out.println("Checking query");
       
   292         if (!names.contains(notEmitterName))
       
   293             throw new Exception("Bad query result: " + names);
       
   294 
       
   295         // ...and according to getMBeanInfo
       
   296         System.out.println("Checking getMBeanInfo(" + notEmitterName + ")");
       
   297         MBeanInfo mbi = mbs.getMBeanInfo(notEmitterName);
       
   298         if (mbi.getNotifications().length > 0)
       
   299             throw new Exception("notEmitter has NotificationInfo");
       
   300 
       
   301         // Make sure we get the right exception for getMBeanInfo on a
       
   302         // non-existent MBean
       
   303         System.out.println("Checking getMBeanInfo on a non-existent MBean");
       
   304         try {
       
   305             mbi = mbs.getMBeanInfo(nonExistentName);
       
   306             throw new Exception("getMBI succeeded but should not have");
       
   307         } catch (InstanceNotFoundException e) {
       
   308         }
       
   309 
       
   310         // Make sure we get the right exception for addNotificationListener on a
       
   311         // non-existent MBean
       
   312         System.out.println(
       
   313                 "Checking addNotificationListener on a non-existent MBean");
       
   314         try {
       
   315             mbs.addNotificationListener(
       
   316                     nonExistentName, new NothingListener(), null, null);
       
   317             throw new Exception("addNL succeeded but should not have");
       
   318         } catch (InstanceNotFoundException e) {
       
   319         }
       
   320 
       
   321         // Make sure we get the right exception for isInstanceOf on a
       
   322         // non-existent MBean
       
   323         System.out.println(
       
   324                 "Checking isInstanceOf on a non-existent MBean");
       
   325         for (Class<?> c : new Class<?>[] {
       
   326             Object.class, NotificationBroadcaster.class, NotificationEmitter.class,
       
   327         }) {
       
   328             try {
       
   329                 boolean is = mbs.isInstanceOf(nonExistentName, c.getName());
       
   330                 throw new Exception(
       
   331                         "isInstanceOf " + c.getName() +
       
   332                         " succeeded but should not have");
       
   333             } catch (InstanceNotFoundException e) {
       
   334             }
       
   335         }
       
   336 
       
   337         // Make sure isInstanceOf works correctly for classes without special
       
   338         // treatment
       
   339         System.out.println(
       
   340                 "Checking isInstanceOf on normal classes");
       
   341         for (ObjectName name : names) {
       
   342             boolean isNothing = mbs.isInstanceOf(name, NothingMBean.class.getName());
       
   343             if (!isNothing) {
       
   344                 throw new Exception("isInstanceOf " + NothingMBean.class.getName() +
       
   345                         " returned false, should be true");
       
   346             }
       
   347             boolean isTimer = mbs.isInstanceOf(name, TimerMBean.class.getName());
       
   348             if (isTimer) {
       
   349                 throw new Exception("isInstanceOf " + TimerMBean.class.getName() +
       
   350                         " returned true, should be false");
       
   351             }
       
   352         }
       
   353 
       
   354         // Make sure that addNL on notEmitterName gets the right exception
       
   355         System.out.println("Checking addNL on non-broadcaster");
       
   356         try {
       
   357             mbs.addNotificationListener(
       
   358                     notEmitterName, new NothingListener(), null, null);
       
   359             throw new Exception("addNL succeeded but should not have");
       
   360         } catch (RuntimeOperationsException e) {
       
   361             if (!(e.getCause() instanceof IllegalArgumentException))
       
   362                 throw new Exception("Wrong exception from addNL", e);
       
   363         }
       
   364 
       
   365         if (!(vmbs instanceof SendNotification)) {
       
   366             System.out.println("Not testing notifications for this implementation");
       
   367             return;
       
   368         }
       
   369 
       
   370         QueueListener qListener = new QueueListener();
       
   371 
       
   372         System.out.println("Testing addNL on emitters");
       
   373         mbs.addNotificationListener(emitterName1, qListener, null, null);
       
   374         mbs.addNotificationListener(emitterName2, qListener, null, null);
       
   375 
       
   376         System.out.println("Testing that listeners work");
       
   377         Notification notif = new Notification("notif.type", "source", 0L);
       
   378 
       
   379         ((SendNotification) vmbs).sendNotification(notif);
       
   380         testListeners(qListener, "notif.type", 2);
       
   381 
       
   382         System.out.println("Testing 2-arg removeNL on emitter1");
       
   383         mbs.removeNotificationListener(emitterName1, qListener);
       
   384 
       
   385         ((SendNotification) vmbs).sendNotification(notif);
       
   386         testListeners(qListener, "notif.type", 1);
       
   387 
       
   388         System.out.println("Testing 4-arg removeNL on emitter2");
       
   389         mbs.removeNotificationListener(emitterName2, qListener, null, null);
       
   390 
       
   391         ((SendNotification) vmbs).sendNotification(notif);
       
   392         testListeners(qListener, "notif.type", 0);
       
   393     }
       
   394 
       
   395     private static void testListeners(
       
   396             QueueListener qListener, String expectedNotifType, int expectedNotifs)
       
   397             throws Exception {
       
   398         for (int i = 1; i <= expectedNotifs; i++) {
       
   399             Notification rNotif = qListener.queue.poll(1, TimeUnit.SECONDS);
       
   400             if (rNotif == null)
       
   401                 throw new Exception("Notification " + i + " never arrived");
       
   402             if (!rNotif.getType().equals(expectedNotifType))
       
   403                 throw new Exception("Wrong type notif: " + rNotif.getType());
       
   404         }
       
   405         Notification xNotif = qListener.queue.poll(10, TimeUnit.MILLISECONDS);
       
   406         if (xNotif != null)
       
   407             throw new Exception("Extra notif: " + xNotif);
       
   408     }
       
   409 }