blob: e6d3c10b0427955beacd21661c2ac09cab12c9ac [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2005 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
20 * CA 95054 USA or visit www.sun.com if you need additional information or
21 * have any 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 * @run clean AnnotationTest
31 * @run build AnnotationTest
32 * @run main AnnotationTest
33 */
34
35import java.lang.annotation.*;
36import java.lang.reflect.*;
37import java.util.*;
38import javax.management.*;
39
40/*
41 This test checks that annotations produce Descriptor entries as
42 specified in javax.management.DescriptorKey. It does two things:
43
44 - An annotation consisting of an int and a String, each with an
45 appropriate @DescriptorKey annotation, is placed on every program
46 element where it can map to a Descriptor, namely:
47
48 . on an MBean interface
49 . on a getter for a read-only attribute
50 . on a setter for a write-only attribute
51 . on the getter but not the setter for a read/write attribute
52 . on the setter but not the getter for a read/write attribute
53 . on both the getter and the setter for a read/write attribute
54 . on an operation
55 . on each parameter of an operation
56 . on a public constructor with no parameters
57 . on a public constructor with a parameter
58 . on the parameter of that public constructor
59 . on all of the above for an MXBean instead of an MBean
60
61 The test checks that in each case the corresponding Descriptor
62 appears in the appropriate place inside the MBean's MBeanInfo.
63
64 - An annotation consisting of enough other types to ensure coverage
65 is placed on a getter. The test checks that the generated
66 MBeanAttributeInfo contains the corresponding Descriptor. The tested
67 types are the following:
68
69 . Class
70 . an enumeration type (java.lang.annotation.RetentionPolicy)
71 . boolean
72 . String[]
73 . Class[]
74 . int[]
75 . an array of enumeration type (RetentionPolicy[])
76 . boolean[]
77 */
78public class AnnotationTest {
79 private static String failed = null;
80
81// @Retention(RetentionPolicy.RUNTIME) @Inherited
82// @Target(ElementType.METHOD)
83// public static @interface DescriptorKey {
84// String value();
85// }
86
87 @Retention(RetentionPolicy.RUNTIME)
88 public static @interface Pair {
89 @DescriptorKey("x")
90 int x();
91 @DescriptorKey("y")
92 String y();
93 }
94
95 @Retention(RetentionPolicy.RUNTIME)
96 public static @interface Full {
97 @DescriptorKey("class")
98 Class classValue();
99 @DescriptorKey("enum")
100 RetentionPolicy enumValue();
101 @DescriptorKey("boolean")
102 boolean booleanValue();
103 @DescriptorKey("stringArray")
104 String[] stringArrayValue();
105 @DescriptorKey("classArray")
106 Class[] classArrayValue();
107 @DescriptorKey("intArray")
108 int[] intArrayValue();
109 @DescriptorKey("enumArray")
110 RetentionPolicy[] enumArrayValue();
111 @DescriptorKey("booleanArray")
112 boolean[] booleanArrayValue();
113 }
114
115 /* We use the annotation @Pair(x = 3, y = "foo") everywhere, and this is
116 the Descriptor that it should produce: */
117 private static Descriptor expectedDescriptor =
118 new ImmutableDescriptor(new String[] {"x", "y"},
119 new Object[] {3, "foo"});
120
121 private static Descriptor expectedFullDescriptor =
122 new ImmutableDescriptor(new String[] {
123 "class", "enum", "boolean", "stringArray",
124 "classArray", "intArray", "enumArray",
125 "booleanArray",
126 },
127 new Object[] {
128 Full.class.getName(),
129 RetentionPolicy.RUNTIME.name(),
130 false,
131 new String[] {"foo", "bar"},
132 new String[] {Full.class.getName()},
133 new int[] {1, 2},
134 new String[] {RetentionPolicy.RUNTIME.name()},
135 new boolean[] {false, true},
136 });
137
138 @Pair(x = 3, y = "foo")
139 public static interface ThingMBean {
140 @Pair(x = 3, y = "foo")
141 @Full(classValue=Full.class,
142 enumValue=RetentionPolicy.RUNTIME,
143 booleanValue=false,
144 stringArrayValue={"foo", "bar"},
145 classArrayValue={Full.class},
146 intArrayValue={1, 2},
147 enumArrayValue={RetentionPolicy.RUNTIME},
148 booleanArrayValue={false, true})
149 int getReadOnly();
150
151 @Pair(x = 3, y = "foo")
152 void setWriteOnly(int x);
153
154 @Pair(x = 3, y = "foo")
155 int getReadWrite1();
156 void setReadWrite1(int x);
157
158 @Pair(x = 3, y = "foo")
159 int getReadWrite2();
160 @Pair(x = 3, y = "foo")
161 void setReadWrite2(int x);
162
163 int getReadWrite3();
164 @Pair(x = 3, y = "foo")
165 void setReadWrite3(int x);
166
167 @Pair(x = 3, y = "foo")
168 int operation(@Pair(x = 3, y = "foo") int p1,
169 @Pair(x = 3, y = "foo") int p2);
170 }
171
172 public static class Thing implements ThingMBean {
173 @Pair(x = 3, y = "foo")
174 public Thing() {}
175
176 @Pair(x = 3, y = "foo")
177 public Thing(@Pair(x = 3, y = "foo") int p1) {}
178
179 public int getReadOnly() {return 0;}
180
181 public void setWriteOnly(int x) {}
182
183 public int getReadWrite1() {return 0;}
184 public void setReadWrite1(int x) {}
185
186 public int getReadWrite2() {return 0;}
187 public void setReadWrite2(int x) {}
188
189 public int getReadWrite3() {return 0;}
190 public void setReadWrite3(int x) {}
191
192 public int operation(int p1, int p2) {return 0;}
193 }
194
195 @Pair(x = 3, y = "foo")
196 public static interface ThingMXBean extends ThingMBean {}
197
198 public static class ThingImpl implements ThingMXBean {
199 @Pair(x = 3, y = "foo")
200 public ThingImpl() {}
201
202 @Pair(x = 3, y = "foo")
203 public ThingImpl(@Pair(x = 3, y = "foo") int p1) {}
204
205 public int getReadOnly() {return 0;}
206
207 public void setWriteOnly(int x) {}
208
209 public int getReadWrite1() {return 0;}
210 public void setReadWrite1(int x) {}
211
212 public int getReadWrite2() {return 0;}
213 public void setReadWrite2(int x) {}
214
215 public int getReadWrite3() {return 0;}
216 public void setReadWrite3(int x) {}
217
218 public int operation(int p1, int p2) {return 0;}
219 }
220
221 public static void main(String[] args) throws Exception {
222 System.out.println("Testing that annotations are correctly " +
223 "reflected in Descriptor entries");
224
225 MBeanServer mbs =
226 java.lang.management.ManagementFactory.getPlatformMBeanServer();
227 ObjectName on = new ObjectName("a:b=c");
228 Thing thing = new Thing();
229 mbs.registerMBean(thing, on);
230 check(mbs, on);
231 mbs.unregisterMBean(on);
232 ThingImpl thingImpl = new ThingImpl();
233 mbs.registerMBean(thingImpl, on);
234 check(mbs, on);
235
236 if (failed == null)
237 System.out.println("Test passed");
238 else if (true)
239 throw new Exception("TEST FAILED: " + failed);
240 else
241 System.out.println("Test disabled until 6221321 implemented");
242 }
243
244 private static void check(MBeanServer mbs, ObjectName on) throws Exception {
245 MBeanInfo mbi = mbs.getMBeanInfo(on);
246
247 // check the MBean itself
248 check(mbi);
249
250 // check attributes
251 MBeanAttributeInfo[] attrs = mbi.getAttributes();
252 for (MBeanAttributeInfo attr : attrs) {
253 check(attr);
254 if (attr.getName().equals("ReadOnly"))
255 check("@Full", attr.getDescriptor(), expectedFullDescriptor);
256 }
257
258 // check operations
259 MBeanOperationInfo[] ops = mbi.getOperations();
260 for (MBeanOperationInfo op : ops) {
261 check(op);
262 check(op.getSignature());
263 }
264
265 MBeanConstructorInfo[] constrs = mbi.getConstructors();
266 for (MBeanConstructorInfo constr : constrs) {
267 check(constr);
268 check(constr.getSignature());
269 }
270 }
271
272 private static void check(DescriptorRead x) {
273 check(x, x.getDescriptor(), expectedDescriptor);
274 }
275
276 private static void check(Object x, Descriptor d, Descriptor expect) {
277 String fail = null;
278 try {
279 Descriptor u = ImmutableDescriptor.union(d, expect);
280 if (!u.equals(d))
281 fail = "should contain " + expect + "; is " + d;
282 } catch (IllegalArgumentException e) {
283 fail = e.getMessage();
284 }
285 if (fail == null) {
286 System.out.println("OK: " + x);
287 } else {
288 failed = "NOT OK: Incorrect descriptor for: " + x;
289 System.out.println(failed);
290 System.out.println("..." + fail);
291 }
292 }
293
294 private static void check(DescriptorRead[] xx) {
295 for (DescriptorRead x : xx)
296 check(x);
297 }
298
299 public static class AnnotatedMBean extends StandardMBean {
300 <T> AnnotatedMBean(T resource, Class<T> interfaceClass, boolean mx) {
301 super(resource, interfaceClass, mx);
302 }
303
304 private static final String[] attrPrefixes = {"get", "set", "is"};
305
306 protected void cacheMBeanInfo(MBeanInfo info) {
307 MBeanAttributeInfo[] attrs = info.getAttributes();
308 MBeanOperationInfo[] ops = info.getOperations();
309
310 for (int i = 0; i < attrs.length; i++) {
311 MBeanAttributeInfo attr = attrs[i];
312 String name = attr.getName();
313 Descriptor d = attr.getDescriptor();
314 Method m;
315 if ((m = getMethod("get" + name)) != null)
316 d = ImmutableDescriptor.union(d, descriptorFor(m));
317 if (attr.getType().equals("boolean") &&
318 (m = getMethod("is" + name)) != null)
319 d = ImmutableDescriptor.union(d, descriptorFor(m));
320 if ((m = getMethod("set" + name, attr)) != null)
321 d = ImmutableDescriptor.union(d, descriptorFor(m));
322 if (!d.equals(attr.getDescriptor())) {
323 attrs[i] =
324 new MBeanAttributeInfo(name, attr.getType(),
325 attr.getDescription(), attr.isReadable(),
326 attr.isWritable(), attr.isIs(), d);
327 }
328 }
329
330 for (int i = 0; i < ops.length; i++) {
331 MBeanOperationInfo op = ops[i];
332 String name = op.getName();
333 Descriptor d = op.getDescriptor();
334 MBeanParameterInfo[] params = op.getSignature();
335 Method m = getMethod(name, params);
336 if (m != null) {
337 d = ImmutableDescriptor.union(d, descriptorFor(m));
338 Annotation[][] annots = m.getParameterAnnotations();
339 for (int pi = 0; pi < params.length; pi++) {
340 MBeanParameterInfo param = params[pi];
341 Descriptor pd =
342 ImmutableDescriptor.union(param.getDescriptor(),
343 descriptorFor(annots[pi]));
344 params[pi] = new MBeanParameterInfo(param.getName(),
345 param.getType(), param.getDescription(), pd);
346 }
347 op = new MBeanOperationInfo(op.getName(),
348 op.getDescription(), params, op.getReturnType(),
349 op.getImpact(), d);
350 if (!ops[i].equals(op))
351 ops[i] = op;
352 }
353 }
354
355 Descriptor id = descriptorFor(getMBeanInterface());
356 info = new MBeanInfo(info.getClassName(), info.getDescription(),
357 attrs, info.getConstructors(), ops, info.getNotifications(),
358 ImmutableDescriptor.union(id, info.getDescriptor()));
359 super.cacheMBeanInfo(info);
360 }
361
362 private Descriptor descriptorFor(AnnotatedElement x) {
363 Annotation[] annots = x.getAnnotations();
364 return descriptorFor(annots);
365 }
366
367 private Descriptor descriptorFor(Annotation[] annots) {
368 if (annots.length == 0)
369 return ImmutableDescriptor.EMPTY_DESCRIPTOR;
370 Map<String, Object> descriptorMap = new HashMap<String, Object>();
371 for (Annotation a : annots) {
372 Class<? extends Annotation> c = a.annotationType();
373 Method[] elements = c.getMethods();
374 for (Method element : elements) {
375 DescriptorKey key =
376 element.getAnnotation(DescriptorKey.class);
377 if (key != null) {
378 String name = key.value();
379 Object value;
380 try {
381 value = element.invoke(a);
382 } catch (Exception e) {
383 // we don't expect this
384 throw new RuntimeException(e);
385 }
386 Object oldValue = descriptorMap.put(name, value);
387 if (oldValue != null && !oldValue.equals(value)) {
388 final String msg =
389 "Inconsistent values for descriptor field " +
390 name + " from annotations: " + value + " :: " +
391 oldValue;
392 throw new IllegalArgumentException(msg);
393 }
394 }
395 }
396 }
397 if (descriptorMap.isEmpty())
398 return ImmutableDescriptor.EMPTY_DESCRIPTOR;
399 else
400 return new ImmutableDescriptor(descriptorMap);
401 }
402
403 private Method getMethod(String name, MBeanFeatureInfo... params) {
404 Class<?> intf = getMBeanInterface();
405 ClassLoader loader = intf.getClassLoader();
406 Class[] classes = new Class[params.length];
407 for (int i = 0; i < params.length; i++) {
408 MBeanFeatureInfo param = params[i];
409 Descriptor d = param.getDescriptor();
410 String type = (String) d.getFieldValue("originalType");
411 if (type == null) {
412 if (param instanceof MBeanAttributeInfo)
413 type = ((MBeanAttributeInfo) param).getType();
414 else
415 type = ((MBeanParameterInfo) param).getType();
416 }
417 Class<?> c = primitives.get(type);
418 if (c == null) {
419 try {
420 c = Class.forName(type, false, loader);
421 } catch (ClassNotFoundException e) {
422 return null;
423 }
424 }
425 classes[i] = c;
426 }
427 try {
428 return intf.getMethod(name, classes);
429 } catch (Exception e) {
430 return null;
431 }
432 }
433
434 private static final Map<String, Class<?>> primitives =
435 new HashMap<String, Class<?>>();
436 static {
437 for (Class<?> c :
438 new Class[] {boolean.class, byte.class, short.class,
439 int.class, long.class, float.class,
440 double.class, char.class, void.class}) {
441 primitives.put(c.getName(), c);
442 }
443 }
444 }
445}