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