1/*
2 * Copyright (c) 2003, 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 4719923
27 * @summary Test that MBeanInfo.equals works even for mutable subclasses
28 * @author Eamonn McManus
29 *
30 * @run clean MBeanInfoEqualsTest
31 * @run build MBeanInfoEqualsTest
32 * @run main MBeanInfoEqualsTest
33 */
34
35/* Test that MBeanInfo and its referenced classes implement the equals
36   and hashCode methods correctly.  These classes include some magic
37   to improve performance based on the classes' immutability.  The
38   logic checks that any subclasses encountered remain immutable, and
39   falls back on less performant code if not.  */
40
41import javax.management.*;
42import java.lang.reflect.*;
43
44public class MBeanInfoEqualsTest {
45    // Class just used for reflection
46    private static class Toy {
47        public Toy() {}
48        public Toy(int a, String b) {}
49        public int getA() {return 0;}
50        public void setA(int a) {}
51        public boolean isB() {return false;}
52        public void setB(boolean b) {}
53        public void run() {}
54        public void blah(int a, String b) {}
55    }
56
57    static final Class toy = Toy.class;
58    static final Constructor con1, con2;
59    static final Method getA, setA, isB, setB, run, blah;
60    static {
61        try {
62            con1 = toy.getConstructor(new Class[] {});
63            con2 = toy.getConstructor(new Class[] {Integer.TYPE,
64                                                   String.class});
65            getA = toy.getMethod("getA", new Class[] {});
66            setA = toy.getMethod("setA", new Class[] {Integer.TYPE});
67            isB  = toy.getMethod("isB",  new Class[] {});
68            setB = toy.getMethod("setB", new Class[] {Boolean.TYPE});
69            run  = toy.getMethod("run",  new Class[] {});
70            blah = toy.getMethod("blah", new Class[] {Integer.TYPE,
71                                                      String.class});
72        } catch (Exception e) {
73            throw new Error(e.getMessage());
74        }
75    }
76
77    private static final MBeanAttributeInfo
78        newMBeanAttributeInfo(String name, String description,
79                              Method getter, Method setter) {
80        try {
81            return new MBeanAttributeInfo(name, description, getter, setter);
82        } catch (IntrospectionException e) {
83            throw new Error(e.getMessage());
84        }
85    }
86
87    static final MBeanAttributeInfo
88        a1a = new MBeanAttributeInfo("thing", "java.foo.bar", "an attribute",
89                                     true, true, false),
90        a1b = new MBeanAttributeInfo("thing", "java.foo.bar", "an attribute",
91                                     true, true, false),
92        a2a = new MBeanAttributeInfo("splob", "java.foo.bar", "an attribute",
93                                     true, true, false),
94        a2b = new MBeanAttributeInfo(a2a.getName(), a2a.getType(),
95                                     a2a.getDescription(), a2a.isReadable(),
96                                     a2a.isWritable(), a2a.isIs()),
97        a3  = new MBeanAttributeInfo("splob", "java.foo.bar", "a whatsit",
98                                     true, true, false),
99        a4  = new MBeanAttributeInfo("splob", "java.foo.bar", "a whatsit",
100                                     false, true, false),
101        a5a = newMBeanAttributeInfo("a", "an attribute", getA, setA),
102        a5b = new MBeanAttributeInfo("a", "int", "an attribute",
103                                     true, true, false),
104        a6a = newMBeanAttributeInfo("a", "an attribute", getA, null),
105        a6b = new MBeanAttributeInfo("a", "int", "an attribute",
106                                     true, false, false),
107        a7a = newMBeanAttributeInfo("a", "an attribute", null, setA),
108        a7b = new MBeanAttributeInfo("a", "int", "an attribute",
109                                     false, true, false),
110        a8a = newMBeanAttributeInfo("b", "an attribute", isB, setB),
111        a8b = new MBeanAttributeInfo("b", "boolean", "an attribute",
112                                     true, true, true),
113        a9a = newMBeanAttributeInfo("b", "an attribute", isB, null),
114        a9b = new MBeanAttributeInfo("b", "boolean", "an attribute",
115                                     true, false, true);
116
117    static final MBeanParameterInfo
118        p1a = new MBeanParameterInfo("thing", "java.foo.bar", "a parameter"),
119        p1b = new MBeanParameterInfo("thing", "java.foo.bar", "a parameter"),
120        p2  = new MBeanParameterInfo("splob", "java.foo.bar", "a parameter"),
121        p3  = new MBeanParameterInfo("thing", "java.foo.bax", "a parameter"),
122        p4  = new MBeanParameterInfo("thing", "java.foo.bar", "a whatsit");
123
124    static final MBeanConstructorInfo
125        c1a = new MBeanConstructorInfo("a constructor", con1),
126        c1b = new MBeanConstructorInfo(c1a.getName(), "a constructor",
127                                       new MBeanParameterInfo[0]),
128        c1c = new MBeanConstructorInfo(c1a.getName(), c1a.getDescription(),
129                                       c1a.getSignature()),
130        c1d = new MBeanConstructorInfo(c1a.getName(), c1a.getDescription(),
131                                       null),
132        c2a = new MBeanConstructorInfo("another constructor", con2),
133        c2b = new MBeanConstructorInfo(c2a.getName(),
134                                       c2a.getDescription(),
135                                       c2a.getSignature()),
136        c3a = new MBeanConstructorInfo("conName", "a constructor",
137                                       new MBeanParameterInfo[] {p2, p3}),
138        c3b = new MBeanConstructorInfo("conName", "a constructor",
139                                       new MBeanParameterInfo[] {p2, p3}),
140        c4  = new MBeanConstructorInfo("conName", "a constructor",
141                                       new MBeanParameterInfo[] {p3, p2}),
142        c5  = new MBeanConstructorInfo("otherName", "a constructor",
143                                       new MBeanParameterInfo[] {p3, p2}),
144        c6  = new MBeanConstructorInfo("otherName", "another constructor",
145                                       new MBeanParameterInfo[] {p3, p2});
146
147    static final MBeanOperationInfo
148        o1a = new MBeanOperationInfo("an operation", run),
149        o1b = new MBeanOperationInfo("an operation", run),
150        o1c = new MBeanOperationInfo("run", "an operation",
151                                     o1a.getSignature(), "void",
152                                     o1a.getImpact()),
153        o1d = new MBeanOperationInfo("run", "an operation",
154                                     new MBeanParameterInfo[0], "void",
155                                     o1a.getImpact()),
156        o1e = new MBeanOperationInfo("run", "an operation",
157                                     null, "void",
158                                     o1a.getImpact()),
159        o2a = new MBeanOperationInfo("another operation", blah),
160        o2b = new MBeanOperationInfo(o2a.getName(), o2a.getDescription(),
161                                     o2a.getSignature(), o2a.getReturnType(),
162                                     o2a.getImpact());
163
164    static final MBeanNotificationInfo
165        n1a = new MBeanNotificationInfo(new String[] {"a.b", "c.d"},
166                                        "x.y.z",
167                                        "a notification info"),
168        n1b = new MBeanNotificationInfo(new String[] {"a.b", "c.d"},
169                                        "x.y.z",
170                                        "a notification info"),
171        n2a = new MBeanNotificationInfo(new String[] {"a.b", "c.d"},
172                                        "x.y.z",
173                                        "another notification info"),
174        n2b = new MBeanNotificationInfo(n2a.getNotifTypes(),
175                                        n2a.getName(),
176                                        n2a.getDescription()),
177        n3  = new MBeanNotificationInfo(new String[] {"a.b", "c.d"},
178                                        "x.y.zz",
179                                        "a notification info"),
180        n4  = new MBeanNotificationInfo(new String[] {"c.d", "a.b"},
181                                        "x.y.z",
182                                        "a notification info");
183
184    static final MBeanAttributeInfo[]
185        xa1a = {a1a, a2a},
186        xa1b = {a1b, a2b},
187        xa2a = {a2a, a1a};
188
189    static final MBeanConstructorInfo[]
190        xc1a = {c1a, c2a},
191        xc1b = {c1b, c2b},
192        xc2a = {c2a, c1a};
193
194    static final MBeanOperationInfo[]
195        xo1a = {o1a, o2a},
196        xo1b = {o1b, o2b},
197        xo2a = {o2a, o1a};
198
199    static final MBeanNotificationInfo[]
200        xn1a = {n1a, n2a},
201        xn1b = {n1b, n2b},
202        xn2a = {n2a, n1a};
203
204    static final MBeanInfo
205        i1a = new MBeanInfo("a.b.c", "an MBean info", xa1a, xc1a, xo1a, xn1a),
206        i1b = new MBeanInfo("a.b.c", "an MBean info", xa1a, xc1a, xo1a, xn1a),
207        i1c = new MBeanInfo("a.b.c", "an MBean info", xa1b, xc1b, xo1b, xn1b),
208        i1d = new MutableMBeanInfo("a.b.c", "an MBean info", xa1b, xc1b, xo1b,
209                                   xn1b),
210        i1e = new ImmutableMBeanInfo("a.b.c", "an MBean info", xa1b, xc1b,
211                                     xo1b, xn1b),
212        i1f = new ImmutableMBeanInfo("a.b.c", "an MBean info", xa1b, xc1b,
213                                     xo1b, xn1b),
214        i2a = new MBeanInfo("a.b.cc", "an MBean info", xa1a, xc1a, xo1a, xn1a),
215        i2b = new MBeanInfo(i2a.getClassName(), i2a.getDescription(),
216                            i2a.getAttributes(), i2a.getConstructors(),
217                            i2a.getOperations(), i2a.getNotifications()),
218        i3  = new MBeanInfo("a.b.c", "another MBean info", xa1a, xc1a, xo1a,
219                            xn1a),
220        i4  = new MBeanInfo("a.b.c", "an MBean info", xa2a, xc1a, xo1a, xn1a),
221        i5  = new MBeanInfo("a.b.c", "an MBean info", xa1a, xc2a, xo1a, xn1a),
222        i6  = new MBeanInfo("a.b.c", "an MBean info", xa1a, xc1a, xo2a, xn1a),
223        i7  = new MBeanInfo("a.b.c", "an MBean info", xa1a, xc1a, xo1a, xn2a);
224
225    static final Object[][] equivalenceClasses = {
226        {a1a, a1b}, {a2a, a2b}, {a3}, {a4}, {a5a, a5b}, {a6a, a6b}, {a7a, a7b},
227        {a8a, a8b}, {a9a, a9b},
228        {c1a, c1b, c1c, c1d}, {c2a, c2b}, {c3a, c3b}, {c4}, {c5}, {c6},
229        {o1a, o1b, o1c, o1d, o1e}, {o2a, o2b},
230        {p1a, p1b}, {p2}, {p3}, {p4},
231        {n1a, n1b}, {n2a, n2b}, {n3}, {n4},
232        {i1a, i1b, i1c, i1d, i1e, i1f}, {i2a, i2b}, {i3}, {i4}, {i5}, {i6},
233        {i7},
234    };
235
236    private static class ImmutableMBeanInfo extends MBeanInfo {
237        ImmutableMBeanInfo(String className,
238                           String description,
239                           MBeanAttributeInfo[] attributes,
240                           MBeanConstructorInfo[] constructors,
241                           MBeanOperationInfo[] operations,
242                           MBeanNotificationInfo[] notifications) {
243            super(className, description, attributes, constructors, operations,
244                  notifications);
245        }
246    }
247
248    /* This class checks that the MBeanInfo.equals() method really
249       does call getClassName() etc rather than referring to its
250       private fields.  */
251    private static class MutableMBeanInfo extends MBeanInfo {
252        private final String className;
253        private final String description;
254        private final MBeanAttributeInfo[] attributes;
255        private final MBeanOperationInfo[] operations;
256        private final MBeanConstructorInfo[] constructors;
257        private final MBeanNotificationInfo[] notifications;
258
259        MutableMBeanInfo(String className,
260                         String description,
261                         MBeanAttributeInfo[] attributes,
262                         MBeanConstructorInfo[] constructors,
263                         MBeanOperationInfo[] operations,
264                         MBeanNotificationInfo[] notifications) {
265            super("bogus", null, null, null, null, null);
266            this.className = className;
267            this.description = description;
268            this.attributes = attributes;
269            this.constructors = constructors;
270            this.operations = operations;
271            this.notifications = notifications;
272        }
273
274        public String getClassName() {
275            return className;
276        }
277
278        public String getDescription() {
279            return description;
280        }
281
282        public MBeanAttributeInfo[] getAttributes() {
283            return attributes;
284        }
285
286        public MBeanOperationInfo[] getOperations() {
287            return operations;
288        }
289
290        public MBeanConstructorInfo[] getConstructors() {
291            return constructors;
292        }
293
294        public MBeanNotificationInfo[] getNotifications() {
295            return notifications;
296        }
297    }
298
299    private static boolean checkEquals(String what, Object[][] equivs) {
300        boolean ok = true;
301        /* The equivs array is an array of equivalence classes.  The members
302           of each equivalence class must be equal among themselves.
303           Each member of each equivalence class must be different from
304           each member of each other equivalence class.  */
305        for (int ei = 0; ei < equivs.length; ei++) {
306            Object[] ec1 = equivs[ei];
307            ok &= checkSame(what + " equivalence class " + ei, ec1);
308            for (int ej = 0; ej < equivs.length; ej++) {
309                if (ei == ej)
310                    continue;
311                Object[] ec2 = equivs[ej];
312                ok &= checkDifferent(what + " equivalence classes " +
313                                     ei + " and " + ej, ec1, ec2);
314            }
315        }
316        if (ok)
317            System.out.println("equals test for " + what + " passed");
318        return ok;
319    }
320
321    /* We could simplify this test to compare every element with every
322       other and choose whether they are supposed to be the same based
323       on whether they are in the same equivalence class.  A bit
324       simpler, but so what.  */
325
326    private static boolean checkSame(String what, Object[] equiv) {
327        boolean ok = true;
328        for (int i = 0; i < equiv.length; i++) {
329            final Object o1 = equiv[i];
330            for (int j = 0; j < equiv.length; j++) {
331                final Object o2 = equiv[j];
332                if (!o1.equals(o2)) {
333                    System.out.println("equals test: " + what +
334                                       ": !obj[" + i +
335                                       "].equals(obj[" + j + "])");
336                    System.out.println("..." + o1 + "  " + o2);
337                    ok = false;
338                }
339                if (o1.hashCode() != o2.hashCode()) {
340                    System.out.println("equals test: " + what +
341                                       ": obj[" + i +
342                                       "].hashCode() != obj[" + j +
343                                       "].hashCode()");
344                    System.out.println("..." + o1 + "  " + o2);
345                    ok = false;
346                }
347            }
348        }
349        return ok;
350    }
351
352    private static boolean checkDifferent(String what, Object[] equiv1,
353                                          Object[] equiv2) {
354        boolean ok = true;
355        for (int i = 0; i < equiv1.length; i++) {
356            final Object o1 = equiv1[i];
357            for (int j = 0; j < equiv2.length; j++) {
358                final Object o2 = equiv2[j];
359                if (o1.equals(o2)) {
360                    System.out.println("equals test " + what + ": obj[" +
361                                       i + "].equals(obj[" + j + "])");
362                    System.out.println("..." + o1 + "  " + o2);
363                    ok = false;
364                }
365            }
366        }
367        return ok;
368    }
369
370    public static void main(String[] args) throws Exception {
371        boolean ok = true;
372        ok &= checkEquals("equivalence", equivalenceClasses);
373        if (ok) {
374            System.out.println("all tests passed");
375        } else {
376            System.out.println("at least one test failed");
377            System.exit(1);
378        }
379    }
380}
381