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