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