1 /*
2 * Copyright (C) The Spice Group. All rights reserved.
3 *
4 * This software is published under the terms of the Spice
5 * Software License version 1.1, a copy of which has been included
6 * with this distribution in the LICENSE.txt file.
7 */
8 package org.realityforge.metaclass.jmx;
9
10 import java.beans.BeanInfo;
11 import java.beans.Introspector;
12 import java.beans.MethodDescriptor;
13 import java.beans.PropertyDescriptor;
14 import java.lang.reflect.Constructor;
15 import java.lang.reflect.Method;
16 import java.util.ArrayList;
17 import java.util.List;
18 import javax.management.Descriptor;
19 import javax.management.MBeanParameterInfo;
20 import javax.management.modelmbean.ModelMBeanAttributeInfo;
21 import javax.management.modelmbean.ModelMBeanConstructorInfo;
22 import javax.management.modelmbean.ModelMBeanInfo;
23 import javax.management.modelmbean.ModelMBeanOperationInfo;
24 import org.realityforge.metaclass.Attributes;
25 import org.realityforge.metaclass.introspector.MetaClassException;
26 import org.realityforge.metaclass.model.Attribute;
27 import org.realityforge.metaclass.model.ParameterDescriptor;
28
29 /***
30 * Utility class to create ModelMBeanInfo objects from classes
31 * annotated with MetaClass attributes. See documentation for
32 * description of valid attributes.
33 *
34 * @author <a href="mailto:peter at realityforge.org">Peter Donald</a>
35 * @version $Revision: 1.1 $ $Date: 2003/10/15 02:10:19 $
36 */
37 public class MBeanInfoBuilder
38 {
39 /***
40 * Constant for class annotation tag.
41 */
42 private static final String MX_COMPONENT_CONSTANT = "mx.component";
43
44 /***
45 * Constant for class annotation tag that indicates
46 * specific interface is management interface.
47 */
48 private static final String MX_INTERFACE_CONSTANT = "mx.interface";
49
50 /***
51 * Constant for constructor annotation tag.
52 */
53 private static final String MX_CONSTRUCTOR_CONSTANT = "mx.constructor";
54
55 /***
56 * Constant for method annotation tag.
57 */
58 private static final String MX_OPERATION_CONSTANT = "mx.operation";
59
60 /***
61 * Constant for getter/setter annotation tag.
62 */
63 private static final String MX_ATTRIBUTE_CONSTANT = "mx.attribute";
64
65 /***
66 * Constant for annotation of ctor or method parameters.
67 */
68 private static final String MX_PARAMETER_CONSTANT = "mx.parameter";
69
70 /***
71 * Constant for parameter name holding description.
72 */
73 private static final String DESCRIPTION_KEY_CONSTANT = "description";
74
75 /***
76 * Constant for parameter name holding method impact.
77 */
78 private static final String IMPACT_KEY_CONSTANT = "impact";
79
80 /***
81 * Constant for parameter name holding name of method/ctor parameters.
82 */
83 private static final String NAME_KEY_CONSTANT = "name";
84
85 /***
86 * Constant for empty string to avoid gratuitous creation.
87 */
88 private static final String EMPTY_STRING = "";
89
90 /***
91 * Constant string for INFO impact.
92 */
93 private static final String IMPACT_INFO = "INFO";
94
95 /***
96 * Constant string for ACTION impact.
97 */
98 private static final String IMPACT_ACTION = "ACTION";
99
100 /***
101 * Constant string for ACTION_INFO impact.
102 */
103 private static final String IMPACT_ACTION_INFO = "ACTION_INFO";
104
105 /***
106 * Create a set of ModelMBeanInfo objects for specified class
107 * if class is annotated.
108 *
109 * @param type the class
110 * @return the ModelMBeanInfo objects
111 * @throws Exception if unable to get resolve specified types
112 */
113 public ModelMBeanInfo[] buildMBeanInfos( final Class type )
114 throws Exception
115 {
116 final List infos = new ArrayList();
117 buildMBeanInfos( type, infos );
118 return (ModelMBeanInfo[])infos.
119 toArray( new ModelMBeanInfo[ infos.size() ] );
120 }
121
122 /***
123 * Create a set of ModelMBeanInfo objects for specified class.
124 *
125 * @param type the class
126 * @param infos the ModelMBeanInfo objects collected so far
127 * @throws Exception if unable to get resolve specified types
128 */
129 void buildMBeanInfos( final Class type,
130 final List infos )
131 throws Exception
132 {
133 final Attribute attribute =
134 Attributes.getAttribute( type, MX_COMPONENT_CONSTANT );
135 if( null != attribute )
136 {
137 final ModelMBeanInfo info = buildMBeanInfo( type );
138 infos.add( info );
139 }
140
141 final Attribute[] attributes =
142 Attributes.getAttributes( type, MX_INTERFACE_CONSTANT );
143 final ClassLoader classLoader = type.getClassLoader();
144 for( int i = 0; i < attributes.length; i++ )
145 {
146 final Attribute ifcAttribute = attributes[ i ];
147 final String classname = ifcAttribute.getParameter( "type", null );
148 if( null != classname )
149 {
150 final Class clazz = classLoader.loadClass( classname );
151 final ModelMBeanInfo info = buildMBeanInfo( clazz );
152 infos.add( info );
153 }
154 }
155 }
156
157 /***
158 * Build a ModelMBeanInfo object for specific class.
159 *
160 * @param type the class
161 * @return the ModelMBeanInfo
162 * @throws Exception if unable to introspect class
163 */
164 ModelMBeanInfo buildMBeanInfo( final Class type )
165 throws Exception
166 {
167 final String description = getTypeDescription( type );
168
169 final ModelInfoCreationHelper helper = new ModelInfoCreationHelper();
170 helper.setClassname( type.getName() );
171 helper.setDescription( description );
172
173 final BeanInfo beanInfo = Introspector.getBeanInfo( type );
174
175 final Constructor[] constructors = type.getConstructors();
176 final PropertyDescriptor[] propertys = beanInfo.getPropertyDescriptors();
177 final MethodDescriptor[] methods = beanInfo.getMethodDescriptors();
178
179 extractConstructors( constructors, helper );
180 extractAttributes( propertys, helper );
181 extractOperations( methods, helper );
182
183 return helper.toModelMBeanInfo();
184 }
185
186 /***
187 * Return the description of class as specified in "description"
188 * parameter of mx.component attribute.
189 *
190 * @param type the type
191 * @return the description of "" if not specified
192 */
193 String getTypeDescription( final Class type )
194 {
195 final Attribute desc =
196 Attributes.getAttribute( type, MX_COMPONENT_CONSTANT );
197 String description = EMPTY_STRING;
198 if( null != desc )
199 {
200 description = desc.getParameter( DESCRIPTION_KEY_CONSTANT, EMPTY_STRING );
201 }
202 return description;
203 }
204
205 /***
206 * Extract a set of Ctor info objects from specified ctors and
207 * add to helper.
208 *
209 * @param constructors the constructors
210 * @param helper the helper
211 */
212 void extractConstructors( final Constructor[] constructors,
213 final ModelInfoCreationHelper helper )
214 {
215 for( int i = 0; i < constructors.length; i++ )
216 {
217 final ModelMBeanConstructorInfo info =
218 extractConstructor( constructors[ i ] );
219 if( null != info )
220 {
221 helper.addConstructor( info );
222 }
223 }
224 }
225
226 /***
227 * Extract info for constructor.
228 *
229 * @param constructor the constructor
230 * @return the info or null if unmanaged
231 */
232 ModelMBeanConstructorInfo extractConstructor( final Constructor constructor )
233 {
234 final Attribute attribute =
235 Attributes.getAttribute( constructor, MX_CONSTRUCTOR_CONSTANT );
236 if( null == attribute )
237 {
238 return null;
239 }
240 final String description =
241 attribute.getParameter( DESCRIPTION_KEY_CONSTANT, EMPTY_STRING );
242
243 final MBeanParameterInfo[] infos = parseParameterInfos( constructor );
244
245 String name = constructor.getName();
246 final int index = name.lastIndexOf( "." );
247 if( -1 != index )
248 {
249 name = name.substring( index + 1 );
250 }
251
252 final ModelMBeanConstructorInfo info =
253 new ModelMBeanConstructorInfo( name,
254 description,
255 infos );
256 //MX4J caches operation results on MBeans, this disables cache
257 final Descriptor descriptor = info.getDescriptor();
258 descriptor.setField( "currencyTimeLimit", new Integer( 0 ) );
259 info.setDescriptor( descriptor );
260
261 return info;
262 }
263
264 /***
265 * Extract a set of attribute info objects from specified propertys
266 * and add to specified hepler.
267 *
268 * @param propertys the peroptys
269 * @param helper the helper
270 */
271 void extractAttributes( final PropertyDescriptor[] propertys,
272 final ModelInfoCreationHelper helper )
273 {
274 for( int i = 0; i < propertys.length; i++ )
275 {
276 final ModelMBeanAttributeInfo info =
277 extractAttribute( propertys[ i ] );
278 if( null != info )
279 {
280 helper.addAttribute( info );
281 }
282 }
283 }
284
285 /***
286 * Extract an attribute info for specified property if attribute marked
287 * as an attribute.
288 *
289 * @param property the property
290 * @return the info or null if property is not marked as an attribute
291 */
292 ModelMBeanAttributeInfo extractAttribute( final PropertyDescriptor property )
293 {
294 Method readMethod = property.getReadMethod();
295 Method writeMethod = property.getWriteMethod();
296 String description = null;
297
298 //If attributes dont have attribute markup then
299 //dont allow user to read/write property
300 if( null != readMethod )
301 {
302 final Attribute attribute =
303 Attributes.getAttribute( readMethod, MX_ATTRIBUTE_CONSTANT );
304 if( null == attribute )
305 {
306 readMethod = null;
307 }
308 else
309 {
310 description =
311 attribute.getParameter( DESCRIPTION_KEY_CONSTANT, null );
312 }
313 }
314
315 if( null != writeMethod )
316 {
317 final Attribute attribute =
318 Attributes.getAttribute( writeMethod, MX_ATTRIBUTE_CONSTANT );
319 if( null == attribute )
320 {
321 writeMethod = null;
322 }
323 else if( null == description )
324 {
325 description =
326 attribute.getParameter( DESCRIPTION_KEY_CONSTANT, null );
327 }
328 }
329
330 final boolean isReadable = null != readMethod;
331 final boolean isIs = isReadable && readMethod.getName().startsWith( "is" );
332 final boolean isWritable = null != writeMethod;
333
334 if( !isReadable && !isWritable )
335 {
336 return null;
337 }
338
339 if( null == description )
340 {
341 description = EMPTY_STRING;
342 }
343
344 final String name = property.getName();
345 final ModelMBeanAttributeInfo info =
346 new ModelMBeanAttributeInfo( name,
347 property.getPropertyType().getName(),
348 description,
349 isReadable,
350 isWritable,
351 isIs );
352 //MX4J caches operation results on MBeans, this disables cache
353 final Descriptor descriptor = info.getDescriptor();
354 descriptor.setField( "currencyTimeLimit", new Integer( 1 ) );
355 if( isReadable )
356 {
357 descriptor.setField( "getMethod", readMethod.getName() );
358 }
359 if( isWritable )
360 {
361 descriptor.setField( "setMethod", writeMethod.getName() );
362 }
363 info.setDescriptor( descriptor );
364 return info;
365 }
366
367 /***
368 * Extract a list of operations for specified method descriptors.
369 *
370 * @param methods the method descriptors
371 * @param helper the helper to add operations to
372 */
373 void extractOperations( final MethodDescriptor[] methods,
374 final ModelInfoCreationHelper helper )
375 {
376 for( int i = 0; i < methods.length; i++ )
377 {
378 final ModelMBeanOperationInfo info = extractOperation( methods[ i ] );
379 if( null != info )
380 {
381 helper.addOperation( info );
382 }
383 }
384 }
385
386 /***
387 * Extract Operation Info if method is marked as an operation.
388 *
389 * @param method the method
390 * @return the info object or null if not an operation
391 */
392 ModelMBeanOperationInfo extractOperation( final MethodDescriptor method )
393 {
394 final Attribute attribute =
395 Attributes.getAttribute( method.getMethod(), MX_OPERATION_CONSTANT );
396 if( null == attribute )
397 {
398 return null;
399 }
400 final String description =
401 attribute.getParameter( DESCRIPTION_KEY_CONSTANT, EMPTY_STRING );
402 final String impact =
403 attribute.getParameter( IMPACT_KEY_CONSTANT, EMPTY_STRING );
404 final int impactCode = parseImpact( impact );
405
406 final MBeanParameterInfo[] infos = parseParameterInfos( method.getMethod() );
407
408 final String returnType = method.getMethod().getReturnType().getName();
409 final ModelMBeanOperationInfo info =
410 new ModelMBeanOperationInfo( method.getName(),
411 description,
412 infos,
413 returnType,
414 impactCode );
415 //MX4J caches operation results on MBeans, this disables cache
416 final Descriptor descriptor = info.getDescriptor();
417 descriptor.setField( "currencyTimeLimit", new Integer( 0 ) );
418 info.setDescriptor( descriptor );
419
420 return info;
421 }
422
423 /***
424 * Extract the parameter infos for specified constructor.
425 *
426 * @param constructor the constructor
427 * @return the infos
428 */
429 MBeanParameterInfo[] parseParameterInfos( final Constructor constructor )
430 {
431 try
432 {
433 final Attribute[] attributes =
434 Attributes.getConstructor( constructor ).getAttributes();
435 final ParameterDescriptor[] parameters =
436 Attributes.getConstructor( constructor ).getParameters();
437 return buildParametersFromMetaData( attributes, parameters );
438 }
439 catch( final MetaClassException mce )
440 {
441 return buildParametersViaReflection( constructor.getParameterTypes() );
442 }
443 }
444
445 /***
446 * Extract the parameter infos for specified method.
447 *
448 * @param method the method
449 * @return the infos
450 */
451 MBeanParameterInfo[] parseParameterInfos( final Method method )
452 {
453 try
454 {
455 final Attribute[] attributes =
456 Attributes.getMethod( method ).getAttributes();
457 final ParameterDescriptor[] parameters =
458 Attributes.getMethod( method ).getParameters();
459 return buildParametersFromMetaData( attributes, parameters );
460 }
461 catch( final MetaClassException mce )
462 {
463 return buildParametersViaReflection( method.getParameterTypes() );
464 }
465 }
466
467 /***
468 * Build a set of parameter info objects via specified metadata.
469 *
470 * @param attributes the attributes
471 * @param parameters the parameters
472 * @return the parameter infos
473 */
474 MBeanParameterInfo[] buildParametersFromMetaData( final Attribute[] attributes,
475 final ParameterDescriptor[] parameters )
476 {
477 final MBeanParameterInfo[] infos = new MBeanParameterInfo[ parameters.length ];
478 for( int i = 0; i < infos.length; i++ )
479 {
480 final ParameterDescriptor parameter = parameters[ i ];
481 final String name = parameter.getName();
482 final String type = parameter.getType();
483 final String description = parseParameterDescription( attributes, name );
484 infos[ i ] = new MBeanParameterInfo( name, type, description );
485 }
486 return infos;
487 }
488
489 /***
490 * Build a set of parameter info objects via reflection.
491 *
492 * @param types the types of parameters
493 * @return the parameter infos
494 */
495 MBeanParameterInfo[] buildParametersViaReflection( final Class[] types )
496 {
497 final MBeanParameterInfo[] infos = new MBeanParameterInfo[ types.length ];
498 for( int i = 0; i < types.length; i++ )
499 {
500 infos[ i ] =
501 new MBeanParameterInfo( EMPTY_STRING,
502 types[ i ].getName(),
503 EMPTY_STRING );
504 }
505 return infos;
506 }
507
508 /***
509 * Extract parameter desciption for specified parameter
510 * from specified attributes. If the attributes have a mx.parameter
511 * specified for that attribute then use the description parameter
512 * of attribute otherwise return an empty string.
513 *
514 * @param attributes the attributes
515 * @param name the name of parameter
516 * @return the parameter description
517 */
518 String parseParameterDescription( final Attribute[] attributes,
519 final String name )
520 {
521 final Attribute[] params =
522 Attributes.getAttributesByName( attributes, MX_PARAMETER_CONSTANT );
523 for( int i = 0; i < params.length; i++ )
524 {
525 final Attribute paramAttribute = params[ i ];
526 final String key = paramAttribute.getParameter( NAME_KEY_CONSTANT );
527 if( name.equals( key ) )
528 {
529 return paramAttribute.getParameter( DESCRIPTION_KEY_CONSTANT, EMPTY_STRING );
530 }
531 }
532 return EMPTY_STRING;
533 }
534
535 /***
536 * Parse Impact enum. Should be one of the IMPACT_*
537 * constants otherwise impact will be set to UNKNOWN.
538 *
539 * @param impact the impact string
540 * @return the impact code
541 */
542 int parseImpact( final String impact )
543 {
544 if( IMPACT_INFO.equals( impact ) )
545 {
546 return ModelMBeanOperationInfo.INFO;
547 }
548 else if( IMPACT_ACTION.equals( impact ) )
549 {
550 return ModelMBeanOperationInfo.ACTION;
551 }
552 else if( IMPACT_ACTION_INFO.equals( impact ) )
553 {
554 return ModelMBeanOperationInfo.ACTION_INFO;
555 }
556 else
557 {
558 return ModelMBeanOperationInfo.UNKNOWN;
559 }
560 }
561 }
This page was automatically generated by Maven