1 /* 2 * Copyright (c) 2003, 2015, 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 4910428 27 * @summary Tests target MBean class loader used before JSR 160 loader 28 * @author Eamonn McManus 29 * 30 * @run clean TargetMBeanTest 31 * @run build TargetMBeanTest 32 * @run main TargetMBeanTest 33 */ 34 35 /* 36 The JSR 160 spec says that, when invoking a method (or setting an 37 attribute or creating) on a target MBean, that MBean's class loader 38 is used to deserialize parameters. The problem is that the RMI 39 connector protocol wraps these parameters as MarshalledObjects. 40 When you call get() on a MarshalledObject, the context class loader 41 is used to deserialize, so if we set this to the target MBean's 42 class loader everything should work. EXCEPT that MarshalledObject 43 first tries to load classes using the first class loader it finds in 44 the caller's stack. If our JSR 160 implementation is part of J2SE, 45 it will not find any such class loader (only the system class 46 loader). But if it's standalone, then it will find the class loader 47 of the JSR 160 implementation. If the class name of a parameter is 48 known to both the 160 loader and the target MBean loader, then we 49 will use the wrong loader for deserialization and the attempt to 50 invoke the target MBean with the deserialized object will fail. 51 52 We test this as follows. We fabricate an MLet that has the same set 53 of URLs as the 160 class loader, which we assume is the system class 54 loader (or at least, it is a URLClassLoader). This MLet is 55 therefore a "shadow class loader" -- for every class name known to 56 the 160 class loader, it can load the same name, but the result is 57 not the same class, since it has not been loaded by the same loader. 58 Then, we use the MLet to create an RMIConnectorServer MBean. This 59 MBean is an instance of "shadow RMIConnectorServer", and its 60 constructor has a parameter of type "shadow JMXServiceURL". If the 61 constructor is invoked with "real JMXServiceURL" it will fail. 62 63 While we are at it, we also test that the behaviour is correct for 64 the JMXMP protocol, if that optional protocol is present. 65 */ 66 import java.lang.reflect.*; 67 import java.net.*; 68 import java.util.*; 69 import javax.management.*; 70 import javax.management.loading.*; 71 import javax.management.remote.*; 72 import javax.management.remote.rmi.RMIConnectorServer; 73 74 public class TargetMBeanTest { 75 private static final ObjectName mletName; 76 static { 77 try { 78 mletName = new ObjectName("x:type=mlet"); 79 } catch (Exception e) { 80 e.printStackTrace(); 81 throw new Error(); 82 } 83 } 84 85 public static void main(String[] args) throws Exception { 86 System.out.println("Test that target MBean class loader is used " + 87 "before JMX Remote API class loader"); 88 89 ClassLoader jmxRemoteClassLoader = 90 JMXServiceURL.class.getClassLoader(); 91 if (jmxRemoteClassLoader == null) { 92 System.out.println("JMX Remote API loaded by bootstrap " + 93 "class loader, this test is irrelevant"); 94 return; 95 } 96 if (!(jmxRemoteClassLoader instanceof URLClassLoader)) { 97 System.out.println("TEST INVALID: JMX Remote API not loaded by " + 98 "URLClassLoader"); 99 System.exit(1); 100 } 101 102 URLClassLoader jrcl = (URLClassLoader) jmxRemoteClassLoader; 103 URL[] urls = jrcl.getURLs(); 104 PrivateMLet mlet = new PrivateMLet(urls, null, false); 105 Class shadowClass = mlet.loadClass(JMXServiceURL.class.getName()); 106 if (shadowClass == JMXServiceURL.class) { 107 System.out.println("TEST INVALID: MLet got original " + 108 "JMXServiceURL not shadow"); 109 System.exit(1); 110 } 111 112 MBeanServer mbs = MBeanServerFactory.newMBeanServer(); 113 mbs.registerMBean(mlet, mletName); 114 115 final String[] protos = {"rmi", "iiop", "jmxmp"}; 116 boolean ok = true; 117 for (int i = 0; i < protos.length; i++) { 118 try { 119 ok &= test(protos[i], mbs); 120 } catch (Exception e) { 121 System.out.println("TEST FAILED WITH EXCEPTION:"); 122 e.printStackTrace(System.out); 123 ok = false; 124 } 125 } 126 127 if (ok) 128 System.out.println("Test passed"); 129 else { 130 System.out.println("TEST FAILED"); 131 System.exit(1); 132 } 133 } 134 135 private static boolean test(String proto, MBeanServer mbs) 136 throws Exception { 137 System.out.println("Testing for proto " + proto); 138 139 JMXConnectorServer cs; 140 JMXServiceURL url = new JMXServiceURL(proto, null, 0); 141 try { 142 cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null, 143 mbs); 144 } catch (MalformedURLException e) { 145 System.out.println("System does not recognize URL: " + url + 146 "; ignoring"); 147 return true; 148 } 149 cs.start(); 150 JMXServiceURL addr = cs.getAddress(); 151 JMXServiceURL rmiurl = new JMXServiceURL("rmi", null, 0); 152 JMXConnector client = JMXConnectorFactory.connect(addr); 153 MBeanServerConnection mbsc = client.getMBeanServerConnection(); 154 ObjectName on = new ObjectName("x:proto=" + proto + ",ok=yes"); 155 mbsc.createMBean(RMIConnectorServer.class.getName(), 156 on, 157 mletName, 158 new Object[] {rmiurl, null}, 159 new String[] {JMXServiceURL.class.getName(), 160 Map.class.getName()}); 161 System.out.println("Successfully deserialized with " + proto); 162 mbsc.unregisterMBean(on); 163 164 client.close(); 165 cs.stop(); 166 return true; 167 } 168 }