334 lines
13 KiB
Java
334 lines
13 KiB
Java
/*
|
|
* Copyright 2003 Sun Microsystems, Inc. All Rights Reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
|
* CA 95054 USA or visit www.sun.com if you need additional information or
|
|
* have any questions.
|
|
*/
|
|
|
|
/*
|
|
* @test
|
|
* @bug 4898478
|
|
* @summary Tests client default class loader used before JSR 160 loader
|
|
* @author Eamonn McManus
|
|
* @run clean MethodResultTest
|
|
* @run build MethodResultTest
|
|
* @run main MethodResultTest
|
|
*/
|
|
|
|
import java.io.*;
|
|
import java.net.*;
|
|
import java.util.*;
|
|
import javax.management.*;
|
|
import javax.management.remote.*;
|
|
|
|
/*
|
|
This test checks that the class loader that is used to deserialize
|
|
the return values from remote MBean server operations is indeed the
|
|
one specified by the user. The only MBean server operations that
|
|
matter are those than can return an arbitrary Object. We don't
|
|
care about getMBeanCount or queryNames or whatever because their
|
|
return values are always of classes loaded by the bootstrap loader.
|
|
But for the operations getAttribute, getAttributes, setAttributes,
|
|
and invoke, the return value can include any Java class. This is
|
|
also true of getMBeanInfo, since the return value can be an exotic
|
|
subclass of MBeanInfo, or a ModelMBeanInfo that refers to an
|
|
arbitrary Object. The JMX Remote API spec requires that these
|
|
return values be deserialized using the class loader supplied by
|
|
the user (default is context class loader). In particular it must
|
|
not be deserialized using the system class loader, which it will be
|
|
with RMI unless special precautions are taken.
|
|
*/
|
|
public class MethodResultTest {
|
|
public static void main(String[] args) throws Exception {
|
|
Class thisClass = MethodResultTest.class;
|
|
Class exoticClass = Exotic.class;
|
|
String exoticClassName = Exotic.class.getName();
|
|
ClassLoader testClassLoader = thisClass.getClassLoader();
|
|
if (!(testClassLoader instanceof URLClassLoader)) {
|
|
System.out.println("TEST INVALID: Not loaded by a " +
|
|
"URLClassLoader: " + testClassLoader);
|
|
System.exit(1);
|
|
}
|
|
|
|
URLClassLoader tcl = (URLClassLoader) testClassLoader;
|
|
URL[] urls = tcl.getURLs();
|
|
ClassLoader shadowLoader =
|
|
new ShadowLoader(urls, testClassLoader,
|
|
new String[] {exoticClassName,
|
|
ExoticMBeanInfo.class.getName(),
|
|
ExoticException.class.getName()});
|
|
Class cl = shadowLoader.loadClass(exoticClassName);
|
|
if (cl == exoticClass) {
|
|
System.out.println("TEST INVALID: Shadow class loader loaded " +
|
|
"same class as test class loader");
|
|
System.exit(1);
|
|
}
|
|
Thread.currentThread().setContextClassLoader(shadowLoader);
|
|
|
|
ObjectName on = new ObjectName("a:b=c");
|
|
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
|
|
mbs.createMBean(Thing.class.getName(), on);
|
|
|
|
final String[] protos = {"rmi", "iiop", "jmxmp"};
|
|
|
|
boolean ok = true;
|
|
for (int i = 0; i < protos.length; i++) {
|
|
try {
|
|
ok &= test(protos[i], mbs, on);
|
|
System.out.println();
|
|
} catch (Exception e) {
|
|
System.out.println("TEST FAILED WITH EXCEPTION:");
|
|
e.printStackTrace(System.out);
|
|
ok = false;
|
|
}
|
|
}
|
|
|
|
if (ok)
|
|
System.out.println("Test passed");
|
|
else {
|
|
System.out.println("TEST FAILED");
|
|
System.exit(1);
|
|
}
|
|
}
|
|
|
|
private static boolean test(String proto, MBeanServer mbs, ObjectName on)
|
|
throws Exception {
|
|
System.out.println("Testing for protocol " + proto);
|
|
|
|
JMXConnectorServer cs;
|
|
JMXServiceURL url = new JMXServiceURL(proto, null, 0);
|
|
try {
|
|
cs = JMXConnectorServerFactory.newJMXConnectorServer(url, null,
|
|
mbs);
|
|
} catch (MalformedURLException e) {
|
|
System.out.println("System does not recognize URL: " + url +
|
|
"; ignoring");
|
|
return true;
|
|
}
|
|
cs.start();
|
|
JMXServiceURL addr = cs.getAddress();
|
|
JMXConnector client = JMXConnectorFactory.connect(addr);
|
|
MBeanServerConnection mbsc = client.getMBeanServerConnection();
|
|
Object getAttributeExotic = mbsc.getAttribute(on, "Exotic");
|
|
AttributeList getAttrs =
|
|
mbsc.getAttributes(on, new String[] {"Exotic"});
|
|
AttributeList setAttrs = new AttributeList();
|
|
setAttrs.add(new Attribute("Exotic", new Exotic()));
|
|
setAttrs = mbsc.setAttributes(on, setAttrs);
|
|
Object invokeExotic =
|
|
mbsc.invoke(on, "anExotic", new Object[] {}, new String[] {});
|
|
MBeanInfo exoticMBI = mbsc.getMBeanInfo(on);
|
|
|
|
mbsc.setAttribute(on, new Attribute("Exception", Boolean.TRUE));
|
|
Exception
|
|
getAttributeException, setAttributeException, invokeException;
|
|
try {
|
|
try {
|
|
mbsc.getAttribute(on, "Exotic");
|
|
throw noException("getAttribute");
|
|
} catch (Exception e) {
|
|
getAttributeException = e;
|
|
}
|
|
try {
|
|
mbsc.setAttribute(on, new Attribute("Exotic", new Exotic()));
|
|
throw noException("setAttribute");
|
|
} catch (Exception e) {
|
|
setAttributeException = e;
|
|
}
|
|
try {
|
|
mbsc.invoke(on, "anExotic", new Object[] {}, new String[] {});
|
|
throw noException("invoke");
|
|
} catch (Exception e) {
|
|
invokeException = e;
|
|
}
|
|
} finally {
|
|
mbsc.setAttribute(on, new Attribute("Exception", Boolean.FALSE));
|
|
}
|
|
client.close();
|
|
cs.stop();
|
|
|
|
boolean ok = true;
|
|
|
|
ok &= checkAttrs("getAttributes", getAttrs);
|
|
ok &= checkAttrs("setAttributes", setAttrs);
|
|
|
|
ok &= checkType("getAttribute", getAttributeExotic, Exotic.class);
|
|
ok &= checkType("getAttributes", attrValue(getAttrs), Exotic.class);
|
|
ok &= checkType("setAttributes", attrValue(setAttrs), Exotic.class);
|
|
ok &= checkType("invoke", invokeExotic, Exotic.class);
|
|
ok &= checkType("getMBeanInfo", exoticMBI, ExoticMBeanInfo.class);
|
|
|
|
ok &= checkExceptionType("getAttribute", getAttributeException,
|
|
ExoticException.class);
|
|
ok &= checkExceptionType("setAttribute", setAttributeException,
|
|
ExoticException.class);
|
|
ok &= checkExceptionType("invoke", invokeException,
|
|
ExoticException.class);
|
|
|
|
if (ok)
|
|
System.out.println("Test passes for protocol " + proto);
|
|
return ok;
|
|
}
|
|
|
|
private static Exception noException(String what) {
|
|
final String msg =
|
|
"Operation " + what + " returned when exception expected";
|
|
return new IllegalStateException(msg);
|
|
}
|
|
|
|
private static Object attrValue(AttributeList attrs) {
|
|
return ((Attribute) attrs.get(0)).getValue();
|
|
}
|
|
|
|
private static boolean checkType(String what, Object object,
|
|
Class wrongClass) {
|
|
return checkType(what, object, wrongClass, false);
|
|
}
|
|
|
|
private static boolean checkType(String what, Object object,
|
|
Class wrongClass, boolean isException) {
|
|
final String type = isException ? "exception" : "object";
|
|
final String rendered = isException ? "thrown" : "returned";
|
|
System.out.println("For " + type + " " + rendered + " by " + what +
|
|
":");
|
|
if (wrongClass.isInstance(object)) {
|
|
System.out.println("TEST FAILS: " + type + " loaded by test " +
|
|
"classloader");
|
|
return false;
|
|
}
|
|
String className = object.getClass().getName();
|
|
if (!className.equals(wrongClass.getName())) {
|
|
System.out.println("TEST FAILS: " + rendered + " " + type +
|
|
" has wrong class name: " + className);
|
|
return false;
|
|
}
|
|
System.out.println("Test passes: " + rendered + " " + type +
|
|
" has same class name but is not same class");
|
|
return true;
|
|
}
|
|
|
|
private static boolean checkExceptionType(String what, Exception exception,
|
|
Class wrongClass) {
|
|
if (!(exception instanceof MBeanException)) {
|
|
System.out.println("Exception thrown by " + what + " is not an " +
|
|
MBeanException.class.getName() +
|
|
":");
|
|
exception.printStackTrace(System.out);
|
|
return false;
|
|
}
|
|
|
|
exception = ((MBeanException) exception).getTargetException();
|
|
|
|
return checkType(what, exception, wrongClass, true);
|
|
}
|
|
|
|
private static boolean checkAttrs(String what, AttributeList attrs) {
|
|
if (attrs.size() != 1) {
|
|
System.out.println("TEST FAILS: list returned by " + what +
|
|
" does not have size 1: " + attrs);
|
|
return false;
|
|
}
|
|
Attribute attr = (Attribute) attrs.get(0);
|
|
if (!"Exotic".equals(attr.getName())) {
|
|
System.out.println("TEST FAILS: " + what + " returned wrong " +
|
|
"attribute: " + attr);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static class Thing
|
|
extends StandardMBean implements ThingMBean {
|
|
public Thing() throws NotCompliantMBeanException {
|
|
super(ThingMBean.class);
|
|
}
|
|
|
|
public Exotic getExotic() throws ExoticException {
|
|
if (exception)
|
|
throw new ExoticException();
|
|
return new Exotic();
|
|
}
|
|
|
|
public void setExotic(Exotic x) throws ExoticException {
|
|
if (exception)
|
|
throw new ExoticException();
|
|
}
|
|
|
|
public Exotic anExotic() throws ExoticException {
|
|
if (exception)
|
|
throw new ExoticException();
|
|
return new Exotic();
|
|
}
|
|
|
|
public void cacheMBeanInfo(MBeanInfo mbi) {
|
|
if (mbi != null)
|
|
mbi = new ExoticMBeanInfo(mbi);
|
|
super.cacheMBeanInfo(mbi);
|
|
}
|
|
|
|
public void setException(boolean x) {
|
|
this.exception = x;
|
|
}
|
|
|
|
private boolean exception;
|
|
}
|
|
|
|
public static interface ThingMBean {
|
|
public Exotic getExotic() throws ExoticException;
|
|
public void setExotic(Exotic x) throws ExoticException;
|
|
public Exotic anExotic() throws ExoticException;
|
|
public void setException(boolean x);
|
|
}
|
|
|
|
public static class Exotic implements Serializable {}
|
|
|
|
public static class ExoticException extends Exception {}
|
|
|
|
public static class ExoticMBeanInfo extends MBeanInfo {
|
|
public ExoticMBeanInfo(MBeanInfo mbi) {
|
|
super(mbi.getClassName(),
|
|
mbi.getDescription(),
|
|
mbi.getAttributes(),
|
|
mbi.getConstructors(),
|
|
mbi.getOperations(),
|
|
mbi.getNotifications());
|
|
}
|
|
}
|
|
|
|
private static class ShadowLoader extends URLClassLoader {
|
|
ShadowLoader(URL[] urls, ClassLoader realLoader,
|
|
String[] shadowClassNames) {
|
|
super(urls, null);
|
|
this.realLoader = realLoader;
|
|
this.shadowClassNames = Arrays.asList(shadowClassNames);
|
|
}
|
|
|
|
protected Class findClass(String name) throws ClassNotFoundException {
|
|
if (shadowClassNames.contains(name))
|
|
return super.findClass(name);
|
|
else
|
|
return realLoader.loadClass(name);
|
|
}
|
|
|
|
private final ClassLoader realLoader;
|
|
private final List shadowClassNames;
|
|
}
|
|
}
|