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