1 /*
   2  * Copyright (c) 2005, 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 6221321 6295867
  27  * @summary Test that annotations in Standard MBean interfaces
  28  * correctly produce Descriptor entries
  29  * @author Eamonn McManus
  30  *
  31  * @run clean AnnotationTest
  32  * @run build AnnotationTest
  33  * @run main AnnotationTest
  34  */
  35 
  36 import java.lang.annotation.Retention;
  37 import java.lang.annotation.RetentionPolicy;
  38 import javax.management.Descriptor;
  39 import javax.management.DescriptorRead;
  40 import javax.management.DescriptorKey;
  41 import javax.management.ImmutableDescriptor;
  42 import javax.management.MBeanAttributeInfo;
  43 import javax.management.MBeanConstructorInfo;
  44 import javax.management.MBeanInfo;
  45 import javax.management.MBeanOperationInfo;
  46 import javax.management.MBeanServer;
  47 import javax.management.ObjectName;
  48 
  49 /*
  50   This test checks that annotations produce Descriptor entries as
  51   specified in javax.management.DescriptorKey.  It does two things:
  52 
  53   - An annotation consisting of an int and a String, each with an
  54     appropriate @DescriptorKey annotation, is placed on every program
  55     element where it can map to a Descriptor, namely:
  56 
  57     . on an MBean interface
  58     . on a getter for a read-only attribute
  59     . on a setter for a write-only attribute
  60     . on the getter but not the setter for a read/write attribute
  61     . on the setter but not the getter for a read/write attribute
  62     . on both the getter and the setter for a read/write attribute
  63     . on an operation
  64     . on each parameter of an operation
  65     . on a public constructor with no parameters
  66     . on a public constructor with a parameter
  67     . on the parameter of that public constructor
  68     . on all of the above for an MXBean instead of an MBean
  69 
  70     The test checks that in each case the corresponding Descriptor
  71     appears in the appropriate place inside the MBean's MBeanInfo.
  72 
  73   - An annotation consisting of enough other types to ensure coverage
  74     is placed on a getter.  The test checks that the generated
  75     MBeanAttributeInfo contains the corresponding Descriptor.  The tested
  76     types are the following:
  77 
  78     . Class
  79     . an enumeration type (java.lang.annotation.RetentionPolicy)
  80     . boolean
  81     . String[]
  82     . Class[]
  83     . int[]
  84     . an array of enumeration type (RetentionPolicy[])
  85     . boolean[]
  86  */
  87 public class AnnotationTest {
  88     private static String failed = null;
  89 
  90     @Retention(RetentionPolicy.RUNTIME)
  91     public static @interface Pair {
  92         @DescriptorKey("x")
  93         int x();
  94         @DescriptorKey("y")
  95         String y();
  96     }
  97 
  98     @Retention(RetentionPolicy.RUNTIME)
  99     public static @interface Full {
 100         @DescriptorKey("class")
 101         Class classValue();
 102         @DescriptorKey("enum")
 103         RetentionPolicy enumValue();
 104         @DescriptorKey("boolean")
 105         boolean booleanValue();
 106         @DescriptorKey("stringArray")
 107         String[] stringArrayValue();
 108         @DescriptorKey("classArray")
 109         Class[] classArrayValue();
 110         @DescriptorKey("intArray")
 111         int[] intArrayValue();
 112         @DescriptorKey("enumArray")
 113         RetentionPolicy[] enumArrayValue();
 114         @DescriptorKey("booleanArray")
 115         boolean[] booleanArrayValue();
 116     }
 117 
 118     /* We use the annotation @Pair(x = 3, y = "foo") everywhere, and this is
 119        the Descriptor that it should produce: */
 120     private static Descriptor expectedDescriptor =
 121         new ImmutableDescriptor(new String[] {"x", "y"},
 122                                 new Object[] {3, "foo"});
 123 
 124     private static Descriptor expectedFullDescriptor =
 125         new ImmutableDescriptor(new String[] {
 126                                     "class", "enum", "boolean", "stringArray",
 127                                     "classArray", "intArray", "enumArray",
 128                                     "booleanArray",
 129                                 },
 130                                 new Object[] {
 131                                     Full.class.getName(),
 132                                     RetentionPolicy.RUNTIME.name(),
 133                                     false,
 134                                     new String[] {"foo", "bar"},
 135                                     new String[] {Full.class.getName()},
 136                                     new int[] {1, 2},
 137                                     new String[] {RetentionPolicy.RUNTIME.name()},
 138                                     new boolean[] {false, true},
 139                                 });
 140 
 141     @Pair(x = 3, y = "foo")
 142     public static interface ThingMBean {
 143         @Pair(x = 3, y = "foo")
 144         @Full(classValue=Full.class,
 145               enumValue=RetentionPolicy.RUNTIME,
 146               booleanValue=false,
 147               stringArrayValue={"foo", "bar"},
 148               classArrayValue={Full.class},
 149               intArrayValue={1, 2},
 150               enumArrayValue={RetentionPolicy.RUNTIME},
 151               booleanArrayValue={false, true})
 152         int getReadOnly();
 153 
 154         @Pair(x = 3, y = "foo")
 155         void setWriteOnly(int x);
 156 
 157         @Pair(x = 3, y = "foo")
 158         int getReadWrite1();
 159         void setReadWrite1(int x);
 160 
 161         @Pair(x = 3, y = "foo")
 162         int getReadWrite2();
 163         @Pair(x = 3, y = "foo")
 164         void setReadWrite2(int x);
 165 
 166         int getReadWrite3();
 167         @Pair(x = 3, y = "foo")
 168         void setReadWrite3(int x);
 169 
 170         @Pair(x = 3, y = "foo")
 171         int operation(@Pair(x = 3, y = "foo") int p1,
 172                       @Pair(x = 3, y = "foo") int p2);
 173     }
 174 
 175     public static class Thing implements ThingMBean {
 176         @Pair(x = 3, y = "foo")
 177         public Thing() {}
 178 
 179         @Pair(x = 3, y = "foo")
 180         public Thing(@Pair(x = 3, y = "foo") int p1) {}
 181 
 182         public int getReadOnly() {return 0;}
 183 
 184         public void setWriteOnly(int x) {}
 185 
 186         public int getReadWrite1() {return 0;}
 187         public void setReadWrite1(int x) {}
 188 
 189         public int getReadWrite2() {return 0;}
 190         public void setReadWrite2(int x) {}
 191 
 192         public int getReadWrite3() {return 0;}
 193         public void setReadWrite3(int x) {}
 194 
 195         public int operation(int p1, int p2) {return 0;}
 196     }
 197 
 198     @Pair(x = 3, y = "foo")
 199     public static interface ThingMXBean extends ThingMBean {}
 200 
 201     public static class ThingImpl implements ThingMXBean {
 202         @Pair(x = 3, y = "foo")
 203         public ThingImpl() {}
 204 
 205         @Pair(x = 3, y = "foo")
 206         public ThingImpl(@Pair(x = 3, y = "foo") int p1) {}
 207 
 208         public int getReadOnly() {return 0;}
 209 
 210         public void setWriteOnly(int x) {}
 211 
 212         public int getReadWrite1() {return 0;}
 213         public void setReadWrite1(int x) {}
 214 
 215         public int getReadWrite2() {return 0;}
 216         public void setReadWrite2(int x) {}
 217 
 218         public int getReadWrite3() {return 0;}
 219         public void setReadWrite3(int x) {}
 220 
 221         public int operation(int p1, int p2) {return 0;}
 222     }
 223 
 224     public static void main(String[] args) throws Exception {
 225         System.out.println("Testing that annotations are correctly " +
 226                            "reflected in Descriptor entries");
 227 
 228         MBeanServer mbs =
 229             java.lang.management.ManagementFactory.getPlatformMBeanServer();
 230         ObjectName on = new ObjectName("a:b=c");
 231 
 232         Thing thing = new Thing();
 233         mbs.registerMBean(thing, on);
 234         check(mbs, on);
 235         mbs.unregisterMBean(on);
 236 
 237         ThingImpl thingImpl = new ThingImpl();
 238         mbs.registerMBean(thingImpl, on);
 239         Descriptor d = mbs.getMBeanInfo(on).getDescriptor();
 240         if (!d.getFieldValue("mxbean").equals("true")) {
 241             System.out.println("NOT OK: expected MXBean");
 242             failed = "Expected MXBean";
 243         }
 244         check(mbs, on);
 245 
 246         if (failed == null)
 247             System.out.println("Test passed");
 248         else
 249             throw new Exception("TEST FAILED: " + failed);
 250     }
 251 
 252     private static void check(MBeanServer mbs, ObjectName on) throws Exception {
 253         MBeanInfo mbi = mbs.getMBeanInfo(on);
 254 
 255         // check the MBean itself
 256         check(mbi);
 257 
 258         // check attributes
 259         MBeanAttributeInfo[] attrs = mbi.getAttributes();
 260         for (MBeanAttributeInfo attr : attrs) {
 261             check(attr);
 262             if (attr.getName().equals("ReadOnly"))
 263                 check("@Full", attr.getDescriptor(), expectedFullDescriptor);
 264         }
 265 
 266         // check operations
 267         MBeanOperationInfo[] ops = mbi.getOperations();
 268         for (MBeanOperationInfo op : ops) {
 269             check(op);
 270             check(op.getSignature());
 271         }
 272 
 273         MBeanConstructorInfo[] constrs = mbi.getConstructors();
 274         for (MBeanConstructorInfo constr : constrs) {
 275             check(constr);
 276             check(constr.getSignature());
 277         }
 278     }
 279 
 280     private static void check(DescriptorRead x) {
 281         check(x, x.getDescriptor(), expectedDescriptor);
 282     }
 283 
 284     private static void check(Object x, Descriptor d, Descriptor expect) {
 285         String fail = null;
 286         try {
 287             Descriptor u = ImmutableDescriptor.union(d, expect);
 288             if (!u.equals(d))
 289                 fail = "should contain " + expect + "; is " + d;
 290         } catch (IllegalArgumentException e) {
 291             fail = e.getMessage();
 292         }
 293         if (fail == null) {
 294             System.out.println("OK: " + x);
 295         } else {
 296             failed = "NOT OK: Incorrect descriptor for: " + x;
 297             System.out.println(failed);
 298             System.out.println("..." + fail);
 299         }
 300     }
 301 
 302     private static void check(DescriptorRead[] xx) {
 303         for (DescriptorRead x : xx)
 304             check(x);
 305     }
 306 }