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