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
35import java.lang.ref.WeakReference;
36import java.net.URL;
37import java.util.Arrays;
38import java.util.Map;
39import javax.management.Attribute;
40import javax.management.JMX;
41import javax.management.MBeanAttributeInfo;
42import javax.management.MBeanInfo;
43import javax.management.MBeanOperationInfo;
44import javax.management.MBeanServer;
45import javax.management.MBeanServerFactory;
46import javax.management.MXBean;
47import javax.management.ObjectName;
48import javax.management.loading.PrivateMLet;
49import javax.management.openmbean.CompositeData;
50import javax.management.openmbean.CompositeDataSupport;
51import javax.management.openmbean.CompositeType;
52import javax.management.openmbean.OpenType;
53import javax.management.openmbean.SimpleType;
54
55public 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}
330