2
|
1 |
/*
|
5506
|
2 |
* Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
|
2
|
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 |
*
|
5506
|
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.
|
2
|
22 |
*/
|
|
23 |
|
|
24 |
/*
|
|
25 |
* @test
|
|
26 |
* @bug 4898478
|
|
27 |
* @summary Tests client default class loader used before JSR 160 loader
|
|
28 |
* @author Eamonn McManus
|
|
29 |
* @run clean MethodResultTest
|
|
30 |
* @run build MethodResultTest
|
|
31 |
* @run main MethodResultTest
|
|
32 |
*/
|
|
33 |
|
|
34 |
import java.io.*;
|
|
35 |
import java.net.*;
|
|
36 |
import java.util.*;
|
|
37 |
import javax.management.*;
|
|
38 |
import javax.management.remote.*;
|
|
39 |
|
|
40 |
/*
|
|
41 |
This test checks that the class loader that is used to deserialize
|
|
42 |
the return values from remote MBean server operations is indeed the
|
|
43 |
one specified by the user. The only MBean server operations that
|
|
44 |
matter are those than can return an arbitrary Object. We don't
|
|
45 |
care about getMBeanCount or queryNames or whatever because their
|
|
46 |
return values are always of classes loaded by the bootstrap loader.
|
|
47 |
But for the operations getAttribute, getAttributes, setAttributes,
|
|
48 |
and invoke, the return value can include any Java class. This is
|
|
49 |
also true of getMBeanInfo, since the return value can be an exotic
|
|
50 |
subclass of MBeanInfo, or a ModelMBeanInfo that refers to an
|
|
51 |
arbitrary Object. The JMX Remote API spec requires that these
|
|
52 |
return values be deserialized using the class loader supplied by
|
|
53 |
the user (default is context class loader). In particular it must
|
|
54 |
not be deserialized using the system class loader, which it will be
|
|
55 |
with RMI unless special precautions are taken.
|
|
56 |
*/
|
|
57 |
public class MethodResultTest {
|
|
58 |
public static void main(String[] args) throws Exception {
|
|
59 |
Class thisClass = MethodResultTest.class;
|
|
60 |
Class exoticClass = Exotic.class;
|
|
61 |
String exoticClassName = Exotic.class.getName();
|
|
62 |
ClassLoader testClassLoader = thisClass.getClassLoader();
|
|
63 |
if (!(testClassLoader instanceof URLClassLoader)) {
|
|
64 |
System.out.println("TEST INVALID: Not loaded by a " +
|
|
65 |
"URLClassLoader: " + testClassLoader);
|
|
66 |
System.exit(1);
|
|
67 |
}
|
|
68 |
|
|
69 |
URLClassLoader tcl = (URLClassLoader) testClassLoader;
|
|
70 |
URL[] urls = tcl.getURLs();
|
|
71 |
ClassLoader shadowLoader =
|
|
72 |
new ShadowLoader(urls, testClassLoader,
|
|
73 |
new String[] {exoticClassName,
|
|
74 |
ExoticMBeanInfo.class.getName(),
|
|
75 |
ExoticException.class.getName()});
|
|
76 |
Class cl = shadowLoader.loadClass(exoticClassName);
|
|
77 |
if (cl == exoticClass) {
|
|
78 |
System.out.println("TEST INVALID: Shadow class loader loaded " +
|
|
79 |
"same class as test class loader");
|
|
80 |
System.exit(1);
|
|
81 |
}
|
|
82 |
Thread.currentThread().setContextClassLoader(shadowLoader);
|
|
83 |
|
|
84 |
ObjectName on = new ObjectName("a:b=c");
|
|
85 |
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
|
|
86 |
mbs.createMBean(Thing.class.getName(), on);
|
|
87 |
|
|
88 |
final String[] protos = {"rmi", "iiop", "jmxmp"};
|
|
89 |
|
|
90 |
boolean ok = true;
|
|
91 |
for (int i = 0; i < protos.length; i++) {
|
|
92 |
try {
|
|
93 |
ok &= test(protos[i], mbs, on);
|
|
94 |
System.out.println();
|
|
95 |
} catch (Exception e) {
|
|
96 |
System.out.println("TEST FAILED WITH EXCEPTION:");
|
|
97 |
e.printStackTrace(System.out);
|
|
98 |
ok = false;
|
|
99 |
}
|
|
100 |
}
|
|
101 |
|
|
102 |
if (ok)
|
|
103 |
System.out.println("Test passed");
|
|
104 |
else {
|
|
105 |
System.out.println("TEST FAILED");
|
|
106 |
System.exit(1);
|
|
107 |
}
|
|
108 |
}
|
|
109 |
|
|
110 |
private static boolean test(String proto, MBeanServer mbs, ObjectName on)
|
|
111 |
throws Exception {
|
|
112 |
System.out.println("Testing for protocol " + proto);
|
|
113 |
|
|
114 |
JMXConnectorServer cs;
|
|
115 |
JMXServiceURL url = new JMXServiceURL(proto, null, 0);
|
|
116 |
try {
|
|
117 |
cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null,
|
|
118 |
mbs);
|
|
119 |
} catch (MalformedURLException e) {
|
|
120 |
System.out.println("System does not recognize URL: " + url +
|
|
121 |
"; ignoring");
|
|
122 |
return true;
|
|
123 |
}
|
|
124 |
cs.start();
|
|
125 |
JMXServiceURL addr = cs.getAddress();
|
|
126 |
JMXConnector client = JMXConnectorFactory.connect(addr);
|
|
127 |
MBeanServerConnection mbsc = client.getMBeanServerConnection();
|
|
128 |
Object getAttributeExotic = mbsc.getAttribute(on, "Exotic");
|
|
129 |
AttributeList getAttrs =
|
|
130 |
mbsc.getAttributes(on, new String[] {"Exotic"});
|
|
131 |
AttributeList setAttrs = new AttributeList();
|
|
132 |
setAttrs.add(new Attribute("Exotic", new Exotic()));
|
|
133 |
setAttrs = mbsc.setAttributes(on, setAttrs);
|
|
134 |
Object invokeExotic =
|
|
135 |
mbsc.invoke(on, "anExotic", new Object[] {}, new String[] {});
|
|
136 |
MBeanInfo exoticMBI = mbsc.getMBeanInfo(on);
|
|
137 |
|
|
138 |
mbsc.setAttribute(on, new Attribute("Exception", Boolean.TRUE));
|
|
139 |
Exception
|
|
140 |
getAttributeException, setAttributeException, invokeException;
|
|
141 |
try {
|
|
142 |
try {
|
|
143 |
mbsc.getAttribute(on, "Exotic");
|
|
144 |
throw noException("getAttribute");
|
|
145 |
} catch (Exception e) {
|
|
146 |
getAttributeException = e;
|
|
147 |
}
|
|
148 |
try {
|
|
149 |
mbsc.setAttribute(on, new Attribute("Exotic", new Exotic()));
|
|
150 |
throw noException("setAttribute");
|
|
151 |
} catch (Exception e) {
|
|
152 |
setAttributeException = e;
|
|
153 |
}
|
|
154 |
try {
|
|
155 |
mbsc.invoke(on, "anExotic", new Object[] {}, new String[] {});
|
|
156 |
throw noException("invoke");
|
|
157 |
} catch (Exception e) {
|
|
158 |
invokeException = e;
|
|
159 |
}
|
|
160 |
} finally {
|
|
161 |
mbsc.setAttribute(on, new Attribute("Exception", Boolean.FALSE));
|
|
162 |
}
|
|
163 |
client.close();
|
|
164 |
cs.stop();
|
|
165 |
|
|
166 |
boolean ok = true;
|
|
167 |
|
|
168 |
ok &= checkAttrs("getAttributes", getAttrs);
|
|
169 |
ok &= checkAttrs("setAttributes", setAttrs);
|
|
170 |
|
|
171 |
ok &= checkType("getAttribute", getAttributeExotic, Exotic.class);
|
|
172 |
ok &= checkType("getAttributes", attrValue(getAttrs), Exotic.class);
|
|
173 |
ok &= checkType("setAttributes", attrValue(setAttrs), Exotic.class);
|
|
174 |
ok &= checkType("invoke", invokeExotic, Exotic.class);
|
|
175 |
ok &= checkType("getMBeanInfo", exoticMBI, ExoticMBeanInfo.class);
|
|
176 |
|
|
177 |
ok &= checkExceptionType("getAttribute", getAttributeException,
|
|
178 |
ExoticException.class);
|
|
179 |
ok &= checkExceptionType("setAttribute", setAttributeException,
|
|
180 |
ExoticException.class);
|
|
181 |
ok &= checkExceptionType("invoke", invokeException,
|
|
182 |
ExoticException.class);
|
|
183 |
|
|
184 |
if (ok)
|
|
185 |
System.out.println("Test passes for protocol " + proto);
|
|
186 |
return ok;
|
|
187 |
}
|
|
188 |
|
|
189 |
private static Exception noException(String what) {
|
|
190 |
final String msg =
|
|
191 |
"Operation " + what + " returned when exception expected";
|
|
192 |
return new IllegalStateException(msg);
|
|
193 |
}
|
|
194 |
|
|
195 |
private static Object attrValue(AttributeList attrs) {
|
|
196 |
return ((Attribute) attrs.get(0)).getValue();
|
|
197 |
}
|
|
198 |
|
|
199 |
private static boolean checkType(String what, Object object,
|
|
200 |
Class wrongClass) {
|
|
201 |
return checkType(what, object, wrongClass, false);
|
|
202 |
}
|
|
203 |
|
|
204 |
private static boolean checkType(String what, Object object,
|
|
205 |
Class wrongClass, boolean isException) {
|
|
206 |
final String type = isException ? "exception" : "object";
|
|
207 |
final String rendered = isException ? "thrown" : "returned";
|
|
208 |
System.out.println("For " + type + " " + rendered + " by " + what +
|
|
209 |
":");
|
|
210 |
if (wrongClass.isInstance(object)) {
|
|
211 |
System.out.println("TEST FAILS: " + type + " loaded by test " +
|
|
212 |
"classloader");
|
|
213 |
return false;
|
|
214 |
}
|
|
215 |
String className = object.getClass().getName();
|
|
216 |
if (!className.equals(wrongClass.getName())) {
|
|
217 |
System.out.println("TEST FAILS: " + rendered + " " + type +
|
|
218 |
" has wrong class name: " + className);
|
|
219 |
return false;
|
|
220 |
}
|
|
221 |
System.out.println("Test passes: " + rendered + " " + type +
|
|
222 |
" has same class name but is not same class");
|
|
223 |
return true;
|
|
224 |
}
|
|
225 |
|
|
226 |
private static boolean checkExceptionType(String what, Exception exception,
|
|
227 |
Class wrongClass) {
|
|
228 |
if (!(exception instanceof MBeanException)) {
|
|
229 |
System.out.println("Exception thrown by " + what + " is not an " +
|
|
230 |
MBeanException.class.getName() +
|
|
231 |
":");
|
|
232 |
exception.printStackTrace(System.out);
|
|
233 |
return false;
|
|
234 |
}
|
|
235 |
|
|
236 |
exception = ((MBeanException) exception).getTargetException();
|
|
237 |
|
|
238 |
return checkType(what, exception, wrongClass, true);
|
|
239 |
}
|
|
240 |
|
|
241 |
private static boolean checkAttrs(String what, AttributeList attrs) {
|
|
242 |
if (attrs.size() != 1) {
|
|
243 |
System.out.println("TEST FAILS: list returned by " + what +
|
|
244 |
" does not have size 1: " + attrs);
|
|
245 |
return false;
|
|
246 |
}
|
|
247 |
Attribute attr = (Attribute) attrs.get(0);
|
|
248 |
if (!"Exotic".equals(attr.getName())) {
|
|
249 |
System.out.println("TEST FAILS: " + what + " returned wrong " +
|
|
250 |
"attribute: " + attr);
|
|
251 |
return false;
|
|
252 |
}
|
|
253 |
|
|
254 |
return true;
|
|
255 |
}
|
|
256 |
|
|
257 |
public static class Thing
|
|
258 |
extends StandardMBean implements ThingMBean {
|
|
259 |
public Thing() throws NotCompliantMBeanException {
|
|
260 |
super(ThingMBean.class);
|
|
261 |
}
|
|
262 |
|
|
263 |
public Exotic getExotic() throws ExoticException {
|
|
264 |
if (exception)
|
|
265 |
throw new ExoticException();
|
|
266 |
return new Exotic();
|
|
267 |
}
|
|
268 |
|
|
269 |
public void setExotic(Exotic x) throws ExoticException {
|
|
270 |
if (exception)
|
|
271 |
throw new ExoticException();
|
|
272 |
}
|
|
273 |
|
|
274 |
public Exotic anExotic() throws ExoticException {
|
|
275 |
if (exception)
|
|
276 |
throw new ExoticException();
|
|
277 |
return new Exotic();
|
|
278 |
}
|
|
279 |
|
|
280 |
public void cacheMBeanInfo(MBeanInfo mbi) {
|
|
281 |
if (mbi != null)
|
|
282 |
mbi = new ExoticMBeanInfo(mbi);
|
|
283 |
super.cacheMBeanInfo(mbi);
|
|
284 |
}
|
|
285 |
|
|
286 |
public void setException(boolean x) {
|
|
287 |
this.exception = x;
|
|
288 |
}
|
|
289 |
|
|
290 |
private boolean exception;
|
|
291 |
}
|
|
292 |
|
|
293 |
public static interface ThingMBean {
|
|
294 |
public Exotic getExotic() throws ExoticException;
|
|
295 |
public void setExotic(Exotic x) throws ExoticException;
|
|
296 |
public Exotic anExotic() throws ExoticException;
|
|
297 |
public void setException(boolean x);
|
|
298 |
}
|
|
299 |
|
|
300 |
public static class Exotic implements Serializable {}
|
|
301 |
|
|
302 |
public static class ExoticException extends Exception {}
|
|
303 |
|
|
304 |
public static class ExoticMBeanInfo extends MBeanInfo {
|
|
305 |
public ExoticMBeanInfo(MBeanInfo mbi) {
|
|
306 |
super(mbi.getClassName(),
|
|
307 |
mbi.getDescription(),
|
|
308 |
mbi.getAttributes(),
|
|
309 |
mbi.getConstructors(),
|
|
310 |
mbi.getOperations(),
|
|
311 |
mbi.getNotifications());
|
|
312 |
}
|
|
313 |
}
|
|
314 |
|
|
315 |
private static class ShadowLoader extends URLClassLoader {
|
|
316 |
ShadowLoader(URL[] urls, ClassLoader realLoader,
|
|
317 |
String[] shadowClassNames) {
|
|
318 |
super(urls, null);
|
|
319 |
this.realLoader = realLoader;
|
|
320 |
this.shadowClassNames = Arrays.asList(shadowClassNames);
|
|
321 |
}
|
|
322 |
|
|
323 |
protected Class findClass(String name) throws ClassNotFoundException {
|
|
324 |
if (shadowClassNames.contains(name))
|
|
325 |
return super.findClass(name);
|
|
326 |
else
|
|
327 |
return realLoader.loadClass(name);
|
|
328 |
}
|
|
329 |
|
|
330 |
private final ClassLoader realLoader;
|
|
331 |
private final List shadowClassNames;
|
|
332 |
}
|
|
333 |
}
|