1 /*
   2  * Copyright (c) 2005, 2015, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 8058865
  27  * @summary Checks MXBean proper registration both as its implementation class and interface
  28  * @author Olivier Lagneau
  29  *
  30  * @library /lib/testlibrary
  31  * @modules java.management.rmi
  32  *
  33  * @compile Basic.java
  34  * @run main/othervm/timeout=300 -DDEBUG_STANDARD MXBeanNotifTest -numOfNotifications 239 -timeForNotificationInSeconds 4
  35  */
  36 
  37 import java.util.HashMap;
  38 import java.util.Iterator;
  39 import java.util.Map;
  40 
  41 import java.util.concurrent.ArrayBlockingQueue;
  42 import java.util.concurrent.BlockingQueue;
  43 import java.util.concurrent.TimeUnit;
  44 
  45 import java.lang.management.ManagementFactory;
  46 
  47 import javax.management.Attribute;
  48 import javax.management.Descriptor;
  49 import javax.management.ImmutableDescriptor;
  50 import javax.management.MBeanServer;
  51 import javax.management.MBeanInfo;
  52 import javax.management.MBeanNotificationInfo;
  53 import javax.management.Notification;
  54 import javax.management.NotificationListener;
  55 import javax.management.MBeanServerConnection;
  56 import javax.management.ObjectName;
  57 
  58 import javax.management.remote.JMXConnector;
  59 import javax.management.remote.JMXConnectorFactory;
  60 import javax.management.remote.JMXConnectorServer;
  61 import javax.management.remote.JMXConnectorServerFactory;
  62 import javax.management.remote.JMXServiceURL;
  63 
  64 import javax.management.openmbean.CompositeType;
  65 import javax.management.openmbean.CompositeData;
  66 import javax.management.openmbean.CompositeDataSupport;
  67 import javax.management.openmbean.OpenType;
  68 import javax.management.openmbean.SimpleType;
  69 import javax.management.openmbean.TabularData;
  70 import javax.management.openmbean.TabularDataSupport;
  71 import javax.management.openmbean.TabularType;
  72 
  73 public class MXBeanNotifTest implements NotificationListener {
  74 
  75     private static String BASIC_MXBEAN_CLASS_NAME = "Basic";
  76     private static String BASIC_MXBEAN_INTERFACE_NAME = "BasicMXBean";
  77 
  78     private long timeForNotificationInSeconds = 3L;
  79     private int numOfNotifications = 1;
  80     private BlockingQueue<Notification> notifList = null;
  81     private int numOfNotifDescriptorElements = 13;
  82 
  83     /*
  84      * First Debug properties and arguments are collect in expected
  85      * map  (argName, value) format, then calls original test's run method.
  86      */
  87     public static void main(String args[]) throws Exception {
  88 
  89         System.out.println("=================================================");
  90 
  91         // Parses parameters
  92         Utils.parseDebugProperties();
  93         Map<String, Object> map = Utils.parseParameters(args) ;
  94 
  95         // Run test
  96         MXBeanNotifTest test = new MXBeanNotifTest();
  97         test.run(map);
  98 
  99     }
 100 
 101     protected void parseArgs(Map<String, Object> args) throws Exception {
 102 
 103         String arg = null;
 104 
 105         // Init numOfNotifications
 106         // It is the number of notifications we should trigger and check.
 107         arg = (String)args.get("-numOfNotifications") ;
 108         if (arg != null) {
 109             numOfNotifications = (new Integer(arg)).intValue();
 110         }
 111 
 112         // Init timeForNotificationInSeconds
 113         // It is the maximum time in seconds we wait for each notification.
 114         arg = (String)args.get("-timeForEachNotificationInSeconds") ;
 115         if (arg != null) {
 116             timeForNotificationInSeconds = (new Long(arg)).longValue();
 117         }
 118 
 119     }
 120 
 121     public void run(Map<String, Object> args) {
 122 
 123         System.out.println("MXBeanNotifTest::run: Start") ;
 124         int errorCount = 0 ;
 125 
 126         try {
 127             parseArgs(args);
 128             notifList = new ArrayBlockingQueue<Notification>(numOfNotifications);
 129 
 130             // JMX MbeanServer used inside single VM as if remote.
 131             MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
 132 
 133             JMXServiceURL url = new JMXServiceURL("rmi", null, 0);
 134             JMXConnectorServer cs =
 135                 JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
 136             cs.start();
 137 
 138             JMXServiceURL addr = cs.getAddress();
 139             JMXConnector cc = JMXConnectorFactory.connect(addr);
 140             MBeanServerConnection mbsc = cc.getMBeanServerConnection();
 141 
 142             // ----
 143             System.out.println("MXBeanNotifTest::run: Create and register the MBean");
 144             ObjectName objName = new ObjectName("sqe:type=Basic,protocol=rmi") ;
 145             mbsc.createMBean(BASIC_MXBEAN_CLASS_NAME, objName);
 146             System.out.println("---- OK\n") ;
 147 
 148             // ----
 149             System.out.println("MXBeanNotifTest::run: Add me as notification listener");
 150             mbsc.addNotificationListener(objName, this, null, null);
 151 
 152             // ----
 153             System.out.println("MXBeanNotifTest::run: Retrieve the Descriptor"
 154                     + " that should be in MBeanNotificationInfo");
 155             TabularData tabData =
 156                 (TabularData)mbsc.getAttribute(objName, "NotifDescriptorAsMapAtt");
 157             Map<String, String> descrMap = new HashMap<>();
 158 
 159             for (Iterator<?> it = tabData.values().iterator(); it.hasNext(); ) {
 160                 CompositeData compData = (CompositeData)it.next();
 161                 descrMap.put((String)compData.get("key"),
 162                         (String)compData.get("value"));
 163             }
 164 
 165             Descriptor refNotifDescriptor = new ImmutableDescriptor(descrMap);
 166             System.out.println("---- OK\n") ;
 167 
 168             // ----
 169             // Because the MBean holding the targeted attribute is MXBean, we
 170             // should use for the setAttribute a converted form for the
 171             // attribute value as described by the MXBean mapping rules.
 172             // This explains all that lovely stuff for creating a
 173             // TabularDataSupport.
 174             //
 175             // WARNING : the MBeanInfo of the MXBean used on opposite side
 176             // is computed when the MBean is registered.
 177             // It means the Descriptor considered for the MBeanNotificationInfo
 178             // is not the one we set in the lines below, it is too late.
 179             // However, we check that set is harmless when we check
 180             // the MBeanNotificationInfo.
 181             //
 182             System.out.println("MXBeanNotifTest::run: Set a Map<String, String>"
 183                     + " attribute");
 184             String typeName =
 185                     "java.util.Map<java.lang.String,java.lang.String>";
 186             String[] keyValue = new String[] {"key", "value"};
 187             OpenType<?>[] openTypes =
 188                     new OpenType<?>[] {SimpleType.STRING, SimpleType.STRING};
 189             CompositeType rowType = new CompositeType(typeName, typeName,
 190                     keyValue, keyValue, openTypes);
 191             TabularType tabType = new TabularType(typeName, typeName,
 192                     rowType, new String[]{"key"});
 193             TabularDataSupport convertedDescrMap =
 194                     new TabularDataSupport(tabType);
 195 
 196             for (int i = 0; i < numOfNotifDescriptorElements; i++) {
 197                 Object[] descrValue = {"field" + i, "value" + i};
 198                 CompositeData data =
 199                         new CompositeDataSupport(rowType, keyValue, descrValue);
 200                 convertedDescrMap.put(data);
 201             }
 202 
 203             Attribute descrAtt =
 204                     new Attribute("NotifDescriptorAsMapAtt", convertedDescrMap);
 205             mbsc.setAttribute(objName, descrAtt);
 206             System.out.println("---- OK\n") ;
 207 
 208             // ----
 209             System.out.println("MXBeanNotifTest::run: Compare the Descriptor from"
 210                     + " the MBeanNotificationInfo against a reference");
 211             MBeanInfo mbInfo = mbsc.getMBeanInfo(objName);
 212             errorCount += checkMBeanInfo(mbInfo, refNotifDescriptor);
 213             System.out.println("---- DONE\n") ;
 214 
 215             // ----
 216             System.out.println("Check isInstanceOf(Basic)");
 217 
 218             if ( ! mbsc.isInstanceOf(objName, BASIC_MXBEAN_CLASS_NAME) ) {
 219                 errorCount++;
 220                 System.out.println("---- ERROR isInstanceOf returned false\n") ;
 221             } else {
 222                 System.out.println("---- OK\n") ;
 223             }
 224 
 225             // ----
 226             System.out.println("Check isInstanceOf(BasicMXBean)");
 227 
 228             if ( ! mbsc.isInstanceOf(objName, BASIC_MXBEAN_INTERFACE_NAME) ) {
 229                 errorCount++;
 230                 System.out.println("---- ERROR isInstanceOf returned false\n") ;
 231             } else {
 232                 System.out.println("---- OK\n") ;
 233             }
 234 
 235             // ----
 236             System.out.println("MXBeanNotifTest::run: Ask for "
 237                     + numOfNotifications + " notification(s)");
 238             Object[] sendNotifParam = new Object[1];
 239             String[] sendNotifSig = new String[]{"java.lang.String"};
 240 
 241             for (int i = 0; i < numOfNotifications; i++) {
 242                 // Select which type of notification we ask for
 243                 if ( i % 2 == 0 ) {
 244                     sendNotifParam[0] = Basic.NOTIF_TYPE_0;
 245                 } else {
 246                     sendNotifParam[0] = Basic.NOTIF_TYPE_1;
 247                 }
 248 
 249                 // Trigger notification emission
 250                 mbsc.invoke(objName,
 251                         "sendNotification",
 252                         sendNotifParam,
 253                         sendNotifSig);
 254 
 255                 // Wait for it then check it when it comes early enough
 256                 Notification notif =
 257                         notifList.poll(timeForNotificationInSeconds,
 258                         TimeUnit.SECONDS) ;
 259                 // The very first notification is likely to come in slower than
 260                 // all the others. Because that test isn't targeting the speed
 261                 // notifications are delivered with, we prefer to secure it.
 262                 if (i == 0 && notif == null) {
 263                     System.out.println("MXBeanNotifTest::run: Wait extra "
 264                             + timeForNotificationInSeconds + " second(s) the "
 265                             + " very first notification");
 266                     notif = notifList.poll(timeForNotificationInSeconds,
 267                             TimeUnit.SECONDS);
 268                 }
 269 
 270                 if ( notif == null ) {
 271                     errorCount++;
 272                     System.out.println("---- ERROR No notification received"
 273                             + " within allocated " + timeForNotificationInSeconds
 274                             + " second(s) !");
 275                 } else {
 276                     errorCount +=
 277                             checkNotification(notif,
 278                             (String)sendNotifParam[0],
 279                             Basic.NOTIFICATION_MESSAGE,
 280                             objName);
 281                 }
 282             }
 283 
 284             int toc = 0;
 285             while ( notifList.size() < 2 && toc < 10 ) {
 286                 Thread.sleep(499);
 287                 toc++;
 288             }
 289             System.out.println("---- DONE\n") ;
 290         } catch(Exception e) {
 291             Utils.printThrowable(e, true) ;
 292             throw new RuntimeException(e);
 293         }
 294 
 295         if ( errorCount == 0 ) {
 296             System.out.println("MXBeanNotifTest::run: Done without any error") ;
 297         } else {
 298             System.out.println("MXBeanNotifTest::run: Done with "
 299                     + errorCount
 300                     + " error(s)") ;
 301             throw new RuntimeException("errorCount = " + errorCount);
 302         }
 303     }
 304 
 305 
 306     private int checkMBeanInfo(MBeanInfo mbi, Descriptor refDescr) {
 307         MBeanNotificationInfo[] notifsInfo = mbi.getNotifications();
 308         int res = 0;
 309 
 310         for (MBeanNotificationInfo mbni : notifsInfo) {
 311             if ( mbni.getDescriptor().equals(refDescr) ) {
 312                 System.out.println("(OK)");
 313             } else {
 314                 System.out.println("(ERROR) Descriptor of the notification is "
 315                         + mbni.getDescriptor()
 316                         + " as we expect "
 317                         + refDescr);
 318                 res++;
 319             }
 320         }
 321 
 322         return res;
 323     }
 324 
 325 
 326     private int checkNotification(Notification notif,
 327             String refType,
 328             String refMessage,
 329             ObjectName refSource) {
 330         int res = 0;
 331 
 332         Utils.debug(Utils.DEBUG_VERBOSE,
 333                 "\t getSource " + notif.getSource());
 334         Utils.debug(Utils.DEBUG_VERBOSE,
 335                 "\t getMessage " + notif.getMessage());
 336         Utils.debug(Utils.DEBUG_VERBOSE,
 337                 "\t getSequenceNumber " + notif.getSequenceNumber());
 338         Utils.debug(Utils.DEBUG_VERBOSE,
 339                 "\t getTimeStamp " + notif.getTimeStamp());
 340         Utils.debug(Utils.DEBUG_VERBOSE,
 341                 "\t getType " + notif.getType());
 342         Utils.debug(Utils.DEBUG_VERBOSE,
 343                 "\t getUserData " + notif.getUserData());
 344 
 345         if ( ! notif.getType().equals(refType) ) {
 346             res++;
 347             System.out.println("(ERROR) Type is not "
 348                     + refType + " in notification\n" + notif);
 349         } else {
 350             if ( notif.getType().equals(Basic.NOTIF_TYPE_0)
 351             && ! (notif instanceof javax.management.Notification) ) {
 352                 res++;
 353                 System.out.println("(ERROR) Notification is not instance of "
 354                         + " javax.management.Notification but rather "
 355                         + notif.getClass().getName());
 356             } else if ( notif.getType().equals(Basic.NOTIF_TYPE_1)
 357             && ! (notif instanceof SqeNotification) ) {
 358                 res++;
 359                 System.out.println("(ERROR) Notification is not instance of "
 360                         + " javasoft.sqe.jmx.share.SqeNotification but rather "
 361                         + notif.getClass().getName());
 362             }
 363         }
 364 
 365         if ( ! notif.getMessage().equals(refMessage) ) {
 366             res++;
 367             System.out.println("(ERROR) Message is not "
 368                     + refMessage + " in notification\n" + notif);
 369         }
 370 
 371         if ( ! notif.getSource().equals(refSource) ) {
 372             res++;
 373             System.out.println("(ERROR) Source is not "
 374                     + refSource + " in notification\n" + notif);
 375         }
 376 
 377         return res;
 378     }
 379 
 380     public void handleNotification(Notification notification, Object handback) {
 381         Utils.debug(Utils.DEBUG_VERBOSE,
 382                 "MXBeanNotifTest::handleNotification: Received "
 383                 + notification);
 384         notifList.add(notification);
 385     }
 386 
 387 }