jdk/test/javax/management/mxbean/MXBeanLoadingTest1.java
author hb
Tue, 01 Mar 2016 09:48:49 +0100
changeset 36214 080d86e2ea6d
parent 34827 de746e7eb1e3
child 44423 306c020eb154
permissions -rw-r--r--
8147610: javax/management/mxbean/MXBeanLoadingTest1.java assumes URLClassLoader Reviewed-by: jbachorik

/*
 * 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
 * @modules java.management
 * @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<String, Object>)null);
    }


    public void run(Map<String, Object> 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<PrivateMLet> mletRef =
                    new WeakReference<PrivateMLet>(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() {}
    }
}