1 /* 2 * Copyright (c) 2005, 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 6175517 6278707 6318827 6305746 6392303 6600709 8010285 27 * @summary General MXBean test. 28 * @author Eamonn McManus 29 * @author Jaroslav Bachorik 30 * @modules java.desktop 31 * java.management 32 * @run clean MXBeanTest MerlinMXBean TigerMXBean 33 * @run build MXBeanTest MerlinMXBean TigerMXBean 34 * @run main MXBeanTest 35 */ 36 37 import java.lang.reflect.Array; 38 import java.lang.reflect.Field; 39 import java.lang.reflect.InvocationHandler; 40 import java.lang.reflect.Method; 41 import java.lang.reflect.Proxy; 42 import java.util.Arrays; 43 import java.util.Collection; 44 import java.util.HashMap; 45 import java.util.Iterator; 46 import java.util.Map; 47 import java.util.SortedMap; 48 import javax.management.JMX; 49 import javax.management.MBeanAttributeInfo; 50 import javax.management.MBeanInfo; 51 import javax.management.MBeanOperationInfo; 52 import javax.management.MBeanParameterInfo; 53 import javax.management.MBeanServer; 54 import javax.management.MBeanServerConnection; 55 import javax.management.MBeanServerFactory; 56 import javax.management.MBeanServerInvocationHandler; 57 import javax.management.NotCompliantMBeanException; 58 import javax.management.ObjectName; 59 import javax.management.StandardMBean; 60 import javax.management.openmbean.ArrayType; 61 import javax.management.openmbean.CompositeData; 62 import javax.management.openmbean.CompositeDataInvocationHandler; 63 import javax.management.openmbean.OpenType; 64 import javax.management.openmbean.SimpleType; 65 import javax.management.openmbean.TabularData; 66 import javax.management.openmbean.TabularType; 67 import javax.management.remote.JMXConnector; 68 import javax.management.remote.JMXConnectorFactory; 69 import javax.management.remote.JMXConnectorServer; 70 import javax.management.remote.JMXConnectorServerFactory; 71 import javax.management.remote.JMXServiceURL; 72 73 public class MXBeanTest { 74 public static void main(String[] args) throws Exception { 75 testInterface(MerlinMXBean.class, false); 76 testInterface(TigerMXBean.class, false); 77 testInterface(MerlinMXBean.class, true); 78 testInterface(TigerMXBean.class, true); 79 testExplicitMXBean(); 80 testSubclassMXBean(); 81 testIndirectMXBean(); 82 testNonCompliantMXBean("Private", new Private()); 83 testNonCompliantMXBean("NonCompliant", new NonCompliant()); 84 85 if (failures == 0) 86 System.out.println("Test passed"); 87 else 88 throw new Exception("TEST FAILURES: " + failures); 89 } 90 91 private static int failures = 0; 92 93 private static interface PrivateMXBean { 94 public int[] getInts(); 95 } 96 97 public static class Private implements PrivateMXBean { 98 public int[] getInts() { 99 return new int[]{1,2,3}; 100 } 101 } 102 103 public static interface NonCompliantMXBean { 104 public boolean getInt(); 105 public boolean isInt(); 106 public void setInt(int a); 107 public void setInt(long b); 108 } 109 110 public static class NonCompliant implements NonCompliantMXBean { 111 public boolean getInt() { 112 return false; 113 } 114 115 public boolean isInt() { 116 return true; 117 } 118 119 public void setInt(int a) { 120 } 121 122 public void setInt(long b) { 123 } 124 } 125 126 public static interface ExplicitMXBean { 127 public int[] getInts(); 128 } 129 public static class Explicit implements ExplicitMXBean { 130 public int[] getInts() { 131 return new int[] {1, 2, 3}; 132 } 133 } 134 public static class Subclass 135 extends StandardMBean 136 implements ExplicitMXBean { 137 public Subclass() { 138 super(ExplicitMXBean.class, true); 139 } 140 141 public int[] getInts() { 142 return new int[] {1, 2, 3}; 143 } 144 } 145 public static interface IndirectInterface extends ExplicitMXBean {} 146 public static class Indirect implements IndirectInterface { 147 public int[] getInts() { 148 return new int[] {1, 2, 3}; 149 } 150 } 151 152 private static void testNonCompliantMXBean(String type, Object bean) throws Exception { 153 System.out.println(type + " MXBean test..."); 154 MBeanServer mbs = MBeanServerFactory.newMBeanServer(); 155 ObjectName on = new ObjectName("test:type=" + type); 156 try { 157 mbs.registerMBean(bean, on); 158 failure(bean.getClass().getInterfaces()[0].getName() + " is not a compliant " 159 + "MXBean interface"); 160 } catch (NotCompliantMBeanException e) { 161 success("Non-compliant MXBean not registered"); 162 } 163 } 164 165 private static void testExplicitMXBean() throws Exception { 166 System.out.println("Explicit MXBean test..."); 167 MBeanServer mbs = MBeanServerFactory.newMBeanServer(); 168 ObjectName on = new ObjectName("test:type=Explicit"); 169 Explicit explicit = new Explicit(); 170 mbs.registerMBean(explicit, on); 171 testMXBean(mbs, on); 172 } 173 174 private static void testSubclassMXBean() throws Exception { 175 System.out.println("Subclass MXBean test..."); 176 MBeanServer mbs = MBeanServerFactory.newMBeanServer(); 177 ObjectName on = new ObjectName("test:type=Subclass"); 178 Subclass subclass = new Subclass(); 179 mbs.registerMBean(subclass, on); 180 testMXBean(mbs, on); 181 } 182 183 private static void testIndirectMXBean() throws Exception { 184 System.out.println("Indirect MXBean test..."); 185 MBeanServer mbs = MBeanServerFactory.newMBeanServer(); 186 ObjectName on = new ObjectName("test:type=Indirect"); 187 Indirect indirect = new Indirect(); 188 mbs.registerMBean(indirect, on); 189 testMXBean(mbs, on); 190 } 191 192 private static void testMXBean(MBeanServer mbs, ObjectName on) 193 throws Exception { 194 MBeanInfo mbi = mbs.getMBeanInfo(on); 195 MBeanAttributeInfo[] attrs = mbi.getAttributes(); 196 int nattrs = attrs.length; 197 if (mbi.getAttributes().length != 1) 198 failure("wrong number of attributes: " + attrs); 199 else { 200 MBeanAttributeInfo mbai = attrs[0]; 201 if (mbai.getName().equals("Ints") 202 && mbai.isReadable() && !mbai.isWritable() 203 && mbai.getDescriptor().getFieldValue("openType") 204 .equals(new ArrayType<int[]>(SimpleType.INTEGER, true)) 205 && attrs[0].getType().equals("[I")) 206 success("MBeanAttributeInfo"); 207 else 208 failure("MBeanAttributeInfo: " + mbai); 209 } 210 211 int[] ints = (int[]) mbs.getAttribute(on, "Ints"); 212 if (equal(ints, new int[] {1, 2, 3}, null)) 213 success("getAttribute"); 214 else 215 failure("getAttribute: " + Arrays.toString(ints)); 216 217 ExplicitMXBean proxy = 218 JMX.newMXBeanProxy(mbs, on, ExplicitMXBean.class); 219 int[] pints = proxy.getInts(); 220 if (equal(pints, new int[] {1, 2, 3}, null)) 221 success("getAttribute through proxy"); 222 else 223 failure("getAttribute through proxy: " + Arrays.toString(pints)); 224 } 225 226 private static class NamedMXBeans extends HashMap<ObjectName, Object> { 227 private static final long serialVersionUID = 0; 228 229 NamedMXBeans(MBeanServerConnection mbsc) { 230 this.mbsc = mbsc; 231 } 232 233 MBeanServerConnection getMBeanServerConnection() { 234 return mbsc; 235 } 236 237 private final MBeanServerConnection mbsc; 238 } 239 240 /* This is the core of the test. Given the MXBean interface c, we 241 make an MXBean object that implements that interface by 242 constructing a dynamic proxy. If the interface defines an 243 attribute Foo (with getFoo and setFoo methods), then it must 244 also contain a field (constant) Foo of the same type, and a 245 field (constant) FooType that is an OpenType. The field Foo is 246 a reference value for this case. We check that the attribute 247 does indeed have the given OpenType. The dynamically-created 248 MXBean will return the reference value from the getFoo() 249 method, and we check that that value survives the mapping to 250 open values and back when the attribute is accessed through an 251 MXBean proxy. The MXBean will also check in its setFoo method 252 that the value being set is equal to the reference value, which 253 tests that the mapping and unmapping also works in the other 254 direction. The interface should define an operation opFoo with 255 two parameters and a return value all of the same type as the 256 attribute. The MXBean will check that the two parameters are 257 equal to the reference value, and will return that value. The 258 test checks that calling the operation through an MXBean proxy 259 returns the reference value, again after mapping to and back 260 from open values. 261 262 If any field (constant) in the MXBean interface has a name that 263 ends with ObjectName, say FooObjectName, then its value must be 264 a String containing an ObjectName value. There must be a field 265 (constant) called Foo that is a valid MXBean, and that MXBean 266 will be registered in the MBean Server with the given name before 267 the test starts. This enables us to test that inter-MXBean 268 references are correctly converted to ObjectNames and back. 269 */ 270 private static <T> void testInterface(Class<T> c, boolean nullTest) 271 throws Exception { 272 273 System.out.println("Testing " + c.getName() + 274 (nullTest ? " for null values" : "") + "..."); 275 276 MBeanServer mbs = MBeanServerFactory.newMBeanServer(); 277 278 JMXServiceURL url = new JMXServiceURL("rmi", null, 0); 279 JMXConnectorServer cs = 280 JMXConnectorServerFactory.newJMXConnectorServer(url, null, mbs); 281 cs.start(); 282 JMXServiceURL addr = cs.getAddress(); 283 JMXConnector cc = JMXConnectorFactory.connect(addr); 284 MBeanServerConnection mbsc = cc.getMBeanServerConnection(); 285 286 NamedMXBeans namedMXBeans = new NamedMXBeans(mbsc); 287 InvocationHandler ih = 288 nullTest ? new MXBeanNullImplInvocationHandler(c, namedMXBeans) : 289 new MXBeanImplInvocationHandler(c, namedMXBeans); 290 T impl = c.cast(Proxy.newProxyInstance(c.getClassLoader(), 291 new Class[] {c}, 292 ih)); 293 ObjectName on = new ObjectName("test:type=" + c.getName()); 294 mbs.registerMBean(impl, on); 295 296 System.out.println("Register any MXBeans..."); 297 298 Field[] fields = c.getFields(); 299 for (Field field : fields) { 300 String n = field.getName(); 301 if (n.endsWith("ObjectName")) { 302 String objectNameString = (String) field.get(null); 303 String base = n.substring(0, n.length() - 10); 304 Field f = c.getField(base); 305 Object mxbean = f.get(null); 306 ObjectName objectName = 307 ObjectName.getInstance(objectNameString); 308 mbs.registerMBean(mxbean, objectName); 309 namedMXBeans.put(objectName, mxbean); 310 } 311 } 312 313 try { 314 testInterface(c, mbsc, on, namedMXBeans, nullTest); 315 } finally { 316 try { 317 cc.close(); 318 } finally { 319 cs.stop(); 320 } 321 } 322 } 323 324 private static <T> void testInterface(Class<T> c, 325 MBeanServerConnection mbsc, 326 ObjectName on, 327 NamedMXBeans namedMXBeans, 328 boolean nullTest) 329 throws Exception { 330 331 System.out.println("Type check..."); 332 333 MBeanInfo mbi = mbsc.getMBeanInfo(on); 334 MBeanAttributeInfo[] mbais = mbi.getAttributes(); 335 for (int i = 0; i < mbais.length; i++) { 336 MBeanAttributeInfo mbai = mbais[i]; 337 String name = mbai.getName(); 338 Field typeField = c.getField(name + "Type"); 339 OpenType typeValue = (OpenType) typeField.get(null); 340 OpenType openType = 341 (OpenType) mbai.getDescriptor().getFieldValue("openType"); 342 if (typeValue.equals(openType)) 343 success("attribute " + name); 344 else { 345 final String msg = 346 "Wrong type attribute " + name + ": " + 347 openType + " should be " + typeValue; 348 failure(msg); 349 } 350 } 351 352 MBeanOperationInfo[] mbois = mbi.getOperations(); 353 for (int i = 0; i < mbois.length; i++) { 354 MBeanOperationInfo mboi = mbois[i]; 355 String oname = mboi.getName(); 356 if (!oname.startsWith("op")) 357 throw new Error(); 358 OpenType retType = 359 (OpenType) mboi.getDescriptor().getFieldValue("openType"); 360 MBeanParameterInfo[] params = mboi.getSignature(); 361 MBeanParameterInfo p1i = params[0]; 362 MBeanParameterInfo p2i = params[1]; 363 OpenType p1Type = 364 (OpenType) p1i.getDescriptor().getFieldValue("openType"); 365 OpenType p2Type = 366 (OpenType) p2i.getDescriptor().getFieldValue("openType"); 367 if (!retType.equals(p1Type) || !p1Type.equals(p2Type)) { 368 final String msg = 369 "Parameter and return open types should all be same " + 370 "but are not: " + retType + " " + oname + "(" + p1Type + 371 ", " + p2Type + ")"; 372 failure(msg); 373 continue; 374 } 375 String name = oname.substring(2); 376 Field typeField = c.getField(name + "Type"); 377 OpenType typeValue = (OpenType) typeField.get(null); 378 if (typeValue.equals(retType)) 379 success("operation " + oname); 380 else { 381 final String msg = 382 "Wrong type operation " + oname + ": " + 383 retType + " should be " + typeValue; 384 failure(msg); 385 } 386 } 387 388 389 System.out.println("Mapping check..."); 390 391 Object proxy = 392 JMX.newMXBeanProxy(mbsc, on, c); 393 394 Method[] methods = c.getMethods(); 395 for (int i = 0; i < methods.length; i++) { 396 final Method method = methods[i]; 397 if (method.getDeclaringClass() != c) 398 continue; // skip hashCode() etc inherited from Object 399 final String mname = method.getName(); 400 final int what = getType(method); 401 final String name = getName(method); 402 final Field refField = c.getField(name); 403 if (nullTest && refField.getType().isPrimitive()) 404 continue; 405 final Field openTypeField = c.getField(name + "Type"); 406 final OpenType openType = (OpenType) openTypeField.get(null); 407 final Object refValue = nullTest ? null : refField.get(null); 408 Object setValue = refValue; 409 try { 410 Field onField = c.getField(name + "ObjectName"); 411 String refName = (String) onField.get(null); 412 ObjectName refObjName = ObjectName.getInstance(refName); 413 Class<?> mxbeanInterface = refField.getType(); 414 setValue = nullTest ? null : 415 JMX.newMXBeanProxy(mbsc, refObjName, mxbeanInterface); 416 } catch (Exception e) { 417 // no xObjectName field, setValue == refValue 418 } 419 boolean ok = true; 420 try { 421 switch (what) { 422 case GET: 423 final Object gotOpen = mbsc.getAttribute(on, name); 424 if (nullTest) { 425 if (gotOpen != null) { 426 failure(mname + " got non-null value " + 427 gotOpen); 428 ok = false; 429 } 430 } else if (!openType.isValue(gotOpen)) { 431 if (gotOpen instanceof TabularData) { 432 // detail the mismatch 433 TabularData gotTabular = (TabularData) gotOpen; 434 compareTabularType((TabularType) openType, 435 gotTabular.getTabularType()); 436 } 437 failure(mname + " got open data " + gotOpen + 438 " not valid for open type " + openType); 439 ok = false; 440 } 441 final Object got = method.invoke(proxy, (Object[]) null); 442 if (!equal(refValue, got, namedMXBeans)) { 443 failure(mname + " got " + string(got) + 444 ", should be " + string(refValue)); 445 ok = false; 446 } 447 break; 448 449 case SET: 450 method.invoke(proxy, new Object[] {setValue}); 451 break; 452 453 case OP: 454 final Object opped = 455 method.invoke(proxy, new Object[] {setValue, setValue}); 456 if (!equal(refValue, opped, namedMXBeans)) { 457 failure( 458 mname + " got " + string(opped) + 459 ", should be " + string(refValue) 460 ); 461 ok = false; 462 } 463 break; 464 465 default: 466 throw new Error(); 467 } 468 469 if (ok) 470 success(mname); 471 472 } catch (Exception e) { 473 failure(mname, e); 474 } 475 } 476 } 477 478 479 private static void success(String what) { 480 System.out.println("OK: " + what); 481 } 482 483 private static void failure(String what) { 484 System.out.println("FAILED: " + what); 485 failures++; 486 } 487 488 private static void failure(String what, Exception e) { 489 System.out.println("FAILED WITH EXCEPTION: " + what); 490 e.printStackTrace(System.out); 491 failures++; 492 } 493 494 private static class MXBeanImplInvocationHandler 495 implements InvocationHandler { 496 MXBeanImplInvocationHandler(Class intf, NamedMXBeans namedMXBeans) { 497 this.intf = intf; 498 this.namedMXBeans = namedMXBeans; 499 } 500 501 public Object invoke(Object proxy, Method method, Object[] args) 502 throws Throwable { 503 final String mname = method.getName(); 504 final int what = getType(method); 505 final String name = getName(method); 506 final Field refField = intf.getField(name); 507 final Object refValue = getRefValue(refField); 508 509 switch (what) { 510 case GET: 511 assert args == null; 512 return refValue; 513 514 case SET: 515 assert args.length == 1; 516 Object setValue = args[0]; 517 if (!equal(refValue, setValue, namedMXBeans)) { 518 final String msg = 519 mname + "(" + string(setValue) + 520 ") does not match ref: " + string(refValue); 521 throw new IllegalArgumentException(msg); 522 } 523 return null; 524 525 case OP: 526 assert args.length == 2; 527 Object arg1 = args[0]; 528 Object arg2 = args[1]; 529 if (!equal(arg1, arg2, namedMXBeans)) { 530 final String msg = 531 mname + "(" + string(arg1) + ", " + string(arg2) + 532 "): args not equal"; 533 throw new IllegalArgumentException(msg); 534 } 535 if (!equal(refValue, arg1, namedMXBeans)) { 536 final String msg = 537 mname + "(" + string(arg1) + ", " + string(arg2) + 538 "): args do not match ref: " + string(refValue); 539 throw new IllegalArgumentException(msg); 540 } 541 return refValue; 542 default: 543 throw new Error(); 544 } 545 } 546 547 Object getRefValue(Field refField) throws Exception { 548 return refField.get(null); 549 } 550 551 private final Class intf; 552 private final NamedMXBeans namedMXBeans; 553 } 554 555 private static class MXBeanNullImplInvocationHandler 556 extends MXBeanImplInvocationHandler { 557 MXBeanNullImplInvocationHandler(Class intf, NamedMXBeans namedMXBeans) { 558 super(intf, namedMXBeans); 559 } 560 561 @Override 562 Object getRefValue(Field refField) throws Exception { 563 Class<?> type = refField.getType(); 564 if (type.isPrimitive()) 565 return super.getRefValue(refField); 566 else 567 return null; 568 } 569 } 570 571 572 private static final String[] prefixes = { 573 "get", "set", "op", 574 }; 575 private static final int GET = 0, SET = 1, OP = 2; 576 577 private static String getName(Method m) { 578 return getName(m.getName()); 579 } 580 581 private static String getName(String n) { 582 for (int i = 0; i < prefixes.length; i++) { 583 if (n.startsWith(prefixes[i])) 584 return n.substring(prefixes[i].length()); 585 } 586 throw new Error(); 587 } 588 589 private static int getType(Method m) { 590 return getType(m.getName()); 591 } 592 593 private static int getType(String n) { 594 for (int i = 0; i < prefixes.length; i++) { 595 if (n.startsWith(prefixes[i])) 596 return i; 597 } 598 throw new Error(); 599 } 600 601 static boolean equal(Object o1, Object o2, NamedMXBeans namedMXBeans) { 602 if (o1 == o2) 603 return true; 604 if (o1 == null || o2 == null) 605 return false; 606 if (o1.getClass().isArray()) { 607 if (!o2.getClass().isArray()) 608 return false; 609 return deepEqual(o1, o2, namedMXBeans); 610 } 611 if (o1 instanceof Map) { 612 if (!(o2 instanceof Map)) 613 return false; 614 return equalMap((Map) o1, (Map) o2, namedMXBeans); 615 } 616 if (o1 instanceof CompositeData && o2 instanceof CompositeData) { 617 return compositeDataEqual((CompositeData) o1, (CompositeData) o2, 618 namedMXBeans); 619 } 620 if (Proxy.isProxyClass(o1.getClass())) { 621 if (Proxy.isProxyClass(o2.getClass())) 622 return proxyEqual(o1, o2, namedMXBeans); 623 InvocationHandler ih = Proxy.getInvocationHandler(o1); 624 // if (ih instanceof MXBeanInvocationHandler) { 625 // return proxyEqualsObject((MXBeanInvocationHandler) ih, 626 // o2, namedMXBeans); 627 if (ih instanceof MBeanServerInvocationHandler) { 628 return true; 629 } else if (ih instanceof CompositeDataInvocationHandler) { 630 return o2.equals(o1); 631 // We assume the other object has a reasonable equals method 632 } 633 } else if (Proxy.isProxyClass(o2.getClass())) 634 return equal(o2, o1, namedMXBeans); 635 return o1.equals(o2); 636 } 637 638 // We'd use Arrays.deepEquals except we want the test to work on 1.4 639 // Note this code assumes no selfreferential arrays 640 // (as does Arrays.deepEquals) 641 private static boolean deepEqual(Object a1, Object a2, 642 NamedMXBeans namedMXBeans) { 643 int len = Array.getLength(a1); 644 if (len != Array.getLength(a2)) 645 return false; 646 for (int i = 0; i < len; i++) { 647 Object e1 = Array.get(a1, i); 648 Object e2 = Array.get(a2, i); 649 if (!equal(e1, e2, namedMXBeans)) 650 return false; 651 } 652 return true; 653 } 654 655 private static boolean equalMap(Map<?,?> m1, Map<?,?> m2, 656 NamedMXBeans namedMXBeans) { 657 if (m1.size() != m2.size()) 658 return false; 659 if ((m1 instanceof SortedMap) != (m2 instanceof SortedMap)) 660 return false; 661 for (Object k1 : m1.keySet()) { 662 if (!m2.containsKey(k1)) 663 return false; 664 if (!equal(m1.get(k1), m2.get(k1), namedMXBeans)) 665 return false; 666 } 667 return true; 668 } 669 670 // This is needed to work around a bug (5095277) 671 // in CompositeDataSupport.equals 672 private static boolean compositeDataEqual(CompositeData cd1, 673 CompositeData cd2, 674 NamedMXBeans namedMXBeans) { 675 if (cd1 == cd2) 676 return true; 677 if (!cd1.getCompositeType().equals(cd2.getCompositeType())) 678 return false; 679 Collection v1 = cd1.values(); 680 Collection v2 = cd2.values(); 681 if (v1.size() != v2.size()) 682 return false; // should not happen 683 for (Iterator i1 = v1.iterator(), i2 = v2.iterator(); 684 i1.hasNext(); ) { 685 if (!equal(i1.next(), i2.next(), namedMXBeans)) 686 return false; 687 } 688 return true; 689 } 690 691 // Also needed for 5095277 692 private static boolean proxyEqual(Object proxy1, Object proxy2, 693 NamedMXBeans namedMXBeans) { 694 if (proxy1.getClass() != proxy2.getClass()) 695 return proxy1.equals(proxy2); 696 InvocationHandler ih1 = Proxy.getInvocationHandler(proxy1); 697 InvocationHandler ih2 = Proxy.getInvocationHandler(proxy2); 698 if (!(ih1 instanceof CompositeDataInvocationHandler) 699 || !(ih2 instanceof CompositeDataInvocationHandler)) 700 return proxy1.equals(proxy2); 701 CompositeData cd1 = 702 ((CompositeDataInvocationHandler) ih1).getCompositeData(); 703 CompositeData cd2 = 704 ((CompositeDataInvocationHandler) ih2).getCompositeData(); 705 return compositeDataEqual(cd1, cd2, namedMXBeans); 706 } 707 708 // private static boolean proxyEqualsObject(MXBeanInvocationHandler ih, 709 // Object o, 710 // NamedMXBeans namedMXBeans) { 711 // if (namedMXBeans.getMBeanServerConnection() != 712 // ih.getMBeanServerConnection()) 713 // return false; 714 // 715 // ObjectName on = ih.getObjectName(); 716 // Object named = namedMXBeans.get(on); 717 // if (named == null) 718 // return false; 719 // return (o == named && ih.getMXBeanInterface().isInstance(named)); 720 // } 721 722 /* I wanted to call this method toString(Object), but oddly enough 723 this meant that I couldn't call it from the inner class 724 MXBeanImplInvocationHandler, because the inherited Object.toString() 725 prevented that. */ 726 static String string(Object o) { 727 if (o == null) 728 return "null"; 729 if (o instanceof String) 730 return '"' + (String) o + '"'; 731 if (o instanceof Collection) 732 return deepToString((Collection) o); 733 if (o.getClass().isArray()) 734 return deepToString(o); 735 return o.toString(); 736 } 737 738 private static String deepToString(Object o) { 739 StringBuffer buf = new StringBuffer(); 740 buf.append("["); 741 int len = Array.getLength(o); 742 for (int i = 0; i < len; i++) { 743 if (i > 0) 744 buf.append(", "); 745 Object e = Array.get(o, i); 746 buf.append(string(e)); 747 } 748 buf.append("]"); 749 return buf.toString(); 750 } 751 752 private static String deepToString(Collection c) { 753 return deepToString(c.toArray()); 754 } 755 756 private static void compareTabularType(TabularType t1, TabularType t2) { 757 if (t1.equals(t2)) { 758 System.out.println("same tabular type"); 759 return; 760 } 761 if (t1.getClassName().equals(t2.getClassName())) 762 System.out.println("same class name"); 763 if (t1.getDescription().equals(t2.getDescription())) 764 System.out.println("same description"); 765 else { 766 System.out.println("t1 description: " + t1.getDescription()); 767 System.out.println("t2 description: " + t2.getDescription()); 768 } 769 if (t1.getIndexNames().equals(t2.getIndexNames())) 770 System.out.println("same index names"); 771 if (t1.getRowType().equals(t2.getRowType())) 772 System.out.println("same row type"); 773 } 774 }