/* * Copyright (c) 2005, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * @test * @bug 8058865 * @summary Checks MXBean proper registration both as its implementation class and interface * @author Olivier Lagneau * @modules java.management.rmi * @library /lib/testlibrary * @compile Basic.java * @run main/othervm/timeout=300 -DDEBUG_STANDARD MXBeanNotifTest -numOfNotifications 239 -timeForNotificationInSeconds 4 */ import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.TimeUnit; import java.lang.management.ManagementFactory; import javax.management.Attribute; import javax.management.Descriptor; import javax.management.ImmutableDescriptor; import javax.management.MBeanServer; import javax.management.MBeanInfo; import javax.management.MBeanNotificationInfo; import javax.management.Notification; import javax.management.NotificationListener; import javax.management.MBeanServerConnection; import javax.management.ObjectName; import javax.management.remote.JMXConnector; import javax.management.remote.JMXConnectorFactory; import javax.management.remote.JMXConnectorServer; import javax.management.remote.JMXConnectorServerFactory; import javax.management.remote.JMXServiceURL; import javax.management.openmbean.CompositeType; import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeDataSupport; import javax.management.openmbean.OpenType; import javax.management.openmbean.SimpleType; import javax.management.openmbean.TabularData; import javax.management.openmbean.TabularDataSupport; import javax.management.openmbean.TabularType; public class MXBeanNotifTest implements NotificationListener { private static String BASIC_MXBEAN_CLASS_NAME = "Basic"; private static String BASIC_MXBEAN_INTERFACE_NAME = "BasicMXBean"; private long timeForNotificationInSeconds = 3L; private int numOfNotifications = 1; private BlockingQueue notifList = null; private int numOfNotifDescriptorElements = 13; /* * First Debug properties and arguments are collect in expected * map (argName, value) format, then calls original test's run method. */ public static void main(String args[]) throws Exception { System.out.println("================================================="); // Parses parameters Utils.parseDebugProperties(); Map map = Utils.parseParameters(args) ; // Run test MXBeanNotifTest test = new MXBeanNotifTest(); test.run(map); } protected void parseArgs(Map args) throws Exception { String arg = null; // Init numOfNotifications // It is the number of notifications we should trigger and check. arg = (String)args.get("-numOfNotifications") ; if (arg != null) { numOfNotifications = (new Integer(arg)).intValue(); } // Init timeForNotificationInSeconds // It is the maximum time in seconds we wait for each notification. arg = (String)args.get("-timeForEachNotificationInSeconds") ; if (arg != null) { timeForNotificationInSeconds = (new Long(arg)).longValue(); } } public void run(Map args) { System.out.println("MXBeanNotifTest::run: Start") ; int errorCount = 0 ; try { parseArgs(args); notifList = new ArrayBlockingQueue(numOfNotifications); // JMX MbeanServer used inside single VM as if remote. MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); JMXServiceURL url = new JMXServiceURL("rmi", null, 0); JMXConnectorServer cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); cs.start(); JMXServiceURL addr = cs.getAddress(); JMXConnector cc = JMXConnectorFactory.connect(addr); MBeanServerConnection mbsc = cc.getMBeanServerConnection(); // ---- System.out.println("MXBeanNotifTest::run: Create and register the MBean"); ObjectName objName = new ObjectName("sqe:type=Basic,protocol=rmi") ; mbsc.createMBean(BASIC_MXBEAN_CLASS_NAME, objName); System.out.println("---- OK\n") ; // ---- System.out.println("MXBeanNotifTest::run: Add me as notification listener"); mbsc.addNotificationListener(objName, this, null, null); // ---- System.out.println("MXBeanNotifTest::run: Retrieve the Descriptor" + " that should be in MBeanNotificationInfo"); TabularData tabData = (TabularData)mbsc.getAttribute(objName, "NotifDescriptorAsMapAtt"); Map descrMap = new HashMap<>(); for (Iterator it = tabData.values().iterator(); it.hasNext(); ) { CompositeData compData = (CompositeData)it.next(); descrMap.put((String)compData.get("key"), (String)compData.get("value")); } Descriptor refNotifDescriptor = new ImmutableDescriptor(descrMap); System.out.println("---- OK\n") ; // ---- // Because the MBean holding the targeted attribute is MXBean, we // should use for the setAttribute a converted form for the // attribute value as described by the MXBean mapping rules. // This explains all that lovely stuff for creating a // TabularDataSupport. // // WARNING : the MBeanInfo of the MXBean used on opposite side // is computed when the MBean is registered. // It means the Descriptor considered for the MBeanNotificationInfo // is not the one we set in the lines below, it is too late. // However, we check that set is harmless when we check // the MBeanNotificationInfo. // System.out.println("MXBeanNotifTest::run: Set a Map" + " attribute"); String typeName = "java.util.Map"; String[] keyValue = new String[] {"key", "value"}; OpenType[] openTypes = new OpenType[] {SimpleType.STRING, SimpleType.STRING}; CompositeType rowType = new CompositeType(typeName, typeName, keyValue, keyValue, openTypes); TabularType tabType = new TabularType(typeName, typeName, rowType, new String[]{"key"}); TabularDataSupport convertedDescrMap = new TabularDataSupport(tabType); for (int i = 0; i < numOfNotifDescriptorElements; i++) { Object[] descrValue = {"field" + i, "value" + i}; CompositeData data = new CompositeDataSupport(rowType, keyValue, descrValue); convertedDescrMap.put(data); } Attribute descrAtt = new Attribute("NotifDescriptorAsMapAtt", convertedDescrMap); mbsc.setAttribute(objName, descrAtt); System.out.println("---- OK\n") ; // ---- System.out.println("MXBeanNotifTest::run: Compare the Descriptor from" + " the MBeanNotificationInfo against a reference"); MBeanInfo mbInfo = mbsc.getMBeanInfo(objName); errorCount += checkMBeanInfo(mbInfo, refNotifDescriptor); System.out.println("---- DONE\n") ; // ---- System.out.println("Check isInstanceOf(Basic)"); if ( ! mbsc.isInstanceOf(objName, BASIC_MXBEAN_CLASS_NAME) ) { errorCount++; System.out.println("---- ERROR isInstanceOf returned false\n") ; } else { System.out.println("---- OK\n") ; } // ---- System.out.println("Check isInstanceOf(BasicMXBean)"); if ( ! mbsc.isInstanceOf(objName, BASIC_MXBEAN_INTERFACE_NAME) ) { errorCount++; System.out.println("---- ERROR isInstanceOf returned false\n") ; } else { System.out.println("---- OK\n") ; } // ---- System.out.println("MXBeanNotifTest::run: Ask for " + numOfNotifications + " notification(s)"); Object[] sendNotifParam = new Object[1]; String[] sendNotifSig = new String[]{"java.lang.String"}; for (int i = 0; i < numOfNotifications; i++) { // Select which type of notification we ask for if ( i % 2 == 0 ) { sendNotifParam[0] = Basic.NOTIF_TYPE_0; } else { sendNotifParam[0] = Basic.NOTIF_TYPE_1; } // Trigger notification emission mbsc.invoke(objName, "sendNotification", sendNotifParam, sendNotifSig); // Wait for it then check it when it comes early enough Notification notif = notifList.poll(timeForNotificationInSeconds, TimeUnit.SECONDS) ; // The very first notification is likely to come in slower than // all the others. Because that test isn't targeting the speed // notifications are delivered with, we prefer to secure it. if (i == 0 && notif == null) { System.out.println("MXBeanNotifTest::run: Wait extra " + timeForNotificationInSeconds + " second(s) the " + " very first notification"); notif = notifList.poll(timeForNotificationInSeconds, TimeUnit.SECONDS); } if ( notif == null ) { errorCount++; System.out.println("---- ERROR No notification received" + " within allocated " + timeForNotificationInSeconds + " second(s) !"); } else { errorCount += checkNotification(notif, (String)sendNotifParam[0], Basic.NOTIFICATION_MESSAGE, objName); } } int toc = 0; while ( notifList.size() < 2 && toc < 10 ) { Thread.sleep(499); toc++; } System.out.println("---- DONE\n") ; } catch(Exception e) { Utils.printThrowable(e, true) ; throw new RuntimeException(e); } if ( errorCount == 0 ) { System.out.println("MXBeanNotifTest::run: Done without any error") ; } else { System.out.println("MXBeanNotifTest::run: Done with " + errorCount + " error(s)") ; throw new RuntimeException("errorCount = " + errorCount); } } private int checkMBeanInfo(MBeanInfo mbi, Descriptor refDescr) { MBeanNotificationInfo[] notifsInfo = mbi.getNotifications(); int res = 0; for (MBeanNotificationInfo mbni : notifsInfo) { if ( mbni.getDescriptor().equals(refDescr) ) { System.out.println("(OK)"); } else { System.out.println("(ERROR) Descriptor of the notification is " + mbni.getDescriptor() + " as we expect " + refDescr); res++; } } return res; } private int checkNotification(Notification notif, String refType, String refMessage, ObjectName refSource) { int res = 0; Utils.debug(Utils.DEBUG_VERBOSE, "\t getSource " + notif.getSource()); Utils.debug(Utils.DEBUG_VERBOSE, "\t getMessage " + notif.getMessage()); Utils.debug(Utils.DEBUG_VERBOSE, "\t getSequenceNumber " + notif.getSequenceNumber()); Utils.debug(Utils.DEBUG_VERBOSE, "\t getTimeStamp " + notif.getTimeStamp()); Utils.debug(Utils.DEBUG_VERBOSE, "\t getType " + notif.getType()); Utils.debug(Utils.DEBUG_VERBOSE, "\t getUserData " + notif.getUserData()); if ( ! notif.getType().equals(refType) ) { res++; System.out.println("(ERROR) Type is not " + refType + " in notification\n" + notif); } else { if ( notif.getType().equals(Basic.NOTIF_TYPE_0) && ! (notif instanceof javax.management.Notification) ) { res++; System.out.println("(ERROR) Notification is not instance of " + " javax.management.Notification but rather " + notif.getClass().getName()); } else if ( notif.getType().equals(Basic.NOTIF_TYPE_1) && ! (notif instanceof SqeNotification) ) { res++; System.out.println("(ERROR) Notification is not instance of " + " javasoft.sqe.jmx.share.SqeNotification but rather " + notif.getClass().getName()); } } if ( ! notif.getMessage().equals(refMessage) ) { res++; System.out.println("(ERROR) Message is not " + refMessage + " in notification\n" + notif); } if ( ! notif.getSource().equals(refSource) ) { res++; System.out.println("(ERROR) Source is not " + refSource + " in notification\n" + notif); } return res; } public void handleNotification(Notification notification, Object handback) { Utils.debug(Utils.DEBUG_VERBOSE, "MXBeanNotifTest::handleNotification: Received " + notification); notifList.add(notification); } }