/* * Copyright (c) 2005, 2016, 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 correct collection of MXBean's class after unregistration * @author Olivier Lagneau * * @library /lib/testlibrary * * @run main/othervm/timeout=300 MXBeanLoadingTest1 */ import java.lang.ref.WeakReference; import java.net.URL; import java.util.Arrays; import java.util.Map; import javax.management.Attribute; import javax.management.JMX; import javax.management.MBeanAttributeInfo; import javax.management.MBeanInfo; import javax.management.MBeanOperationInfo; import javax.management.MBeanServer; import javax.management.MBeanServerFactory; import javax.management.MXBean; import javax.management.ObjectName; import javax.management.loading.PrivateMLet; import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeDataSupport; import javax.management.openmbean.CompositeType; import javax.management.openmbean.OpenType; import javax.management.openmbean.SimpleType; public class MXBeanLoadingTest1 { public static void main(String[] args) throws Exception { MXBeanLoadingTest1 test = new MXBeanLoadingTest1(); test.run((Map)null); } public void run(Map args) { System.out.println("MXBeanLoadingTest1::run: Start") ; try { System.out.println("We ensure no reference is retained on MXBean class" + " after it is unregistered. We take time to perform" + " some little extra check of Descriptors, MBean*Info."); ClassLoader myClassLoader = MXBeanLoadingTest1.class.getClassLoader(); if(myClassLoader == null) throw new RuntimeException("Test Failed : Null Classloader for test"); URL url = myClassLoader.getResource( MXBeanLoadingTest1.class.getCanonicalName() .replace(".", "/") + ".class"); String clsLoadPath = url.toURI().toString(). replaceAll(MXBeanLoadingTest1.class.getSimpleName() + ".class", ""); URL[] urls = new URL[]{new URL(clsLoadPath)}; PrivateMLet mlet = new PrivateMLet(urls, null, false); Class shadowClass = mlet.loadClass(TestMXBean.class.getName()); if (shadowClass == TestMXBean.class) { String message = "(ERROR) MLet got original TestMXBean, not shadow"; System.out.println(message); throw new RuntimeException(message); } shadowClass = null; MBeanServer mbs = MBeanServerFactory.createMBeanServer(); ObjectName mletName = new ObjectName("x:type=mlet"); mbs.registerMBean(mlet, mletName); ObjectName testName = new ObjectName("x:type=test"); mbs.createMBean(Test.class.getName(), testName, mletName); // That test fails because the MXBean instance is accessed via // a delegate OpenMBean which has ClassLoader testLoader = mbs.getClassLoaderFor(testName); if (testLoader != mlet) { System.out.println("MLet " + mlet); String message = "(ERROR) MXBean's class loader is not MLet: " + testLoader; System.out.println(message); throw new RuntimeException(message); } testLoader = null; // Cycle get/set/get of the attribute of type Luis. // We check the set is effective. CompositeData cd_B = (CompositeData)mbs.getAttribute(testName, "B"); CompositeType compType_B = cd_B.getCompositeType(); CompositeDataSupport cds_B = new CompositeDataSupport(compType_B, new String[]{"something"}, new Object[]{Integer.valueOf(13)}); Attribute myAtt = new Attribute("B", cds_B); mbs.setAttribute(testName, myAtt); CompositeData cd_B2 = (CompositeData)mbs.getAttribute(testName, "B"); if ( ((Integer)cd_B2.get("something")).intValue() != 13 ) { String message = "(ERROR) The setAttribute of att B did not work;" + " expect Luis.something = 13 but got " + cd_B2.get("something"); System.out.println(message); throw new RuntimeException(message); } MBeanInfo info = mbs.getMBeanInfo(testName); String mxbeanField = (String)info.getDescriptor().getFieldValue(JMX.MXBEAN_FIELD); if ( mxbeanField == null || ! mxbeanField.equals("true")) { String message = "(ERROR) Improper mxbean field value " + mxbeanField; System.out.println(message); throw new RuntimeException(message); } // Check the 2 attributes. MBeanAttributeInfo[] attrs = info.getAttributes(); if ( attrs.length == 2 ) { for (MBeanAttributeInfo mbai : attrs) { String originalTypeFieldValue = (String)mbai.getDescriptor().getFieldValue(JMX.ORIGINAL_TYPE_FIELD); OpenType openTypeFieldValue = (OpenType)mbai.getDescriptor().getFieldValue(JMX.OPEN_TYPE_FIELD); if ( mbai.getName().equals("A") ) { if ( !mbai.isReadable() || !mbai.isWritable() || mbai.isIs() || !mbai.getType().equals("int") ) { String message = "(ERROR) Unexpected MBeanAttributeInfo for A " + mbai; System.out.println(message); throw new RuntimeException(message); } if ( ! originalTypeFieldValue.equals("int") ) { String message = "(ERROR) Unexpected originalType in Descriptor for A " + originalTypeFieldValue; System.out.println(message); throw new RuntimeException(message); } if ( ! openTypeFieldValue.equals(SimpleType.INTEGER) ) { String message = "(ERROR) Unexpected openType in Descriptor for A " + originalTypeFieldValue; System.out.println(message); throw new RuntimeException(message); } } else if ( mbai.getName().equals("B") ) { if ( !mbai.isReadable() || !mbai.isWritable() || mbai.isIs() || !mbai.getType().equals("javax.management.openmbean.CompositeData") ) { String message = "(ERROR) Unexpected MBeanAttributeInfo for B " + mbai; System.out.println(message); throw new RuntimeException(message); } if ( ! originalTypeFieldValue.equals(Luis.class.getName()) ) { String message = "(ERROR) Unexpected originalType in Descriptor for B " + originalTypeFieldValue; System.out.println(message); throw new RuntimeException(message); } if ( ! openTypeFieldValue.equals(compType_B) ) { String message = "(ERROR) Unexpected openType in Descriptor for B " + compType_B; System.out.println(message); throw new RuntimeException(message); } } else { String message = "(ERROR) Unknown attribute name"; System.out.println(message); throw new RuntimeException(message); } } } else { String message = "(ERROR) Unexpected MBeanAttributeInfo array" + Arrays.deepToString(attrs); System.out.println(message); throw new RuntimeException(message); } // Check the MXBean operation. MBeanOperationInfo[] ops = info.getOperations(); // The impact is ACTION_INFO as for a standard MBean it is UNKNOWN, // logged 6320104. if (ops.length != 1 || !ops[0].getName().equals("bogus") || ops[0].getSignature().length > 0 || !ops[0].getReturnType().equals("void")) { String message = "(ERROR) Unexpected MBeanOperationInfo array " + Arrays.deepToString(ops); System.out.println(message); throw new RuntimeException(message); } String originalTypeFieldValue = (String)ops[0].getDescriptor().getFieldValue(JMX.ORIGINAL_TYPE_FIELD); OpenType openTypeFieldValue = (OpenType)ops[0].getDescriptor().getFieldValue(JMX.OPEN_TYPE_FIELD); if ( ! originalTypeFieldValue.equals("void") ) { String message = "(ERROR) Unexpected originalType in Descriptor for bogus " + originalTypeFieldValue; System.out.println(message); throw new RuntimeException(message); } if ( ! openTypeFieldValue.equals(SimpleType.VOID) ) { String message = "(ERROR) Unexpected openType in Descriptor for bogus " + originalTypeFieldValue; System.out.println(message); throw new RuntimeException(message); } // Check there is 2 constructors. if (info.getConstructors().length != 2) { String message = "(ERROR) Wrong number of constructors " + "in introspected bean: " + Arrays.asList(info.getConstructors()); System.out.println(message); throw new RuntimeException(message); } // Check MXBean class name. if (!info.getClassName().endsWith("Test")) { String message = "(ERROR) Wrong info class name: " + info.getClassName(); System.out.println(message); throw new RuntimeException(message); } mbs.unregisterMBean(testName); mbs.unregisterMBean(mletName); WeakReference mletRef = new WeakReference(mlet); mlet = null; System.out.println("MXBean registered and unregistered, waiting for " + "garbage collector to collect class loader"); for (int i = 0; i < 10000 && mletRef.get() != null; i++) { System.gc(); Thread.sleep(1); } if (mletRef.get() == null) System.out.println("(OK) class loader was GC'd"); else { String message = "(ERROR) Class loader was not GC'd"; System.out.println(message); throw new RuntimeException(message); } } catch(Exception e) { Utils.printThrowable(e, true) ; throw new RuntimeException(e); } System.out.println("MXBeanLoadingTest1::run: Done without any error") ; } // I agree the use of the MXBean annotation and the MXBean suffix for the // interface name are redundant but however harmless. // @MXBean(true) public static interface TestMXBean { public void bogus(); public int getA(); public void setA(int a); public Luis getB(); public void setB(Luis mi); } public static class Test implements TestMXBean { private Luis luis = new Luis() ; public Test() {} public Test(int x) {} public void bogus() {} public int getA() {return 0;} public void setA(int a) {} public Luis getB() {return this.luis;} public void setB(Luis luis) {this.luis = luis;} } public static class Luis { private int something = 0; public Luis() {} public int getSomething() {return something;} public void setSomething(int v) {something = v;} public void doNothing() {} } }