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 {
  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 }