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