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 
41 import javax.management.*;
42 import java.lang.reflect.*;
43 
44 public class MBeanInfoEqualsTest {
45     // Class just used for reflection
46     private static class Toy {
Toy()47         public Toy() {}
Toy(int a, String b)48         public Toy(int a, String b) {}
getA()49         public int getA() {return 0;}
setA(int a)50         public void setA(int a) {}
isB()51         public boolean isB() {return false;}
setB(boolean b)52         public void setB(boolean b) {}
run()53         public void run() {}
blah(int a, String b)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
newMBeanAttributeInfo(String name, String description, Method getter, Method setter)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 {
ImmutableMBeanInfo(String className, String description, MBeanAttributeInfo[] attributes, MBeanConstructorInfo[] constructors, MBeanOperationInfo[] operations, MBeanNotificationInfo[] notifications)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 
MutableMBeanInfo(String className, String description, MBeanAttributeInfo[] attributes, MBeanConstructorInfo[] constructors, MBeanOperationInfo[] operations, MBeanNotificationInfo[] notifications)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 
getClassName()274         public String getClassName() {
275             return className;
276         }
277 
getDescription()278         public String getDescription() {
279             return description;
280         }
281 
getAttributes()282         public MBeanAttributeInfo[] getAttributes() {
283             return attributes;
284         }
285 
getOperations()286         public MBeanOperationInfo[] getOperations() {
287             return operations;
288         }
289 
getConstructors()290         public MBeanConstructorInfo[] getConstructors() {
291             return constructors;
292         }
293 
getNotifications()294         public MBeanNotificationInfo[] getNotifications() {
295             return notifications;
296         }
297     }
298 
checkEquals(String what, Object[][] equivs)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 
checkSame(String what, Object[] equiv)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 
checkDifferent(String what, Object[] equiv1, Object[] equiv2)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 
main(String[] args)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