110 RetentionPolicy[] enumArrayValue(); |
109 RetentionPolicy[] enumArrayValue(); |
111 @DescriptorKey("booleanArray") |
110 @DescriptorKey("booleanArray") |
112 boolean[] booleanArrayValue(); |
111 boolean[] booleanArrayValue(); |
113 } |
112 } |
114 |
113 |
115 /* We use the annotation @Pair(x = 3, y = "foo") everywhere, and this is |
114 /* We use the annotations @Pair(x = 3, y = "foo") |
116 the Descriptor that it should produce: */ |
115 and @DescriptorFields({"foo=bar", "baz="}) everywhere, and this is |
|
116 the Descriptor that they should produce: */ |
117 private static Descriptor expectedDescriptor = |
117 private static Descriptor expectedDescriptor = |
118 new ImmutableDescriptor(new String[] {"x", "y"}, |
118 new ImmutableDescriptor(new String[] {"x", "y", "foo", "baz"}, |
119 new Object[] {3, "foo"}); |
119 new Object[] {3, "foo", "bar", ""}); |
120 |
120 |
121 private static Descriptor expectedFullDescriptor = |
121 private static Descriptor expectedFullDescriptor = |
122 new ImmutableDescriptor(new String[] { |
122 new ImmutableDescriptor(new String[] { |
123 "class", "enum", "boolean", "stringArray", |
123 "class", "enum", "boolean", "stringArray", |
124 "classArray", "intArray", "enumArray", |
124 "classArray", "intArray", "enumArray", |
134 new String[] {RetentionPolicy.RUNTIME.name()}, |
134 new String[] {RetentionPolicy.RUNTIME.name()}, |
135 new boolean[] {false, true}, |
135 new boolean[] {false, true}, |
136 }); |
136 }); |
137 |
137 |
138 @Pair(x = 3, y = "foo") |
138 @Pair(x = 3, y = "foo") |
|
139 @DescriptorFields({"foo=bar", "baz="}) |
139 public static interface ThingMBean { |
140 public static interface ThingMBean { |
140 @Pair(x = 3, y = "foo") |
141 @Pair(x = 3, y = "foo") |
|
142 @DescriptorFields({"foo=bar", "baz="}) |
141 @Full(classValue=Full.class, |
143 @Full(classValue=Full.class, |
142 enumValue=RetentionPolicy.RUNTIME, |
144 enumValue=RetentionPolicy.RUNTIME, |
143 booleanValue=false, |
145 booleanValue=false, |
144 stringArrayValue={"foo", "bar"}, |
146 stringArrayValue={"foo", "bar"}, |
145 classArrayValue={Full.class}, |
147 classArrayValue={Full.class}, |
147 enumArrayValue={RetentionPolicy.RUNTIME}, |
149 enumArrayValue={RetentionPolicy.RUNTIME}, |
148 booleanArrayValue={false, true}) |
150 booleanArrayValue={false, true}) |
149 int getReadOnly(); |
151 int getReadOnly(); |
150 |
152 |
151 @Pair(x = 3, y = "foo") |
153 @Pair(x = 3, y = "foo") |
|
154 @DescriptorFields({"foo=bar", "baz="}) |
152 void setWriteOnly(int x); |
155 void setWriteOnly(int x); |
153 |
156 |
154 @Pair(x = 3, y = "foo") |
157 @Pair(x = 3, y = "foo") |
|
158 @DescriptorFields({"foo=bar", "baz="}) |
155 int getReadWrite1(); |
159 int getReadWrite1(); |
156 void setReadWrite1(int x); |
160 void setReadWrite1(int x); |
157 |
161 |
158 @Pair(x = 3, y = "foo") |
162 @Pair(x = 3, y = "foo") |
|
163 @DescriptorFields({"foo=bar", "baz="}) |
159 int getReadWrite2(); |
164 int getReadWrite2(); |
160 @Pair(x = 3, y = "foo") |
165 @Pair(x = 3, y = "foo") |
|
166 @DescriptorFields({"foo=bar", "baz="}) |
161 void setReadWrite2(int x); |
167 void setReadWrite2(int x); |
162 |
168 |
163 int getReadWrite3(); |
169 int getReadWrite3(); |
164 @Pair(x = 3, y = "foo") |
170 @Pair(x = 3, y = "foo") |
|
171 @DescriptorFields({"foo=bar", "baz="}) |
165 void setReadWrite3(int x); |
172 void setReadWrite3(int x); |
166 |
173 |
167 @Pair(x = 3, y = "foo") |
174 @Pair(x = 3, y = "foo") |
168 int operation(@Pair(x = 3, y = "foo") int p1, |
175 @DescriptorFields({"foo=bar", "baz="}) |
169 @Pair(x = 3, y = "foo") int p2); |
176 int operation(@Pair(x = 3, y = "foo") |
|
177 @DescriptorFields({"foo=bar", "baz="}) |
|
178 int p1, |
|
179 @Pair(x = 3, y = "foo") |
|
180 @DescriptorFields({"foo=bar", "baz="}) |
|
181 int p2); |
170 } |
182 } |
171 |
183 |
172 public static class Thing implements ThingMBean { |
184 public static class Thing implements ThingMBean { |
173 @Pair(x = 3, y = "foo") |
185 @Pair(x = 3, y = "foo") |
|
186 @DescriptorFields({"foo=bar", "baz="}) |
174 public Thing() {} |
187 public Thing() {} |
175 |
188 |
176 @Pair(x = 3, y = "foo") |
189 @Pair(x = 3, y = "foo") |
177 public Thing(@Pair(x = 3, y = "foo") int p1) {} |
190 @DescriptorFields({"foo=bar", "baz="}) |
|
191 public Thing( |
|
192 @Pair(x = 3, y = "foo") |
|
193 @DescriptorFields({"foo=bar", "baz="}) |
|
194 int p1) {} |
178 |
195 |
179 public int getReadOnly() {return 0;} |
196 public int getReadOnly() {return 0;} |
180 |
197 |
181 public void setWriteOnly(int x) {} |
198 public void setWriteOnly(int x) {} |
182 |
199 |
191 |
208 |
192 public int operation(int p1, int p2) {return 0;} |
209 public int operation(int p1, int p2) {return 0;} |
193 } |
210 } |
194 |
211 |
195 @Pair(x = 3, y = "foo") |
212 @Pair(x = 3, y = "foo") |
|
213 @DescriptorFields({"foo=bar", "baz="}) |
196 public static interface ThingMXBean extends ThingMBean {} |
214 public static interface ThingMXBean extends ThingMBean {} |
197 |
215 |
198 public static class ThingImpl implements ThingMXBean { |
216 public static class ThingImpl implements ThingMXBean { |
199 @Pair(x = 3, y = "foo") |
217 @Pair(x = 3, y = "foo") |
|
218 @DescriptorFields({"foo=bar", "baz="}) |
200 public ThingImpl() {} |
219 public ThingImpl() {} |
201 |
220 |
202 @Pair(x = 3, y = "foo") |
221 @Pair(x = 3, y = "foo") |
203 public ThingImpl(@Pair(x = 3, y = "foo") int p1) {} |
222 @DescriptorFields({"foo=bar", "baz="}) |
|
223 public ThingImpl( |
|
224 @Pair(x = 3, y = "foo") |
|
225 @DescriptorFields({"foo=bar", "baz="}) |
|
226 int p1) {} |
204 |
227 |
205 public int getReadOnly() {return 0;} |
228 public int getReadOnly() {return 0;} |
206 |
229 |
207 public void setWriteOnly(int x) {} |
230 public void setWriteOnly(int x) {} |
208 |
231 |
214 |
237 |
215 public int getReadWrite3() {return 0;} |
238 public int getReadWrite3() {return 0;} |
216 public void setReadWrite3(int x) {} |
239 public void setReadWrite3(int x) {} |
217 |
240 |
218 public int operation(int p1, int p2) {return 0;} |
241 public int operation(int p1, int p2) {return 0;} |
|
242 } |
|
243 |
|
244 @Retention(RetentionPolicy.RUNTIME) |
|
245 public static @interface DefaultTest { |
|
246 @DescriptorKey(value = "string1", omitIfDefault = true) |
|
247 String string1() default ""; |
|
248 @DescriptorKey(value = "string2", omitIfDefault = true) |
|
249 String string2() default "tiddly pom"; |
|
250 @DescriptorKey(value = "int", omitIfDefault = true) |
|
251 int intx() default 23; |
|
252 @DescriptorKey(value = "intarray1", omitIfDefault = true) |
|
253 int[] intArray1() default {}; |
|
254 @DescriptorKey(value = "intarray2", omitIfDefault = true) |
|
255 int[] intArray2() default {1, 2}; |
|
256 @DescriptorKey(value = "stringarray1", omitIfDefault = true) |
|
257 String[] stringArray1() default {}; |
|
258 @DescriptorKey(value = "stringarray2", omitIfDefault = true) |
|
259 String[] stringArray2() default {"foo", "bar"}; |
|
260 } |
|
261 |
|
262 @Retention(RetentionPolicy.RUNTIME) |
|
263 public static @interface Expect { |
|
264 String[] value() default {}; |
|
265 } |
|
266 |
|
267 public static interface DefaultMBean { |
|
268 @DefaultTest |
|
269 @Expect() |
|
270 public void a(); |
|
271 |
|
272 @DefaultTest(string1="") |
|
273 @Expect() |
|
274 public void b(); |
|
275 |
|
276 @DefaultTest(string1="nondefault") |
|
277 @Expect("string1=nondefault") |
|
278 public void c(); |
|
279 |
|
280 @DefaultTest(string2="tiddly pom") |
|
281 @Expect() |
|
282 public void d(); |
|
283 |
|
284 @DefaultTest(intx=23) |
|
285 @Expect() |
|
286 public void e(); |
|
287 |
|
288 @DefaultTest(intx=34) |
|
289 @Expect("int=34") |
|
290 public void f(); |
|
291 |
|
292 @DefaultTest(intArray1={}) |
|
293 @Expect() |
|
294 public void g(); |
|
295 |
|
296 @DefaultTest(intArray1={2,3}) |
|
297 @Expect("intarray1=[2, 3]") |
|
298 public void h(); |
|
299 |
|
300 @DefaultTest(intArray2={}) |
|
301 @Expect("intarray2=[]") |
|
302 public void i(); |
|
303 |
|
304 @DefaultTest(stringArray1={}) |
|
305 @Expect() |
|
306 public void j(); |
|
307 |
|
308 @DefaultTest(stringArray1={"foo"}) |
|
309 @Expect("stringarray1=[foo]") |
|
310 public void k(); |
|
311 |
|
312 @DefaultTest(stringArray2={}) |
|
313 @Expect("stringarray2=[]") |
|
314 public void l(); |
219 } |
315 } |
220 |
316 |
221 public static void main(String[] args) throws Exception { |
317 public static void main(String[] args) throws Exception { |
222 System.out.println("Testing that annotations are correctly " + |
318 System.out.println("Testing that annotations are correctly " + |
223 "reflected in Descriptor entries"); |
319 "reflected in Descriptor entries"); |
224 |
320 |
225 MBeanServer mbs = |
321 MBeanServer mbs = |
226 java.lang.management.ManagementFactory.getPlatformMBeanServer(); |
322 java.lang.management.ManagementFactory.getPlatformMBeanServer(); |
227 ObjectName on = new ObjectName("a:b=c"); |
323 ObjectName on = new ObjectName("a:b=c"); |
|
324 |
228 Thing thing = new Thing(); |
325 Thing thing = new Thing(); |
229 mbs.registerMBean(thing, on); |
326 mbs.registerMBean(thing, on); |
230 check(mbs, on); |
327 check(mbs, on); |
231 mbs.unregisterMBean(on); |
328 mbs.unregisterMBean(on); |
|
329 |
232 ThingImpl thingImpl = new ThingImpl(); |
330 ThingImpl thingImpl = new ThingImpl(); |
233 mbs.registerMBean(thingImpl, on); |
331 mbs.registerMBean(thingImpl, on); |
|
332 Descriptor d = mbs.getMBeanInfo(on).getDescriptor(); |
|
333 if (!d.getFieldValue("mxbean").equals("true")) { |
|
334 System.out.println("NOT OK: expected MXBean"); |
|
335 failed = "Expected MXBean"; |
|
336 } |
234 check(mbs, on); |
337 check(mbs, on); |
|
338 |
|
339 System.out.println(); |
|
340 System.out.println("Testing that omitIfDefault works"); |
|
341 DefaultMBean defaultImpl = (DefaultMBean) Proxy.newProxyInstance( |
|
342 DefaultMBean.class.getClassLoader(), |
|
343 new Class<?>[] {DefaultMBean.class}, |
|
344 new InvocationHandler(){ |
|
345 public Object invoke(Object proxy, Method method, Object[] args) { |
|
346 return null; |
|
347 } |
|
348 }); |
|
349 DynamicMBean mbean = new StandardMBean(defaultImpl, DefaultMBean.class); |
|
350 MBeanOperationInfo[] ops = mbean.getMBeanInfo().getOperations(); |
|
351 for (MBeanOperationInfo op : ops) { |
|
352 String name = op.getName(); |
|
353 Expect expect = |
|
354 DefaultMBean.class.getMethod(name).getAnnotation(Expect.class); |
|
355 Descriptor opd = op.getDescriptor(); |
|
356 List<String> fields = new ArrayList<String>(); |
|
357 for (String fieldName : opd.getFieldNames()) { |
|
358 Object value = opd.getFieldValue(fieldName); |
|
359 String s = Arrays.deepToString(new Object[] {value}); |
|
360 s = s.substring(1, s.length() - 1); |
|
361 fields.add(fieldName + "=" + s); |
|
362 } |
|
363 Descriptor opds = new ImmutableDescriptor(fields.toArray(new String[0])); |
|
364 Descriptor expd = new ImmutableDescriptor(expect.value()); |
|
365 if (opds.equals(expd)) |
|
366 System.out.println("OK: op " + name + ": " + opds); |
|
367 else { |
|
368 String failure = "Bad descriptor for op " + name + ": " + |
|
369 "expected " + expd + ", got " + opds; |
|
370 System.out.println("NOT OK: " + failure); |
|
371 failed = failure; |
|
372 } |
|
373 } |
|
374 System.out.println(); |
235 |
375 |
236 if (failed == null) |
376 if (failed == null) |
237 System.out.println("Test passed"); |
377 System.out.println("Test passed"); |
238 else if (true) |
378 else |
239 throw new Exception("TEST FAILED: " + failed); |
379 throw new Exception("TEST FAILED: " + failed); |
240 else |
|
241 System.out.println("Test disabled until 6221321 implemented"); |
|
242 } |
380 } |
243 |
381 |
244 private static void check(MBeanServer mbs, ObjectName on) throws Exception { |
382 private static void check(MBeanServer mbs, ObjectName on) throws Exception { |
245 MBeanInfo mbi = mbs.getMBeanInfo(on); |
383 MBeanInfo mbi = mbs.getMBeanInfo(on); |
246 |
384 |
293 |
431 |
294 private static void check(DescriptorRead[] xx) { |
432 private static void check(DescriptorRead[] xx) { |
295 for (DescriptorRead x : xx) |
433 for (DescriptorRead x : xx) |
296 check(x); |
434 check(x); |
297 } |
435 } |
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 } |
436 } |