Path: blob/master/test/jdk/javax/management/MBeanInfo/NotificationInfoTest.java
41149 views
/*1* Copyright (c) 2004, 2015, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/*24* @test25* @bug 501263426* @summary Test that JMX classes use fully-qualified class names27* in MBeanNotificationInfo28* @author Eamonn McManus29* @modules java.management.rmi30* @run clean NotificationInfoTest31* @run build NotificationInfoTest32* @run main NotificationInfoTest33*/3435import java.io.*;36import java.lang.management.*;37import java.lang.reflect.*;38import java.net.*;39import java.nio.file.FileSystem;40import java.nio.file.FileSystems;41import java.nio.file.Files;42import java.nio.file.Path;43import java.nio.file.Paths;44import java.util.*;45import java.util.stream.Collectors;46import javax.management.*;47import javax.management.relation.*;48import javax.management.remote.*;49import javax.management.remote.rmi.*;5051/*52* This test finds all classes in the same code-base as the JMX53* classes that look like Standard MBeans, and checks that if they are54* NotificationBroadcasters they declare existent notification types.55* A class looks like a Standard MBean if both Thing and ThingMBean56* classes exist. So for example javax.management.timer.Timer looks57* like a Standard MBean because javax.management.timer.TimerMBean58* exists. Timer is instanceof NotificationBroadcaster, so we expect59* that ((NotificationBroadcaster) timer).getNotificationInfo() will60* return an array of MBeanNotificationInfo where each entry has a61* getName() that names an existent Java class that is a Notification.62*63* An MBean is "suspicious" if it is a NotificationBroadcaster but its64* MBeanNotificationInfo[] is empty. This is legal, but surprising.65*66* In order to call getNotificationInfo(), we need an instance of the67* class. We attempt to make one by calling a public no-arg68* constructor. But the "construct" method below can be extended to69* construct specific MBean classes for which the no-arg constructor70* doesn't exist.71*72* The test is obviously not exhaustive, but does catch the cases that73* failed in 5012634.74*/75public class NotificationInfoTest {76// class or object names where the test failed77private static final Set<String> failed = new TreeSet<String>();7879// class or object names where there were no MBeanNotificationInfo entries80private static final Set<String> suspicious = new TreeSet<String>();8182public static void main(String[] args) throws Exception {83System.out.println("Checking that all known MBeans that are " +84"NotificationBroadcasters have sane " +85"MBeanInfo.getNotifications()");8687System.out.println("Checking platform MBeans...");88checkPlatformMBeans();8990URL codeBase;91String home = System.getProperty("java.home");92Path classFile = Paths.get(home, "modules", "java.management");93if (Files.isDirectory(classFile)) {94codeBase = classFile.toUri().toURL();95} else {96codeBase = URI.create("jrt:/java.management").toURL();97}9899System.out.println();100System.out.println("Looking for standard MBeans...");101String[] classes = findStandardMBeans(codeBase);102103System.out.println("Testing standard MBeans...");104for (int i = 0; i < classes.length; i++) {105String name = classes[i];106Class<?> c;107try {108c = Class.forName(name);109} catch (Throwable e) {110System.out.println(name + ": cannot load (not public?): " + e);111continue;112}113if (!NotificationBroadcaster.class.isAssignableFrom(c)) {114System.out.println(name + ": not a NotificationBroadcaster");115continue;116}117if (Modifier.isAbstract(c.getModifiers())) {118System.out.println(name + ": abstract class");119continue;120}121122NotificationBroadcaster mbean;123Constructor<?> constr;124try {125constr = c.getConstructor();126} catch (Exception e) {127System.out.println(name + ": no public no-arg constructor: "128+ e);129continue;130}131try {132mbean = (NotificationBroadcaster) constr.newInstance();133} catch (Exception e) {134System.out.println(name + ": no-arg constructor failed: " + e);135continue;136}137138check(mbean);139}140141System.out.println();142System.out.println("Testing some explicit cases...");143144check(new RelationService(false));145/*146We can't do this:147check(new RequiredModelMBean());148because the Model MBean spec more or less forces us to use the149names GENERIC and ATTRIBUTE_CHANGE for its standard notifs.150*/151checkRMIConnectorServer();152153System.out.println();154if (!suspicious.isEmpty())155System.out.println("SUSPICIOUS CLASSES: " + suspicious);156157if (failed.isEmpty())158System.out.println("TEST PASSED");159else {160System.out.println("TEST FAILED: " + failed);161System.exit(1);162}163}164165private static void check(NotificationBroadcaster mbean)166throws Exception {167System.out.print(mbean.getClass().getName() + ": ");168169check(mbean.getClass().getName(), mbean.getNotificationInfo());170}171172private static void checkPlatformMBeans() throws Exception {173MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();174Set<ObjectName> mbeanNames = mbs.queryNames(null, null);175for (ObjectName name : mbeanNames) {176if (!mbs.isInstanceOf(name,177NotificationBroadcaster.class.getName())) {178System.out.println(name + ": not a NotificationBroadcaster");179} else {180MBeanInfo mbi = mbs.getMBeanInfo(name);181check(name.toString(), mbi.getNotifications());182}183}184}185186private static void checkRMIConnectorServer() throws Exception {187JMXServiceURL url = new JMXServiceURL("service:jmx:rmi://");188RMIConnectorServer connector = new RMIConnectorServer(url, null);189check(connector);190}191192private static void check(String what, MBeanNotificationInfo[] mbnis) {193System.out.print(what + ": checking notification info: ");194195if (mbnis.length == 0) {196System.out.println("NONE (suspicious)");197suspicious.add(what);198return;199}200201// Each MBeanNotificationInfo.getName() should be an existent202// Java class that is Notification or a subclass of it203for (int j = 0; j < mbnis.length; j++) {204String notifClassName = mbnis[j].getName();205Class notifClass;206try {207notifClass = Class.forName(notifClassName);208} catch (Exception e) {209System.out.print("FAILED(" + notifClassName + ": " + e +210") ");211failed.add(what);212continue;213}214if (!Notification.class.isAssignableFrom(notifClass)) {215System.out.print("FAILED(" + notifClassName +216": not a Notification) ");217failed.add(what);218continue;219}220System.out.print("OK(" + notifClassName + ") ");221}222System.out.println();223}224225private static String[] findStandardMBeans(URL codeBase) throws Exception {226Set<String> names;227if (codeBase.getProtocol().equalsIgnoreCase("jrt")) {228names = findStandardMBeansFromRuntime();229} else {230names = findStandardMBeansFromDir(codeBase);231}232233Set<String> standardMBeanNames = new TreeSet<String>();234for (String name : names) {235if (name.endsWith("MBean")) {236String prefix = name.substring(0, name.length() - 5);237if (names.contains(prefix))238standardMBeanNames.add(prefix);239}240}241return standardMBeanNames.toArray(new String[0]);242}243244private static Set<String> findStandardMBeansFromRuntime() throws Exception {245FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));246Path modules = fs.getPath("/modules");247return Files.walk(modules)248.filter(path -> path.toString().endsWith(".class"))249.map(path -> path.subpath(2, path.getNameCount()))250.map(Path::toString)251.map(s -> s.substring(0, s.length() - 6)) // drop .class252.filter(s -> !s.equals("module-info"))253.map(s -> s.replace('/', '.'))254.collect(Collectors.toSet());255}256257private static Set<String> findStandardMBeansFromDir(URL codeBase)258throws Exception {259File dir = new File(new URI(codeBase.toString()));260Set<String> names = new TreeSet<String>();261scanDir(dir, "", names);262return names;263}264265private static void scanDir(File dir, String prefix, Set<String> names)266throws Exception {267File[] files = dir.listFiles();268if (files == null)269return;270for (int i = 0; i < files.length; i++) {271File f = files[i];272String name = f.getName();273String p = (prefix.equals("")) ? name : prefix + "." + name;274if (f.isDirectory())275scanDir(f, p, names);276else if (name.endsWith(".class")) {277p = p.substring(0, p.length() - 6);278names.add(p);279}280}281}282}283284285