0N/A/*
2362N/A * Copyright (c) 2004, 2008, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
0N/A * published by the Free Software Foundation.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A/*
0N/A * @test
0N/A * @bug 5012634
0N/A * @summary Test that JMX classes use fully-qualified class names
0N/A * in MBeanNotificationInfo
0N/A * @author Eamonn McManus
0N/A * @run clean NotificationInfoTest
0N/A * @run build NotificationInfoTest
0N/A * @run main NotificationInfoTest
0N/A */
0N/A
0N/Aimport java.io.*;
527N/Aimport java.lang.management.*;
0N/Aimport java.lang.reflect.*;
0N/Aimport java.net.*;
0N/Aimport java.security.CodeSource;
0N/Aimport java.util.*;
0N/Aimport java.util.jar.*;
0N/Aimport javax.management.*;
0N/Aimport javax.management.relation.*;
527N/Aimport javax.management.remote.*;
527N/Aimport javax.management.remote.rmi.*;
0N/A
0N/A/*
0N/A * This test finds all classes in the same code-base as the JMX
0N/A * classes that look like Standard MBeans, and checks that if they are
0N/A * NotificationBroadcasters they declare existent notification types.
0N/A * A class looks like a Standard MBean if both Thing and ThingMBean
0N/A * classes exist. So for example javax.management.timer.Timer looks
0N/A * like a Standard MBean because javax.management.timer.TimerMBean
0N/A * exists. Timer is instanceof NotificationBroadcaster, so we expect
0N/A * that ((NotificationBroadcaster) timer).getNotificationInfo() will
0N/A * return an array of MBeanNotificationInfo where each entry has a
0N/A * getName() that names an existent Java class that is a Notification.
0N/A *
0N/A * An MBean is "suspicious" if it is a NotificationBroadcaster but its
0N/A * MBeanNotificationInfo[] is empty. This is legal, but surprising.
0N/A *
0N/A * In order to call getNotificationInfo(), we need an instance of the
0N/A * class. We attempt to make one by calling a public no-arg
0N/A * constructor. But the "construct" method below can be extended to
0N/A * construct specific MBean classes for which the no-arg constructor
0N/A * doesn't exist.
0N/A *
0N/A * The test is obviously not exhaustive, but does catch the cases that
0N/A * failed in 5012634.
0N/A */
0N/Apublic class NotificationInfoTest {
0N/A // class or object names where the test failed
527N/A private static final Set<String> failed = new TreeSet<String>();
0N/A
0N/A // class or object names where there were no MBeanNotificationInfo entries
527N/A private static final Set<String> suspicious = new TreeSet<String>();
0N/A
0N/A public static void main(String[] args) throws Exception {
0N/A System.out.println("Checking that all known MBeans that are " +
0N/A "NotificationBroadcasters have sane " +
0N/A "MBeanInfo.getNotifications()");
0N/A
0N/A System.out.println("Checking platform MBeans...");
0N/A checkPlatformMBeans();
0N/A
0N/A CodeSource cs =
0N/A javax.management.MBeanServer.class.getProtectionDomain()
0N/A .getCodeSource();
0N/A URL codeBase;
0N/A if (cs == null) {
527N/A String javaHome = System.getProperty("java.home");
527N/A String[] candidates = {"/lib/rt.jar", "/classes/"};
527N/A codeBase = null;
527N/A for (String candidate : candidates) {
527N/A File file = new File(javaHome + candidate);
527N/A if (file.exists()) {
527N/A codeBase = file.toURI().toURL();
527N/A break;
527N/A }
527N/A }
527N/A if (codeBase == null) {
527N/A throw new Exception(
527N/A "Could not determine codeBase for java.home=" + javaHome);
527N/A }
0N/A } else
0N/A codeBase = cs.getLocation();
0N/A
0N/A System.out.println();
0N/A System.out.println("Looking for standard MBeans...");
0N/A String[] classes = findStandardMBeans(codeBase);
0N/A
0N/A System.out.println("Testing standard MBeans...");
0N/A for (int i = 0; i < classes.length; i++) {
0N/A String name = classes[i];
527N/A Class<?> c;
0N/A try {
0N/A c = Class.forName(name);
0N/A } catch (Throwable e) {
0N/A System.out.println(name + ": cannot load (not public?): " + e);
0N/A continue;
0N/A }
0N/A if (!NotificationBroadcaster.class.isAssignableFrom(c)) {
0N/A System.out.println(name + ": not a NotificationBroadcaster");
0N/A continue;
0N/A }
527N/A if (Modifier.isAbstract(c.getModifiers())) {
527N/A System.out.println(name + ": abstract class");
527N/A continue;
527N/A }
0N/A
0N/A NotificationBroadcaster mbean;
527N/A Constructor<?> constr;
0N/A try {
527N/A constr = c.getConstructor();
0N/A } catch (Exception e) {
0N/A System.out.println(name + ": no public no-arg constructor: "
0N/A + e);
0N/A continue;
0N/A }
0N/A try {
527N/A mbean = (NotificationBroadcaster) constr.newInstance();
0N/A } catch (Exception e) {
0N/A System.out.println(name + ": no-arg constructor failed: " + e);
0N/A continue;
0N/A }
0N/A
0N/A check(mbean);
0N/A }
0N/A
0N/A System.out.println();
0N/A System.out.println("Testing some explicit cases...");
0N/A
0N/A check(new RelationService(false));
0N/A /*
0N/A We can't do this:
0N/A check(new RequiredModelMBean());
0N/A because the Model MBean spec more or less forces us to use the
0N/A names GENERIC and ATTRIBUTE_CHANGE for its standard notifs.
0N/A */
0N/A checkRMIConnectorServer();
0N/A
0N/A System.out.println();
0N/A if (!suspicious.isEmpty())
0N/A System.out.println("SUSPICIOUS CLASSES: " + suspicious);
0N/A
0N/A if (failed.isEmpty())
0N/A System.out.println("TEST PASSED");
0N/A else {
0N/A System.out.println("TEST FAILED: " + failed);
0N/A System.exit(1);
0N/A }
0N/A }
0N/A
0N/A private static void check(NotificationBroadcaster mbean)
0N/A throws Exception {
0N/A System.out.print(mbean.getClass().getName() + ": ");
0N/A
0N/A check(mbean.getClass().getName(), mbean.getNotificationInfo());
0N/A }
0N/A
0N/A private static void checkPlatformMBeans() throws Exception {
527N/A MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
527N/A Set<ObjectName> mbeanNames = mbs.queryNames(null, null);
527N/A for (ObjectName name : mbeanNames) {
0N/A if (!mbs.isInstanceOf(name,
0N/A NotificationBroadcaster.class.getName())) {
0N/A System.out.println(name + ": not a NotificationBroadcaster");
0N/A } else {
0N/A MBeanInfo mbi = mbs.getMBeanInfo(name);
0N/A check(name.toString(), mbi.getNotifications());
0N/A }
0N/A }
0N/A }
0N/A
0N/A private static void checkRMIConnectorServer() throws Exception {
527N/A JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");
527N/A RMIConnectorServer connector = new RMIConnectorServer(url, null);
527N/A check(connector);
0N/A }
0N/A
0N/A private static void check(String what, MBeanNotificationInfo[] mbnis) {
0N/A System.out.print(what + ": checking notification info: ");
0N/A
0N/A if (mbnis.length == 0) {
0N/A System.out.println("NONE (suspicious)");
0N/A suspicious.add(what);
0N/A return;
0N/A }
0N/A
0N/A // Each MBeanNotificationInfo.getName() should be an existent
0N/A // Java class that is Notification or a subclass of it
0N/A for (int j = 0; j < mbnis.length; j++) {
0N/A String notifClassName = mbnis[j].getName();
0N/A Class notifClass;
0N/A try {
0N/A notifClass = Class.forName(notifClassName);
0N/A } catch (Exception e) {
0N/A System.out.print("FAILED(" + notifClassName + ": " + e +
0N/A ") ");
0N/A failed.add(what);
0N/A continue;
0N/A }
0N/A if (!Notification.class.isAssignableFrom(notifClass)) {
0N/A System.out.print("FAILED(" + notifClassName +
0N/A ": not a Notification) ");
0N/A failed.add(what);
0N/A continue;
0N/A }
0N/A System.out.print("OK(" + notifClassName + ") ");
0N/A }
0N/A System.out.println();
0N/A }
0N/A
0N/A private static String[] findStandardMBeans(URL codeBase)
0N/A throws Exception {
527N/A Set<String> names;
0N/A if (codeBase.getProtocol().equalsIgnoreCase("file")
0N/A && codeBase.toString().endsWith("/"))
0N/A names = findStandardMBeansFromDir(codeBase);
0N/A else
0N/A names = findStandardMBeansFromJar(codeBase);
0N/A
527N/A Set<String> standardMBeanNames = new TreeSet<String>();
527N/A for (String name : names) {
0N/A if (name.endsWith("MBean")) {
0N/A String prefix = name.substring(0, name.length() - 5);
0N/A if (names.contains(prefix))
0N/A standardMBeanNames.add(prefix);
0N/A }
0N/A }
527N/A return standardMBeanNames.toArray(new String[0]);
0N/A }
0N/A
527N/A private static Set<String> findStandardMBeansFromJar(URL codeBase)
0N/A throws Exception {
0N/A InputStream is = codeBase.openStream();
0N/A JarInputStream jis = new JarInputStream(is);
527N/A Set<String> names = new TreeSet<String>();
0N/A JarEntry entry;
0N/A while ((entry = jis.getNextJarEntry()) != null) {
0N/A String name = entry.getName();
0N/A if (!name.endsWith(".class"))
0N/A continue;
0N/A name = name.substring(0, name.length() - 6);
0N/A name = name.replace('/', '.');
0N/A names.add(name);
0N/A }
0N/A return names;
0N/A }
0N/A
527N/A private static Set<String> findStandardMBeansFromDir(URL codeBase)
0N/A throws Exception {
0N/A File dir = new File(new URI(codeBase.toString()));
527N/A Set<String> names = new TreeSet<String>();
0N/A scanDir(dir, "", names);
0N/A return names;
0N/A }
0N/A
527N/A private static void scanDir(File dir, String prefix, Set<String> names)
0N/A throws Exception {
0N/A File[] files = dir.listFiles();
0N/A if (files == null)
0N/A return;
0N/A for (int i = 0; i < files.length; i++) {
0N/A File f = files[i];
0N/A String name = f.getName();
0N/A String p = (prefix.equals("")) ? name : prefix + "." + name;
0N/A if (f.isDirectory())
0N/A scanDir(f, p, names);
0N/A else if (name.endsWith(".class")) {
0N/A p = p.substring(0, p.length() - 6);
0N/A names.add(p);
0N/A }
0N/A }
0N/A }
0N/A}