1 /*
   2  * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /*
  25  * @test
  26  * @bug 8058865
  27  * @summary Checks correct collection of MXBean's class after unregistration
  28  * @author Olivier Lagneau
  29  * @modules java.management
  30  * @library /lib/testlibrary
  31  * @run main/othervm/timeout=300 MXBeanLoadingTest1
  32  */
  33 
  34 import java.lang.ref.WeakReference;
  35 import java.net.URL;
  36 import java.util.Arrays;
  37 import java.util.Map;
  38 import javax.management.Attribute;
  39 import javax.management.JMX;
  40 import javax.management.MBeanAttributeInfo;
  41 import javax.management.MBeanInfo;
  42 import javax.management.MBeanOperationInfo;
  43 import javax.management.MBeanServer;
  44 import javax.management.MBeanServerFactory;
  45 import javax.management.MXBean;
  46 import javax.management.ObjectName;
  47 import javax.management.loading.PrivateMLet;
  48 import javax.management.openmbean.CompositeData;
  49 import javax.management.openmbean.CompositeDataSupport;
  50 import javax.management.openmbean.CompositeType;
  51 import javax.management.openmbean.OpenType;
  52 import javax.management.openmbean.SimpleType;
  53 
  54 public class MXBeanLoadingTest1 {
  55 
  56     public static void main(String[] args) throws Exception {
  57         MXBeanLoadingTest1 test = new MXBeanLoadingTest1();
  58         test.run((Map<String, Object>)null);
  59     }
  60 
  61 
  62     public void run(Map<String, Object> args) {
  63 
  64         System.out.println("MXBeanLoadingTest1::run: Start") ;
  65 
  66         try {
  67             System.out.println("We ensure no reference is retained on MXBean class"
  68                     + " after it is unregistered. We take time to perform"
  69                     + " some little extra check of Descriptors, MBean*Info.");
  70 
  71             ClassLoader myClassLoader = MXBeanLoadingTest1.class.getClassLoader();
  72             if(myClassLoader == null)
  73                 throw new RuntimeException("Test Failed : Null Classloader for test");
  74             URL url = myClassLoader.getResource(
  75                     MXBeanLoadingTest1.class.getCanonicalName()
  76                             .replace(".", "/") + ".class");
  77             String clsLoadPath = url.toURI().toString().
  78                     replaceAll(MXBeanLoadingTest1.class.getSimpleName()
  79                             + ".class", "");
  80 
  81             URL[] urls = new URL[]{new URL(clsLoadPath)};
  82             PrivateMLet mlet = new PrivateMLet(urls, null, false);
  83             Class<?> shadowClass = mlet.loadClass(TestMXBean.class.getName());
  84 
  85             if (shadowClass == TestMXBean.class) {
  86                 String message = "(ERROR) MLet got original TestMXBean, not shadow";
  87                 System.out.println(message);
  88                 throw new RuntimeException(message);
  89             }
  90             shadowClass = null;
  91 
  92             MBeanServer mbs = MBeanServerFactory.createMBeanServer();
  93             ObjectName mletName = new ObjectName("x:type=mlet");
  94             mbs.registerMBean(mlet, mletName);
  95 
  96             ObjectName testName = new ObjectName("x:type=test");
  97             mbs.createMBean(Test.class.getName(), testName, mletName);
  98 
  99             // That test fails because the MXBean instance is accessed via
 100             // a delegate OpenMBean which has
 101             ClassLoader testLoader = mbs.getClassLoaderFor(testName);
 102 
 103             if (testLoader != mlet) {
 104                 System.out.println("MLet " + mlet);
 105                 String message = "(ERROR) MXBean's class loader is not MLet: "
 106                         + testLoader;
 107                 System.out.println(message);
 108                 throw new RuntimeException(message);
 109             }
 110             testLoader = null;
 111 
 112 
 113             // Cycle get/set/get of the attribute of type Luis.
 114             // We check the set is effective.
 115             CompositeData cd_B = (CompositeData)mbs.getAttribute(testName, "B");
 116             CompositeType compType_B = cd_B.getCompositeType();
 117 
 118             CompositeDataSupport cds_B =
 119                     new CompositeDataSupport(compType_B,
 120                     new String[]{"something"},
 121                     new Object[]{Integer.valueOf(13)});
 122             Attribute myAtt = new Attribute("B",  cds_B);
 123             mbs.setAttribute(testName, myAtt);
 124 
 125             CompositeData cd_B2 = (CompositeData)mbs.getAttribute(testName, "B");
 126 
 127             if ( ((Integer)cd_B2.get("something")).intValue() != 13 ) {
 128                 String message = "(ERROR) The setAttribute of att B did not work;"
 129                         + " expect Luis.something = 13 but got "
 130                         + cd_B2.get("something");
 131                 System.out.println(message);
 132                 throw new RuntimeException(message);
 133             }
 134 
 135             MBeanInfo info = mbs.getMBeanInfo(testName);
 136             String mxbeanField =
 137                     (String)info.getDescriptor().getFieldValue(JMX.MXBEAN_FIELD);
 138 
 139             if ( mxbeanField == null || ! mxbeanField.equals("true")) {
 140                 String message = "(ERROR) Improper mxbean field value "
 141                         + mxbeanField;
 142                 System.out.println(message);
 143                 throw new RuntimeException(message);
 144             }
 145 
 146             // Check the 2 attributes.
 147             MBeanAttributeInfo[] attrs = info.getAttributes();
 148 
 149             if ( attrs.length == 2 ) {
 150                 for (MBeanAttributeInfo mbai : attrs) {
 151                     String originalTypeFieldValue =
 152                             (String)mbai.getDescriptor().getFieldValue(JMX.ORIGINAL_TYPE_FIELD);
 153                     OpenType<?> openTypeFieldValue =
 154                             (OpenType<?>)mbai.getDescriptor().getFieldValue(JMX.OPEN_TYPE_FIELD);
 155 
 156                     if ( mbai.getName().equals("A") ) {
 157                         if ( !mbai.isReadable() || !mbai.isWritable()
 158                         || mbai.isIs()
 159                         || !mbai.getType().equals("int") ) {
 160                             String message = "(ERROR) Unexpected MBeanAttributeInfo for A "
 161                                     + mbai;
 162                             System.out.println(message);
 163                             throw new RuntimeException(message);
 164                         }
 165 
 166                         if ( ! originalTypeFieldValue.equals("int") ) {
 167                             String message = "(ERROR) Unexpected originalType in Descriptor for A "
 168                                     + originalTypeFieldValue;
 169                             System.out.println(message);
 170                             throw new RuntimeException(message);
 171                         }
 172 
 173                         if ( ! openTypeFieldValue.equals(SimpleType.INTEGER) ) {
 174                             String message = "(ERROR) Unexpected openType in Descriptor for A "
 175                                     + originalTypeFieldValue;
 176                             System.out.println(message);
 177                             throw new RuntimeException(message);
 178                         }
 179                     } else if ( mbai.getName().equals("B") ) {
 180                         if ( !mbai.isReadable() || !mbai.isWritable()
 181                         || mbai.isIs()
 182                         || !mbai.getType().equals("javax.management.openmbean.CompositeData") ) {
 183                             String message = "(ERROR) Unexpected MBeanAttributeInfo for B "
 184                                     + mbai;
 185                             System.out.println(message);
 186                             throw new RuntimeException(message);
 187                         }
 188 
 189                         if ( ! originalTypeFieldValue.equals(Luis.class.getName()) ) {
 190                             String message = "(ERROR) Unexpected originalType in Descriptor for B "
 191                                     + originalTypeFieldValue;
 192                             System.out.println(message);
 193                             throw new RuntimeException(message);
 194                         }
 195 
 196                         if ( ! openTypeFieldValue.equals(compType_B) ) {
 197                             String message = "(ERROR) Unexpected openType in Descriptor for B "
 198                                     + compType_B;
 199                             System.out.println(message);
 200                             throw new RuntimeException(message);
 201                         }
 202                     } else {
 203                         String message = "(ERROR) Unknown attribute name";
 204                         System.out.println(message);
 205                         throw new RuntimeException(message);
 206                     }
 207                 }
 208             } else {
 209                 String message = "(ERROR) Unexpected MBeanAttributeInfo array"
 210                         + Arrays.deepToString(attrs);
 211                 System.out.println(message);
 212                 throw new RuntimeException(message);
 213             }
 214 
 215             // Check the MXBean operation.
 216             MBeanOperationInfo[] ops = info.getOperations();
 217             // The impact is ACTION_INFO as for a standard MBean it is UNKNOWN,
 218             // logged 6320104.
 219             if (ops.length != 1 || !ops[0].getName().equals("bogus")
 220             || ops[0].getSignature().length > 0
 221                     || !ops[0].getReturnType().equals("void")) {
 222                 String message = "(ERROR) Unexpected MBeanOperationInfo array "
 223                         + Arrays.deepToString(ops);
 224                 System.out.println(message);
 225                 throw new RuntimeException(message);
 226             }
 227 
 228             String originalTypeFieldValue =
 229                     (String)ops[0].getDescriptor().getFieldValue(JMX.ORIGINAL_TYPE_FIELD);
 230             OpenType<?> openTypeFieldValue =
 231                     (OpenType<?>)ops[0].getDescriptor().getFieldValue(JMX.OPEN_TYPE_FIELD);
 232 
 233             if ( ! originalTypeFieldValue.equals("void") ) {
 234                 String message = "(ERROR) Unexpected originalType in Descriptor for bogus "
 235                         + originalTypeFieldValue;
 236                 System.out.println(message);
 237                 throw new RuntimeException(message);
 238             }
 239 
 240             if ( ! openTypeFieldValue.equals(SimpleType.VOID) ) {
 241                 String message = "(ERROR) Unexpected openType in Descriptor for bogus "
 242                         + originalTypeFieldValue;
 243                 System.out.println(message);
 244                 throw new RuntimeException(message);
 245             }
 246 
 247             // Check there is 2 constructors.
 248             if (info.getConstructors().length != 2) {
 249                 String message = "(ERROR) Wrong number of constructors " +
 250                         "in introspected bean: " +
 251                         Arrays.asList(info.getConstructors());
 252                 System.out.println(message);
 253                 throw new RuntimeException(message);
 254             }
 255 
 256             // Check MXBean class name.
 257             if (!info.getClassName().endsWith("Test")) {
 258                 String message = "(ERROR) Wrong info class name: " +
 259                         info.getClassName();
 260                 System.out.println(message);
 261                 throw new RuntimeException(message);
 262             }
 263 
 264             mbs.unregisterMBean(testName);
 265             mbs.unregisterMBean(mletName);
 266 
 267             WeakReference<PrivateMLet> mletRef =
 268                     new WeakReference<PrivateMLet>(mlet);
 269             mlet = null;
 270 
 271             System.out.println("MXBean registered and unregistered, waiting for " +
 272                     "garbage collector to collect class loader");
 273 
 274             for (int i = 0; i < 10000 && mletRef.get() != null; i++) {
 275                 System.gc();
 276                 Thread.sleep(1);
 277             }
 278 
 279             if (mletRef.get() == null)
 280                 System.out.println("(OK) class loader was GC'd");
 281             else {
 282                 String message = "(ERROR) Class loader was not GC'd";
 283                 System.out.println(message);
 284                 throw new RuntimeException(message);
 285             }
 286         } catch(Exception e) {
 287             Utils.printThrowable(e, true) ;
 288             throw new RuntimeException(e);
 289         }
 290 
 291         System.out.println("MXBeanLoadingTest1::run: Done without any error") ;
 292     }
 293 
 294 
 295     // I agree the use of the MXBean annotation and the MXBean suffix for the
 296     // interface name are redundant but however harmless.
 297     //
 298     @MXBean(true)
 299     public static interface TestMXBean {
 300         public void bogus();
 301         public int getA();
 302         public void setA(int a);
 303         public Luis getB();
 304         public void setB(Luis mi);
 305     }
 306 
 307 
 308     public static class Test implements TestMXBean {
 309         private Luis luis = new Luis() ;
 310         public Test() {}
 311         public Test(int x) {}
 312 
 313         public void bogus() {}
 314         public int getA() {return 0;}
 315         public void setA(int a) {}
 316         public Luis getB() {return this.luis;}
 317         public void setB(Luis luis) {this.luis = luis;}
 318     }
 319 
 320 
 321     public static class Luis {
 322         private int something = 0;
 323         public Luis() {}
 324         public int getSomething() {return something;}
 325         public void setSomething(int v) {something = v;}
 326         public void doNothing() {}
 327     }
 328 }