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