3789983e89
Reviewed-by: darcy, ihse
774 lines
29 KiB
Java
774 lines
29 KiB
Java
/*
|
|
* Copyright (c) 2005, 2015, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
|
|
/*
|
|
* @test
|
|
* @bug 6175517 6278707 6318827 6305746 6392303 6600709 8010285
|
|
* @summary General MXBean test.
|
|
* @author Eamonn McManus
|
|
* @author Jaroslav Bachorik
|
|
* @modules java.management.rmi
|
|
* @run clean MXBeanTest MerlinMXBean TigerMXBean
|
|
* @run build MXBeanTest MerlinMXBean TigerMXBean
|
|
* @run main MXBeanTest
|
|
*/
|
|
|
|
import java.lang.reflect.Array;
|
|
import java.lang.reflect.Field;
|
|
import java.lang.reflect.InvocationHandler;
|
|
import java.lang.reflect.Method;
|
|
import java.lang.reflect.Proxy;
|
|
import java.util.Arrays;
|
|
import java.util.Collection;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.Map;
|
|
import java.util.SortedMap;
|
|
import javax.management.JMX;
|
|
import javax.management.MBeanAttributeInfo;
|
|
import javax.management.MBeanInfo;
|
|
import javax.management.MBeanOperationInfo;
|
|
import javax.management.MBeanParameterInfo;
|
|
import javax.management.MBeanServer;
|
|
import javax.management.MBeanServerConnection;
|
|
import javax.management.MBeanServerFactory;
|
|
import javax.management.MBeanServerInvocationHandler;
|
|
import javax.management.NotCompliantMBeanException;
|
|
import javax.management.ObjectName;
|
|
import javax.management.StandardMBean;
|
|
import javax.management.openmbean.ArrayType;
|
|
import javax.management.openmbean.CompositeData;
|
|
import javax.management.openmbean.CompositeDataInvocationHandler;
|
|
import javax.management.openmbean.OpenType;
|
|
import javax.management.openmbean.SimpleType;
|
|
import javax.management.openmbean.TabularData;
|
|
import javax.management.openmbean.TabularType;
|
|
import javax.management.remote.JMXConnector;
|
|
import javax.management.remote.JMXConnectorFactory;
|
|
import javax.management.remote.JMXConnectorServer;
|
|
import javax.management.remote.JMXConnectorServerFactory;
|
|
import javax.management.remote.JMXServiceURL;
|
|
|
|
public class MXBeanTest {
|
|
public static void main(String[] args) throws Exception {
|
|
testInterface(MerlinMXBean.class, false);
|
|
testInterface(TigerMXBean.class, false);
|
|
testInterface(MerlinMXBean.class, true);
|
|
testInterface(TigerMXBean.class, true);
|
|
testExplicitMXBean();
|
|
testSubclassMXBean();
|
|
testIndirectMXBean();
|
|
testNonCompliantMXBean("Private", new Private());
|
|
testNonCompliantMXBean("NonCompliant", new NonCompliant());
|
|
|
|
if (failures == 0)
|
|
System.out.println("Test passed");
|
|
else
|
|
throw new Exception("TEST FAILURES: " + failures);
|
|
}
|
|
|
|
private static int failures = 0;
|
|
|
|
private static interface PrivateMXBean {
|
|
public int[] getInts();
|
|
}
|
|
|
|
public static class Private implements PrivateMXBean {
|
|
public int[] getInts() {
|
|
return new int[]{1,2,3};
|
|
}
|
|
}
|
|
|
|
public static interface NonCompliantMXBean {
|
|
public boolean getInt();
|
|
public boolean isInt();
|
|
public void setInt(int a);
|
|
public void setInt(long b);
|
|
}
|
|
|
|
public static class NonCompliant implements NonCompliantMXBean {
|
|
public boolean getInt() {
|
|
return false;
|
|
}
|
|
|
|
public boolean isInt() {
|
|
return true;
|
|
}
|
|
|
|
public void setInt(int a) {
|
|
}
|
|
|
|
public void setInt(long b) {
|
|
}
|
|
}
|
|
|
|
public static interface ExplicitMXBean {
|
|
public int[] getInts();
|
|
}
|
|
public static class Explicit implements ExplicitMXBean {
|
|
public int[] getInts() {
|
|
return new int[] {1, 2, 3};
|
|
}
|
|
}
|
|
public static class Subclass
|
|
extends StandardMBean
|
|
implements ExplicitMXBean {
|
|
public Subclass() {
|
|
super(ExplicitMXBean.class, true);
|
|
}
|
|
|
|
public int[] getInts() {
|
|
return new int[] {1, 2, 3};
|
|
}
|
|
}
|
|
public static interface IndirectInterface extends ExplicitMXBean {}
|
|
public static class Indirect implements IndirectInterface {
|
|
public int[] getInts() {
|
|
return new int[] {1, 2, 3};
|
|
}
|
|
}
|
|
|
|
private static void testNonCompliantMXBean(String type, Object bean) throws Exception {
|
|
System.out.println(type + " MXBean test...");
|
|
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
|
|
ObjectName on = new ObjectName("test:type=" + type);
|
|
try {
|
|
mbs.registerMBean(bean, on);
|
|
failure(bean.getClass().getInterfaces()[0].getName() + " is not a compliant "
|
|
+ "MXBean interface");
|
|
} catch (NotCompliantMBeanException e) {
|
|
success("Non-compliant MXBean not registered");
|
|
}
|
|
}
|
|
|
|
private static void testExplicitMXBean() throws Exception {
|
|
System.out.println("Explicit MXBean test...");
|
|
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
|
|
ObjectName on = new ObjectName("test:type=Explicit");
|
|
Explicit explicit = new Explicit();
|
|
mbs.registerMBean(explicit, on);
|
|
testMXBean(mbs, on);
|
|
}
|
|
|
|
private static void testSubclassMXBean() throws Exception {
|
|
System.out.println("Subclass MXBean test...");
|
|
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
|
|
ObjectName on = new ObjectName("test:type=Subclass");
|
|
Subclass subclass = new Subclass();
|
|
mbs.registerMBean(subclass, on);
|
|
testMXBean(mbs, on);
|
|
}
|
|
|
|
private static void testIndirectMXBean() throws Exception {
|
|
System.out.println("Indirect MXBean test...");
|
|
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
|
|
ObjectName on = new ObjectName("test:type=Indirect");
|
|
Indirect indirect = new Indirect();
|
|
mbs.registerMBean(indirect, on);
|
|
testMXBean(mbs, on);
|
|
}
|
|
|
|
private static void testMXBean(MBeanServer mbs, ObjectName on)
|
|
throws Exception {
|
|
MBeanInfo mbi = mbs.getMBeanInfo(on);
|
|
MBeanAttributeInfo[] attrs = mbi.getAttributes();
|
|
int nattrs = attrs.length;
|
|
if (mbi.getAttributes().length != 1)
|
|
failure("wrong number of attributes: " + attrs);
|
|
else {
|
|
MBeanAttributeInfo mbai = attrs[0];
|
|
if (mbai.getName().equals("Ints")
|
|
&& mbai.isReadable() && !mbai.isWritable()
|
|
&& mbai.getDescriptor().getFieldValue("openType")
|
|
.equals(new ArrayType<int[]>(SimpleType.INTEGER, true))
|
|
&& attrs[0].getType().equals("[I"))
|
|
success("MBeanAttributeInfo");
|
|
else
|
|
failure("MBeanAttributeInfo: " + mbai);
|
|
}
|
|
|
|
int[] ints = (int[]) mbs.getAttribute(on, "Ints");
|
|
if (equal(ints, new int[] {1, 2, 3}, null))
|
|
success("getAttribute");
|
|
else
|
|
failure("getAttribute: " + Arrays.toString(ints));
|
|
|
|
ExplicitMXBean proxy =
|
|
JMX.newMXBeanProxy(mbs, on, ExplicitMXBean.class);
|
|
int[] pints = proxy.getInts();
|
|
if (equal(pints, new int[] {1, 2, 3}, null))
|
|
success("getAttribute through proxy");
|
|
else
|
|
failure("getAttribute through proxy: " + Arrays.toString(pints));
|
|
}
|
|
|
|
private static class NamedMXBeans extends HashMap<ObjectName, Object> {
|
|
private static final long serialVersionUID = 0;
|
|
|
|
NamedMXBeans(MBeanServerConnection mbsc) {
|
|
this.mbsc = mbsc;
|
|
}
|
|
|
|
MBeanServerConnection getMBeanServerConnection() {
|
|
return mbsc;
|
|
}
|
|
|
|
private final MBeanServerConnection mbsc;
|
|
}
|
|
|
|
/* This is the core of the test. Given the MXBean interface c, we
|
|
make an MXBean object that implements that interface by
|
|
constructing a dynamic proxy. If the interface defines an
|
|
attribute Foo (with getFoo and setFoo methods), then it must
|
|
also contain a field (constant) Foo of the same type, and a
|
|
field (constant) FooType that is an OpenType. The field Foo is
|
|
a reference value for this case. We check that the attribute
|
|
does indeed have the given OpenType. The dynamically-created
|
|
MXBean will return the reference value from the getFoo()
|
|
method, and we check that that value survives the mapping to
|
|
open values and back when the attribute is accessed through an
|
|
MXBean proxy. The MXBean will also check in its setFoo method
|
|
that the value being set is equal to the reference value, which
|
|
tests that the mapping and unmapping also works in the other
|
|
direction. The interface should define an operation opFoo with
|
|
two parameters and a return value all of the same type as the
|
|
attribute. The MXBean will check that the two parameters are
|
|
equal to the reference value, and will return that value. The
|
|
test checks that calling the operation through an MXBean proxy
|
|
returns the reference value, again after mapping to and back
|
|
from open values.
|
|
|
|
If any field (constant) in the MXBean interface has a name that
|
|
ends with ObjectName, say FooObjectName, then its value must be
|
|
a String containing an ObjectName value. There must be a field
|
|
(constant) called Foo that is a valid MXBean, and that MXBean
|
|
will be registered in the MBean Server with the given name before
|
|
the test starts. This enables us to test that inter-MXBean
|
|
references are correctly converted to ObjectNames and back.
|
|
*/
|
|
private static <T> void testInterface(Class<T> c, boolean nullTest)
|
|
throws Exception {
|
|
|
|
System.out.println("Testing " + c.getName() +
|
|
(nullTest ? " for null values" : "") + "...");
|
|
|
|
MBeanServer mbs = MBeanServerFactory.newMBeanServer();
|
|
|
|
JMXServiceURL url = new JMXServiceURL("rmi", null, 0);
|
|
JMXConnectorServer cs =
|
|
JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs);
|
|
cs.start();
|
|
JMXServiceURL addr = cs.getAddress();
|
|
JMXConnector cc = JMXConnectorFactory.connect(addr);
|
|
MBeanServerConnection mbsc = cc.getMBeanServerConnection();
|
|
|
|
NamedMXBeans namedMXBeans = new NamedMXBeans(mbsc);
|
|
InvocationHandler ih =
|
|
nullTest ? new MXBeanNullImplInvocationHandler(c, namedMXBeans) :
|
|
new MXBeanImplInvocationHandler(c, namedMXBeans);
|
|
T impl = c.cast(Proxy.newProxyInstance(c.getClassLoader(),
|
|
new Class[] {c},
|
|
ih));
|
|
ObjectName on = new ObjectName("test:type=" + c.getName());
|
|
mbs.registerMBean(impl, on);
|
|
|
|
System.out.println("Register any MXBeans...");
|
|
|
|
Field[] fields = c.getFields();
|
|
for (Field field : fields) {
|
|
String n = field.getName();
|
|
if (n.endsWith("ObjectName")) {
|
|
String objectNameString = (String) field.get(null);
|
|
String base = n.substring(0, n.length() - 10);
|
|
Field f = c.getField(base);
|
|
Object mxbean = f.get(null);
|
|
ObjectName objectName =
|
|
ObjectName.getInstance(objectNameString);
|
|
mbs.registerMBean(mxbean, objectName);
|
|
namedMXBeans.put(objectName, mxbean);
|
|
}
|
|
}
|
|
|
|
try {
|
|
testInterface(c, mbsc, on, namedMXBeans, nullTest);
|
|
} finally {
|
|
try {
|
|
cc.close();
|
|
} finally {
|
|
cs.stop();
|
|
}
|
|
}
|
|
}
|
|
|
|
private static <T> void testInterface(Class<T> c,
|
|
MBeanServerConnection mbsc,
|
|
ObjectName on,
|
|
NamedMXBeans namedMXBeans,
|
|
boolean nullTest)
|
|
throws Exception {
|
|
|
|
System.out.println("Type check...");
|
|
|
|
MBeanInfo mbi = mbsc.getMBeanInfo(on);
|
|
MBeanAttributeInfo[] mbais = mbi.getAttributes();
|
|
for (int i = 0; i < mbais.length; i++) {
|
|
MBeanAttributeInfo mbai = mbais[i];
|
|
String name = mbai.getName();
|
|
Field typeField = c.getField(name + "Type");
|
|
OpenType typeValue = (OpenType) typeField.get(null);
|
|
OpenType openType =
|
|
(OpenType) mbai.getDescriptor().getFieldValue("openType");
|
|
if (typeValue.equals(openType))
|
|
success("attribute " + name);
|
|
else {
|
|
final String msg =
|
|
"Wrong type attribute " + name + ": " +
|
|
openType + " should be " + typeValue;
|
|
failure(msg);
|
|
}
|
|
}
|
|
|
|
MBeanOperationInfo[] mbois = mbi.getOperations();
|
|
for (int i = 0; i < mbois.length; i++) {
|
|
MBeanOperationInfo mboi = mbois[i];
|
|
String oname = mboi.getName();
|
|
if (!oname.startsWith("op"))
|
|
throw new Error();
|
|
OpenType retType =
|
|
(OpenType) mboi.getDescriptor().getFieldValue("openType");
|
|
MBeanParameterInfo[] params = mboi.getSignature();
|
|
MBeanParameterInfo p1i = params[0];
|
|
MBeanParameterInfo p2i = params[1];
|
|
OpenType p1Type =
|
|
(OpenType) p1i.getDescriptor().getFieldValue("openType");
|
|
OpenType p2Type =
|
|
(OpenType) p2i.getDescriptor().getFieldValue("openType");
|
|
if (!retType.equals(p1Type) || !p1Type.equals(p2Type)) {
|
|
final String msg =
|
|
"Parameter and return open types should all be same " +
|
|
"but are not: " + retType + " " + oname + "(" + p1Type +
|
|
", " + p2Type + ")";
|
|
failure(msg);
|
|
continue;
|
|
}
|
|
String name = oname.substring(2);
|
|
Field typeField = c.getField(name + "Type");
|
|
OpenType typeValue = (OpenType) typeField.get(null);
|
|
if (typeValue.equals(retType))
|
|
success("operation " + oname);
|
|
else {
|
|
final String msg =
|
|
"Wrong type operation " + oname + ": " +
|
|
retType + " should be " + typeValue;
|
|
failure(msg);
|
|
}
|
|
}
|
|
|
|
|
|
System.out.println("Mapping check...");
|
|
|
|
Object proxy =
|
|
JMX.newMXBeanProxy(mbsc, on, c);
|
|
|
|
Method[] methods = c.getMethods();
|
|
for (int i = 0; i < methods.length; i++) {
|
|
final Method method = methods[i];
|
|
if (method.getDeclaringClass() != c)
|
|
continue; // skip hashCode() etc inherited from Object
|
|
final String mname = method.getName();
|
|
final int what = getType(method);
|
|
final String name = getName(method);
|
|
final Field refField = c.getField(name);
|
|
if (nullTest && refField.getType().isPrimitive())
|
|
continue;
|
|
final Field openTypeField = c.getField(name + "Type");
|
|
final OpenType openType = (OpenType) openTypeField.get(null);
|
|
final Object refValue = nullTest ? null : refField.get(null);
|
|
Object setValue = refValue;
|
|
try {
|
|
Field onField = c.getField(name + "ObjectName");
|
|
String refName = (String) onField.get(null);
|
|
ObjectName refObjName = ObjectName.getInstance(refName);
|
|
Class<?> mxbeanInterface = refField.getType();
|
|
setValue = nullTest ? null :
|
|
JMX.newMXBeanProxy(mbsc, refObjName, mxbeanInterface);
|
|
} catch (Exception e) {
|
|
// no xObjectName field, setValue == refValue
|
|
}
|
|
boolean ok = true;
|
|
try {
|
|
switch (what) {
|
|
case GET:
|
|
final Object gotOpen = mbsc.getAttribute(on, name);
|
|
if (nullTest) {
|
|
if (gotOpen != null) {
|
|
failure(mname + " got non-null value " +
|
|
gotOpen);
|
|
ok = false;
|
|
}
|
|
} else if (!openType.isValue(gotOpen)) {
|
|
if (gotOpen instanceof TabularData) {
|
|
// detail the mismatch
|
|
TabularData gotTabular = (TabularData) gotOpen;
|
|
compareTabularType((TabularType) openType,
|
|
gotTabular.getTabularType());
|
|
}
|
|
failure(mname + " got open data " + gotOpen +
|
|
" not valid for open type " + openType);
|
|
ok = false;
|
|
}
|
|
final Object got = method.invoke(proxy, (Object[]) null);
|
|
if (!equal(refValue, got, namedMXBeans)) {
|
|
failure(mname + " got " + string(got) +
|
|
", should be " + string(refValue));
|
|
ok = false;
|
|
}
|
|
break;
|
|
|
|
case SET:
|
|
method.invoke(proxy, new Object[] {setValue});
|
|
break;
|
|
|
|
case OP:
|
|
final Object opped =
|
|
method.invoke(proxy, new Object[] {setValue, setValue});
|
|
if (!equal(refValue, opped, namedMXBeans)) {
|
|
failure(
|
|
mname + " got " + string(opped) +
|
|
", should be " + string(refValue)
|
|
);
|
|
ok = false;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
throw new Error();
|
|
}
|
|
|
|
if (ok)
|
|
success(mname);
|
|
|
|
} catch (Exception e) {
|
|
failure(mname, e);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private static void success(String what) {
|
|
System.out.println("OK: " + what);
|
|
}
|
|
|
|
private static void failure(String what) {
|
|
System.out.println("FAILED: " + what);
|
|
failures++;
|
|
}
|
|
|
|
private static void failure(String what, Exception e) {
|
|
System.out.println("FAILED WITH EXCEPTION: " + what);
|
|
e.printStackTrace(System.out);
|
|
failures++;
|
|
}
|
|
|
|
private static class MXBeanImplInvocationHandler
|
|
implements InvocationHandler {
|
|
MXBeanImplInvocationHandler(Class intf, NamedMXBeans namedMXBeans) {
|
|
this.intf = intf;
|
|
this.namedMXBeans = namedMXBeans;
|
|
}
|
|
|
|
public Object invoke(Object proxy, Method method, Object[] args)
|
|
throws Throwable {
|
|
final String mname = method.getName();
|
|
final int what = getType(method);
|
|
final String name = getName(method);
|
|
final Field refField = intf.getField(name);
|
|
final Object refValue = getRefValue(refField);
|
|
|
|
switch (what) {
|
|
case GET:
|
|
assert args == null;
|
|
return refValue;
|
|
|
|
case SET:
|
|
assert args.length == 1;
|
|
Object setValue = args[0];
|
|
if (!equal(refValue, setValue, namedMXBeans)) {
|
|
final String msg =
|
|
mname + "(" + string(setValue) +
|
|
") does not match ref: " + string(refValue);
|
|
throw new IllegalArgumentException(msg);
|
|
}
|
|
return null;
|
|
|
|
case OP:
|
|
assert args.length == 2;
|
|
Object arg1 = args[0];
|
|
Object arg2 = args[1];
|
|
if (!equal(arg1, arg2, namedMXBeans)) {
|
|
final String msg =
|
|
mname + "(" + string(arg1) + ", " + string(arg2) +
|
|
"): args not equal";
|
|
throw new IllegalArgumentException(msg);
|
|
}
|
|
if (!equal(refValue, arg1, namedMXBeans)) {
|
|
final String msg =
|
|
mname + "(" + string(arg1) + ", " + string(arg2) +
|
|
"): args do not match ref: " + string(refValue);
|
|
throw new IllegalArgumentException(msg);
|
|
}
|
|
return refValue;
|
|
default:
|
|
throw new Error();
|
|
}
|
|
}
|
|
|
|
Object getRefValue(Field refField) throws Exception {
|
|
return refField.get(null);
|
|
}
|
|
|
|
private final Class intf;
|
|
private final NamedMXBeans namedMXBeans;
|
|
}
|
|
|
|
private static class MXBeanNullImplInvocationHandler
|
|
extends MXBeanImplInvocationHandler {
|
|
MXBeanNullImplInvocationHandler(Class intf, NamedMXBeans namedMXBeans) {
|
|
super(intf, namedMXBeans);
|
|
}
|
|
|
|
@Override
|
|
Object getRefValue(Field refField) throws Exception {
|
|
Class<?> type = refField.getType();
|
|
if (type.isPrimitive())
|
|
return super.getRefValue(refField);
|
|
else
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
private static final String[] prefixes = {
|
|
"get", "set", "op",
|
|
};
|
|
private static final int GET = 0, SET = 1, OP = 2;
|
|
|
|
private static String getName(Method m) {
|
|
return getName(m.getName());
|
|
}
|
|
|
|
private static String getName(String n) {
|
|
for (int i = 0; i < prefixes.length; i++) {
|
|
if (n.startsWith(prefixes[i]))
|
|
return n.substring(prefixes[i].length());
|
|
}
|
|
throw new Error();
|
|
}
|
|
|
|
private static int getType(Method m) {
|
|
return getType(m.getName());
|
|
}
|
|
|
|
private static int getType(String n) {
|
|
for (int i = 0; i < prefixes.length; i++) {
|
|
if (n.startsWith(prefixes[i]))
|
|
return i;
|
|
}
|
|
throw new Error();
|
|
}
|
|
|
|
static boolean equal(Object o1, Object o2, NamedMXBeans namedMXBeans) {
|
|
if (o1 == o2)
|
|
return true;
|
|
if (o1 == null || o2 == null)
|
|
return false;
|
|
if (o1.getClass().isArray()) {
|
|
if (!o2.getClass().isArray())
|
|
return false;
|
|
return deepEqual(o1, o2, namedMXBeans);
|
|
}
|
|
if (o1 instanceof Map) {
|
|
if (!(o2 instanceof Map))
|
|
return false;
|
|
return equalMap((Map) o1, (Map) o2, namedMXBeans);
|
|
}
|
|
if (o1 instanceof CompositeData && o2 instanceof CompositeData) {
|
|
return compositeDataEqual((CompositeData) o1, (CompositeData) o2,
|
|
namedMXBeans);
|
|
}
|
|
if (Proxy.isProxyClass(o1.getClass())) {
|
|
if (Proxy.isProxyClass(o2.getClass()))
|
|
return proxyEqual(o1, o2, namedMXBeans);
|
|
InvocationHandler ih = Proxy.getInvocationHandler(o1);
|
|
// if (ih instanceof MXBeanInvocationHandler) {
|
|
// return proxyEqualsObject((MXBeanInvocationHandler) ih,
|
|
// o2, namedMXBeans);
|
|
if (ih instanceof MBeanServerInvocationHandler) {
|
|
return true;
|
|
} else if (ih instanceof CompositeDataInvocationHandler) {
|
|
return o2.equals(o1);
|
|
// We assume the other object has a reasonable equals method
|
|
}
|
|
} else if (Proxy.isProxyClass(o2.getClass()))
|
|
return equal(o2, o1, namedMXBeans);
|
|
return o1.equals(o2);
|
|
}
|
|
|
|
// We'd use Arrays.deepEquals except we want the test to work on 1.4
|
|
// Note this code assumes no selfreferential arrays
|
|
// (as does Arrays.deepEquals)
|
|
private static boolean deepEqual(Object a1, Object a2,
|
|
NamedMXBeans namedMXBeans) {
|
|
int len = Array.getLength(a1);
|
|
if (len != Array.getLength(a2))
|
|
return false;
|
|
for (int i = 0; i < len; i++) {
|
|
Object e1 = Array.get(a1, i);
|
|
Object e2 = Array.get(a2, i);
|
|
if (!equal(e1, e2, namedMXBeans))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private static boolean equalMap(Map<?,?> m1, Map<?,?> m2,
|
|
NamedMXBeans namedMXBeans) {
|
|
if (m1.size() != m2.size())
|
|
return false;
|
|
if ((m1 instanceof SortedMap) != (m2 instanceof SortedMap))
|
|
return false;
|
|
for (Object k1 : m1.keySet()) {
|
|
if (!m2.containsKey(k1))
|
|
return false;
|
|
if (!equal(m1.get(k1), m2.get(k1), namedMXBeans))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// This is needed to work around a bug (5095277)
|
|
// in CompositeDataSupport.equals
|
|
private static boolean compositeDataEqual(CompositeData cd1,
|
|
CompositeData cd2,
|
|
NamedMXBeans namedMXBeans) {
|
|
if (cd1 == cd2)
|
|
return true;
|
|
if (!cd1.getCompositeType().equals(cd2.getCompositeType()))
|
|
return false;
|
|
Collection v1 = cd1.values();
|
|
Collection v2 = cd2.values();
|
|
if (v1.size() != v2.size())
|
|
return false; // should not happen
|
|
for (Iterator i1 = v1.iterator(), i2 = v2.iterator();
|
|
i1.hasNext(); ) {
|
|
if (!equal(i1.next(), i2.next(), namedMXBeans))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Also needed for 5095277
|
|
private static boolean proxyEqual(Object proxy1, Object proxy2,
|
|
NamedMXBeans namedMXBeans) {
|
|
if (proxy1.getClass() != proxy2.getClass())
|
|
return proxy1.equals(proxy2);
|
|
InvocationHandler ih1 = Proxy.getInvocationHandler(proxy1);
|
|
InvocationHandler ih2 = Proxy.getInvocationHandler(proxy2);
|
|
if (!(ih1 instanceof CompositeDataInvocationHandler)
|
|
|| !(ih2 instanceof CompositeDataInvocationHandler))
|
|
return proxy1.equals(proxy2);
|
|
CompositeData cd1 =
|
|
((CompositeDataInvocationHandler) ih1).getCompositeData();
|
|
CompositeData cd2 =
|
|
((CompositeDataInvocationHandler) ih2).getCompositeData();
|
|
return compositeDataEqual(cd1, cd2, namedMXBeans);
|
|
}
|
|
|
|
// private static boolean proxyEqualsObject(MXBeanInvocationHandler ih,
|
|
// Object o,
|
|
// NamedMXBeans namedMXBeans) {
|
|
// if (namedMXBeans.getMBeanServerConnection() !=
|
|
// ih.getMBeanServerConnection())
|
|
// return false;
|
|
//
|
|
// ObjectName on = ih.getObjectName();
|
|
// Object named = namedMXBeans.get(on);
|
|
// if (named == null)
|
|
// return false;
|
|
// return (o == named && ih.getMXBeanInterface().isInstance(named));
|
|
// }
|
|
|
|
/* I wanted to call this method toString(Object), but oddly enough
|
|
this meant that I couldn't call it from the inner class
|
|
MXBeanImplInvocationHandler, because the inherited Object.toString()
|
|
prevented that. */
|
|
static String string(Object o) {
|
|
if (o == null)
|
|
return "null";
|
|
if (o instanceof String)
|
|
return '"' + (String) o + '"';
|
|
if (o instanceof Collection)
|
|
return deepToString((Collection) o);
|
|
if (o.getClass().isArray())
|
|
return deepToString(o);
|
|
return o.toString();
|
|
}
|
|
|
|
private static String deepToString(Object o) {
|
|
StringBuffer buf = new StringBuffer();
|
|
buf.append("[");
|
|
int len = Array.getLength(o);
|
|
for (int i = 0; i < len; i++) {
|
|
if (i > 0)
|
|
buf.append(", ");
|
|
Object e = Array.get(o, i);
|
|
buf.append(string(e));
|
|
}
|
|
buf.append("]");
|
|
return buf.toString();
|
|
}
|
|
|
|
private static String deepToString(Collection c) {
|
|
return deepToString(c.toArray());
|
|
}
|
|
|
|
private static void compareTabularType(TabularType t1, TabularType t2) {
|
|
if (t1.equals(t2)) {
|
|
System.out.println("same tabular type");
|
|
return;
|
|
}
|
|
if (t1.getClassName().equals(t2.getClassName()))
|
|
System.out.println("same class name");
|
|
if (t1.getDescription().equals(t2.getDescription()))
|
|
System.out.println("same description");
|
|
else {
|
|
System.out.println("t1 description: " + t1.getDescription());
|
|
System.out.println("t2 description: " + t2.getDescription());
|
|
}
|
|
if (t1.getIndexNames().equals(t2.getIndexNames()))
|
|
System.out.println("same index names");
|
|
if (t1.getRowType().equals(t2.getRowType()))
|
|
System.out.println("same row type");
|
|
}
|
|
}
|