| /* |
| * 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 |
| * @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<Notification> 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<String, Object> map = Utils.parseParameters(args) ; |
| |
| // Run test |
| MXBeanNotifTest test = new MXBeanNotifTest(); |
| test.run(map); |
| |
| } |
| |
| protected void parseArgs(Map<String, Object> 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<String, Object> args) { |
| |
| System.out.println("MXBeanNotifTest::run: Start") ; |
| int errorCount = 0 ; |
| |
| try { |
| parseArgs(args); |
| notifList = new ArrayBlockingQueue<Notification>(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<String, String> 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<String, String>" |
| + " attribute"); |
| String typeName = |
| "java.util.Map<java.lang.String,java.lang.String>"; |
| 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); |
| } |
| |
| } |