View Javadoc
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.32 $ $Date: 2003/10/14 01:27:21 $ 36 */ 37 public class MBeanBuilder 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