| 1 | /** |
| 2 | * Class <code>ModelMBeanIntegrator<code>. |
| 3 | */ |
| 4 | package org.jsesoft.mmbi; |
| 5 | |
| 6 | import java.lang.annotation.Annotation; |
| 7 | import java.lang.reflect.Constructor; |
| 8 | import java.lang.reflect.Field; |
| 9 | import java.lang.reflect.InvocationTargetException; |
| 10 | import java.lang.reflect.Method; |
| 11 | import java.lang.reflect.Modifier; |
| 12 | import java.beans.*; |
| 13 | import java.util.Vector; |
| 14 | import javax.management.Descriptor; |
| 15 | import javax.management.MBeanException; |
| 16 | import javax.management.MBeanOperationInfo; |
| 17 | import javax.management.MBeanParameterInfo; |
| 18 | import javax.management.MBeanServer; |
| 19 | import javax.management.ObjectName; |
| 20 | import javax.management.modelmbean.DescriptorSupport; |
| 21 | import javax.management.modelmbean.ModelMBeanAttributeInfo; |
| 22 | import javax.management.modelmbean.ModelMBeanConstructorInfo; |
| 23 | import javax.management.modelmbean.ModelMBeanInfo; |
| 24 | import javax.management.modelmbean.ModelMBeanInfoSupport; |
| 25 | import javax.management.modelmbean.ModelMBeanOperationInfo; |
| 26 | import javax.management.modelmbean.XMLParseException; |
| 27 | import javax.management.modelmbean.ModelMBeanNotificationInfo; |
| 28 | |
| 29 | import org.jsesoft.ri.InspectorSupport; |
| 30 | import org.jsesoft.ri.ReflectionInspector; |
| 31 | |
| 32 | /** |
| 33 | * Instruments (an registers) an arbitrary object as Model MBean. |
| 34 | * |
| 35 | * <p> |
| 36 | * This class provides JMX Model MBean instrumentation and registration |
| 37 | * for objects of arbitrary classes. It is implemented as a strategy for the |
| 38 | * {@link org.jsesoft.ri.ReflectionInspector <code>ReflectionInspector</code>} |
| 39 | * class. |
| 40 | * </p> |
| 41 | * <p> |
| 42 | * <b>Example:</b> |
| 43 | * <code> |
| 44 | * <pre> |
| 45 | * ModelMBeanInstrumentor instrumentor = new ModelMBeanInstrumentor(); |
| 46 | * SampleResource resource = new SampleResource(); |
| 47 | * NamedModelMBean mbean = instrumentor.instrument( resource, "sampleResource" ); |
| 48 | * System.out.println( "Stop JConsole " + mbean.getName().getCanonicalName() ); |
| 49 | * MBeanServer server = |
| 50 | * java.lang.management.ManagementFactory.getPlatformMBeanServer(); |
| 51 | * int count = 300; |
| 52 | * while( ! resource.isStopped() ) { |
| 53 | * Thread.sleep( 100 ); |
| 54 | * count--; |
| 55 | * if( (count <= 0) && (! resource.isControlled()) ) { |
| 56 | * resource.setStopped( true ); |
| 57 | * } |
| 58 | * } |
| 59 | * server.unregisterMBean( mbean.getName() ); |
| 60 | * </pre> |
| 61 | * </code> |
| 62 | * </p> |
| 63 | * |
| 64 | * @author JSESoft |
| 65 | * @version $Revision: 1.6 $ |
| 66 | */ |
| 67 | public class ModelMBeanInstrumentor |
| 68 | extends InspectorSupport |
| 69 | { |
| 70 | /** The mbean's descriptor. */ |
| 71 | private Descriptor mbeanDescriptor; |
| 72 | /** The mbean's ModelMBeanInfo. */ |
| 73 | private ModelMBeanInfo mbeanInfo; |
| 74 | /** The actually insepted classes JavaBean BeanInfo */ |
| 75 | BeanInfo beanInfo; |
| 76 | /** The mbean's construcotr infos. */ |
| 77 | private Vector<ModelMBeanConstructorInfo> constructorInfos; |
| 78 | /** The mbean's operation infos. */ |
| 79 | private Vector<ModelMBeanOperationInfo> operationInfos; |
| 80 | /** The mbean's attribuye infos. */ |
| 81 | private Vector<ModelMBeanAttributeInfo> attributeInfos; |
| 82 | /** The mbean's attribuye infos. */ |
| 83 | private Vector<ModelMBeanNotificationInfo> notificationInfos; |
| 84 | /** A type specifier for Vector.toArray() */ |
| 85 | private static final ModelMBeanConstructorInfo[] constructors |
| 86 | = new ModelMBeanConstructorInfo[0]; |
| 87 | /** A type specifier for Vector.toArray() */ |
| 88 | private static final ModelMBeanOperationInfo[] operations |
| 89 | = new ModelMBeanOperationInfo[0]; |
| 90 | /** A type specifier for Vector.toArray() */ |
| 91 | private static final ModelMBeanAttributeInfo[] attributes |
| 92 | = new ModelMBeanAttributeInfo[0]; |
| 93 | /** A type specifier for Vector.toArray() */ |
| 94 | private static final ModelMBeanNotificationInfo[] notifications |
| 95 | = new ModelMBeanNotificationInfo[0]; |
| 96 | /** The class where to stop reflection recursion. */ |
| 97 | private Class sentinel; |
| 98 | /** Whether or not to produce debug info. */ |
| 99 | private static final boolean debug = true; |
| 100 | /** The mbean itself. */ |
| 101 | private NamedModelMBean mbean = null; |
| 102 | /** The inspector uesed. */ |
| 103 | private ReflectionInspector inspector; |
| 104 | |
| 105 | /** |
| 106 | * Constructs an instance with <code>Object.class</code> |
| 107 | * as sentinel. |
| 108 | * |
| 109 | * @see #ModelMBeanInstrumentor(Class) |
| 110 | */ |
| 111 | public ModelMBeanInstrumentor() |
| 112 | { |
| 113 | this( Object.class ); |
| 114 | } |
| 115 | |
| 116 | /** |
| 117 | * Constructs an instance with the specified class as sentinel. |
| 118 | * |
| 119 | * <p> |
| 120 | * Traversal of the reflection tree is recusrsive with respect |
| 121 | * to inheritance. The <b>sentinel</b> specifies where recursion stops. |
| 122 | * Default is <code>Object.class</code> (see |
| 123 | * {@link #ModelMBeanInstrumentor() non-parameter constructor}). |
| 124 | * </p> |
| 125 | * @param sentinel Class where recursive traversal stops |
| 126 | */ |
| 127 | public ModelMBeanInstrumentor( Class sentinel ) |
| 128 | { |
| 129 | super(); |
| 130 | this.sentinel = sentinel; |
| 131 | } |
| 132 | |
| 133 | /** |
| 134 | * Creates a <code>ModelMBean</code> for a managed resource. |
| 135 | * |
| 136 | * @param managedResource the resource to be managed as Model MBean |
| 137 | * @return the created NamedModelMBean |
| 138 | * @throws Exception if instrumentation fails |
| 139 | */ |
| 140 | public NamedModelMBean instrument( Object managedResource ) |
| 141 | throws Exception |
| 142 | { |
| 143 | return instrument( managedResource, null ); |
| 144 | } |
| 145 | |
| 146 | /** |
| 147 | * Creates a <code>ModelMBean</code> for a managed resource |
| 148 | * and registers it as JMX MBean. |
| 149 | * |
| 150 | * <p> |
| 151 | * This function is the workhorse of this class. It |
| 152 | * <ol> |
| 153 | * <li> |
| 154 | * It creates a {@link NamedModelMBean <code>NamedModelMBean</code>} |
| 155 | * instance. |
| 156 | * </li> |
| 157 | * <li> |
| 158 | * It internally creates an |
| 159 | * {@link org.jsesoft.ri.ReflectionInspector ReflectionInspector} for |
| 160 | * traversing the reflection tree. |
| 161 | * </li> |
| 162 | * <li> |
| 163 | * It inspects the managed resource. |
| 164 | * </li> |
| 165 | * <li> |
| 166 | * if the <code>nickName</code> parameter is null, thats it. Otherqwise |
| 167 | * </li> |
| 168 | * <li> |
| 169 | * It creates an object name like this |
| 170 | * <code> |
| 171 | * <pre> |
| 172 | * classnameOfManagedResource:id=nickName-hashCode |
| 173 | * </pre> |
| 174 | * </code> |
| 175 | * </li> |
| 176 | * <li> |
| 177 | * registers the MBean at the platform MBean server |
| 178 | * </li> |
| 179 | * <li> |
| 180 | * sets the instance of the mbean |
| 181 | * </li> |
| 182 | * </ol> |
| 183 | * </p> |
| 184 | * |
| 185 | * @param managedResource the resource to be managed as Model MBean |
| 186 | * @param nickName part of the object name (if null then don't register) |
| 187 | * @return the created NamedModelMBean |
| 188 | * @throws Exception if instrumentation fails |
| 189 | */ |
| 190 | public NamedModelMBean instrument( Object managedResource, String nickName ) |
| 191 | throws Exception |
| 192 | { |
| 193 | mbeanInfo = null; |
| 194 | attributeInfos = null; |
| 195 | constructorInfos = null; |
| 196 | operationInfos = null; |
| 197 | notificationInfos = null; |
| 198 | mbean = new NamedModelMBean(); |
| 199 | mbean.setManagedResource( managedResource, "ObjectReference" ); |
| 200 | inspector = new ReflectionInspector(); |
| 201 | beanInfo = |
| 202 | Introspector.getBeanInfo( managedResource.getClass(), sentinel ); |
| 203 | inspector.setInspectee( managedResource.getClass() ); |
| 204 | inspector.setStrategy( this ); |
| 205 | boolean reply = inspector.inspect(); |
| 206 | assert reply:"inspection failed"; |
| 207 | if( nickName == null ) { |
| 208 | return mbean; |
| 209 | } |
| 210 | StringBuilder name = |
| 211 | new StringBuilder( managedResource.getClass().getName() ); |
| 212 | name.append( ":id=" ).append( nickName ).append( "-" ).append( managedResource. |
| 213 | hashCode() ); |
| 214 | ObjectName objectName = new ObjectName( name.toString() ); |
| 215 | MBeanServer server = java.lang.management.ManagementFactory. |
| 216 | getPlatformMBeanServer(); |
| 217 | mbean.setInstance( server.registerMBean( mbean, objectName ) ); |
| 218 | return mbean; |
| 219 | } |
| 220 | |
| 221 | /** |
| 222 | * This override sets the mbean's MBeanInfo after inspection. |
| 223 | * |
| 224 | * <p> |
| 225 | * <b>Note:</b> This function is not intended to be called |
| 226 | * by customers. Instead, it is called by the inspector. |
| 227 | * </p> |
| 228 | * @param inspected the inspected class |
| 229 | * @throws Exception if inspection fails |
| 230 | */ |
| 231 | @Override public void postInspect( Class inspected ) |
| 232 | throws Exception |
| 233 | { |
| 234 | mbean.setModelMBeanInfo( getMBeanInfo() ); |
| 235 | } |
| 236 | |
| 237 | /** |
| 238 | * This override creates a JMX descriptor for the inspected class. |
| 239 | * |
| 240 | * <p> |
| 241 | * <b>Note:</b> This function is not intended to be called |
| 242 | * by customers. Instead, it is called by the inspector. |
| 243 | * </p> |
| 244 | * @param inspected the inspected class |
| 245 | * @throws Exception if inspection fails |
| 246 | * @return true if inspection complete |
| 247 | */ |
| 248 | @Override public boolean inspect( Class inspected ) |
| 249 | throws Exception |
| 250 | { |
| 251 | if( inspected.equals( sentinel ) ) { |
| 252 | return true; |
| 253 | } |
| 254 | BeanInfo oldBeanInfo = beanInfo; |
| 255 | beanInfo = Introspector.getBeanInfo( inspected, sentinel ); |
| 256 | handleJMXNotificationAnnotations( inspected.getAnnotations() ); |
| 257 | JMX annotation = |
| 258 | ( JMX )inspected.getAnnotation( org.jsesoft.mmbi.JMX.class ); |
| 259 | |
| 260 | Descriptor descriptor = |
| 261 | getDescriptor( |
| 262 | annotation, |
| 263 | beanInfo.getBeanDescriptor(), |
| 264 | inspected.getName(), |
| 265 | inspected.getSimpleName(), |
| 266 | inspected.getCanonicalName(), |
| 267 | "MBean" ); |
| 268 | mbeanDescriptor = descriptor; |
| 269 | if( debug ) { |
| 270 | System.out.println( ( ( DescriptorSupport )descriptor ).toXMLString() ); |
| 271 | } |
| 272 | beanInfo = oldBeanInfo; |
| 273 | return false; |
| 274 | } |
| 275 | |
| 276 | /** |
| 277 | * This override creates a constructor info for the inspected constructor. |
| 278 | * |
| 279 | * <p> |
| 280 | * <b>Note:</b> This function is not intended to be called |
| 281 | * by customers. Instead, it is called by the inspector. |
| 282 | * </p> |
| 283 | * @param inspected the inspected class |
| 284 | * @param constructor the inspected constructor |
| 285 | * @throws Exception if inspection fails |
| 286 | * @return true if inspection complete |
| 287 | */ |
| 288 | @Override |
| 289 | public boolean inspectConstructor( Class inspected, Constructor constructor ) |
| 290 | throws Exception |
| 291 | { |
| 292 | if( !Modifier.isPublic( constructor.getModifiers() ) ) { |
| 293 | return false; |
| 294 | } |
| 295 | handleJMXNotificationAnnotations( constructor.getAnnotations() ); |
| 296 | JMX annotation = |
| 297 | constructor.getAnnotation( org.jsesoft.mmbi.JMX.class ); |
| 298 | if( ( annotation != null ) |
| 299 | && ( annotation.hide() ) ) { |
| 300 | return false; |
| 301 | } |
| 302 | MBeanParameterInfo[] parameterInfos = |
| 303 | getParameterInfos( |
| 304 | constructor.getParameterTypes(), |
| 305 | constructor.getParameterAnnotations(), |
| 306 | null ); |
| 307 | String name = constructor.getName(); |
| 308 | Descriptor descriptor = |
| 309 | getDescriptor( |
| 310 | annotation, |
| 311 | null, |
| 312 | name, |
| 313 | name, |
| 314 | name, |
| 315 | "operation" ); |
| 316 | descriptor.setField( "role", "constructor" ); |
| 317 | if( constructorInfos == null ) { |
| 318 | constructorInfos = new Vector<ModelMBeanConstructorInfo>(); |
| 319 | } |
| 320 | constructorInfos.add( |
| 321 | new ModelMBeanConstructorInfo( |
| 322 | ( String )descriptor.getFieldValue( "name" ), |
| 323 | ( String )descriptor.getFieldValue( "description" ), |
| 324 | parameterInfos, |
| 325 | descriptor ) ); |
| 326 | if( debug ) { |
| 327 | System.out.println( ( ( DescriptorSupport )descriptor ).toXMLString() ); |
| 328 | } |
| 329 | return false; |
| 330 | } |
| 331 | |
| 332 | /** |
| 333 | * This override creates a operation info for the inspected method. |
| 334 | * |
| 335 | * <p> |
| 336 | * <b>Note:</b> This function is not intended to be called |
| 337 | * by customers. Instead, it is called by the inspector. |
| 338 | * </p> |
| 339 | * @param inspected the inspected class |
| 340 | * @param operation the inspected method |
| 341 | * @throws Exception if inspection fails |
| 342 | * @return true if inspection complete |
| 343 | */ |
| 344 | @Override public boolean inspectMethod( Class inspected, Method operation ) |
| 345 | throws Exception |
| 346 | { |
| 347 | if( !Modifier.isPublic( operation.getModifiers() ) ) { |
| 348 | return false; |
| 349 | } |
| 350 | handleJMXNotificationAnnotations( operation.getAnnotations() ); |
| 351 | JMX annotation = |
| 352 | operation.getAnnotation( org.jsesoft.mmbi.JMX.class ); |
| 353 | if( ( annotation != null ) |
| 354 | && ( annotation.hide() ) ) { |
| 355 | return false; |
| 356 | } |
| 357 | MethodDescriptor methodDescriptor = |
| 358 | getMethodDescriptor( |
| 359 | beanInfo.getMethodDescriptors(), |
| 360 | operation ); |
| 361 | ParameterDescriptor[] parameterDescriptors = |
| 362 | ( methodDescriptor == null ) ? null |
| 363 | : methodDescriptor.getParameterDescriptors(); |
| 364 | MBeanParameterInfo[] parameterInfos = |
| 365 | getParameterInfos( |
| 366 | operation.getParameterTypes(), |
| 367 | operation.getParameterAnnotations(), |
| 368 | parameterDescriptors ); |
| 369 | String name = operation.getName(); |
| 370 | Descriptor descriptor = |
| 371 | getDescriptor( |
| 372 | annotation, |
| 373 | methodDescriptor, |
| 374 | name, |
| 375 | name, |
| 376 | name, |
| 377 | "operation" ); |
| 378 | descriptor.setField( "class", inspected.getName() ); |
| 379 | descriptor.setField( "displayName", descriptor.getFieldValue( "name" ) ); |
| 380 | if( ( operation.getName().startsWith( "get" ) |
| 381 | && ( operation.getParameterTypes().length == 0 ) |
| 382 | && ( operation.getReturnType() != null ) ) |
| 383 | || ( operation.getName().startsWith( "is" ) |
| 384 | && ( operation.getParameterTypes().length == 0 ) |
| 385 | && boolean.class.equals( operation.getReturnType() ) ) ) { |
| 386 | descriptor.setField( "role", "getter" ); |
| 387 | } else |
| 388 | if( operation.getName().startsWith( "set" ) |
| 389 | && ( void.class.equals( operation.getReturnType() ) ) |
| 390 | && ( operation.getParameterTypes().length == 1 ) ) { |
| 391 | descriptor.setField( "role", "setter" ); |
| 392 | } else { |
| 393 | descriptor.setField( "role", "operation" ); |
| 394 | } |
| 395 | int impact = |
| 396 | ( annotation == null ) ? MBeanOperationInfo.ACTION_INFO : annotation.impact(); |
| 397 | if( impact == MBeanOperationInfo.UNKNOWN ) { |
| 398 | impact = MBeanOperationInfo.ACTION_INFO; |
| 399 | } |
| 400 | if( operationInfos == null ) { |
| 401 | operationInfos = new Vector<ModelMBeanOperationInfo>(); |
| 402 | } |
| 403 | operationInfos.add( |
| 404 | new ModelMBeanOperationInfo( |
| 405 | ( String )descriptor.getFieldValue( "name" ), |
| 406 | ( String )descriptor.getFieldValue( "description" ), |
| 407 | parameterInfos, |
| 408 | operation.getReturnType().getName(), |
| 409 | impact, |
| 410 | descriptor ) ); |
| 411 | if( debug ) { |
| 412 | System.out.println( ( ( DescriptorSupport )descriptor ).toXMLString() ); |
| 413 | } |
| 414 | |
| 415 | return false; |
| 416 | } |
| 417 | |
| 418 | private MBeanParameterInfo[] getParameterInfos( |
| 419 | Class<? >[] types, |
| 420 | Annotation[][] annotations, |
| 421 | ParameterDescriptor[] features ) |
| 422 | throws |
| 423 | NoSuchMethodException, |
| 424 | InvocationTargetException, |
| 425 | IllegalAccessException |
| 426 | { |
| 427 | if( types.length == 0 ) { |
| 428 | return new MBeanParameterInfo[0]; |
| 429 | } |
| 430 | MBeanParameterInfo[] parameterInfos |
| 431 | = new MBeanParameterInfo[types.length]; |
| 432 | for( int iX = 0; iX < types.length; iX++ ) { |
| 433 | Class<? > type = types[iX]; |
| 434 | JMX annotation = getJMXAnnotation( annotations[iX] ); |
| 435 | String name = ( features == null ) ? null : features[iX].getName(); |
| 436 | if( name == null ) { |
| 437 | name = "p" + iX; |
| 438 | } |
| 439 | String description = |
| 440 | ( features == null ) ? null : features[iX].getShortDescription(); |
| 441 | if( description == null ) { |
| 442 | description = ( annotation == null ) ? "" : annotation.description(); |
| 443 | } |
| 444 | if( "".equals( description ) ) { |
| 445 | description = name; |
| 446 | } |
| 447 | parameterInfos[iX] = |
| 448 | new MBeanParameterInfo( name, type.getName(), description ); |
| 449 | } |
| 450 | return parameterInfos; |
| 451 | } |
| 452 | |
| 453 | /** |
| 454 | * Gets the JavaBeans method descriptor for the method. |
| 455 | * |
| 456 | * @param features the BeanInfo MethodDescriptors |
| 457 | * @param method the Method |
| 458 | * @return the MethodDescriptor, null if none |
| 459 | */ |
| 460 | public MethodDescriptor getMethodDescriptor( MethodDescriptor[] features, |
| 461 | Method method ) |
| 462 | { |
| 463 | for( MethodDescriptor feature : features ) { |
| 464 | if( feature.getMethod().equals( method ) ) { |
| 465 | return feature; |
| 466 | } |
| 467 | } |
| 468 | return null; |
| 469 | } |
| 470 | |
| 471 | /** |
| 472 | * This override creates a attribute info for the inspected field. |
| 473 | * |
| 474 | * <p> |
| 475 | * <b>Note:</b> This function is not intended to be called |
| 476 | * by customers. Instead, it is called by the inspector. |
| 477 | * </p> |
| 478 | * @param inspected the inspected class |
| 479 | * @param field the inspected field |
| 480 | * @throws Exception if inspection fails |
| 481 | * @return true if inspection complete |
| 482 | */ |
| 483 | @Override public boolean inspectField( Class inspected, Field field ) |
| 484 | throws Exception |
| 485 | { |
| 486 | JMX annotation = |
| 487 | field.getAnnotation( org.jsesoft.mmbi.JMX.class ); |
| 488 | if( ( annotation != null ) |
| 489 | && ( annotation.hide() ) ) { |
| 490 | return false; |
| 491 | } |
| 492 | PropertyDescriptor propertyDescriptor = |
| 493 | getPropertyDescriptor( beanInfo.getPropertyDescriptors(), field ); |
| 494 | Method getter = null; |
| 495 | if( propertyDescriptor != null ) { |
| 496 | getter = propertyDescriptor.getReadMethod(); |
| 497 | } |
| 498 | if( getter == null ) { |
| 499 | getter = getGetter( inspected, field ); |
| 500 | } |
| 501 | Method setter = null; |
| 502 | if( propertyDescriptor != null ) { |
| 503 | setter = propertyDescriptor.getWriteMethod(); |
| 504 | } |
| 505 | if( setter == null ) { |
| 506 | setter = getSetter( inspected, field ); |
| 507 | } |
| 508 | if( ( setter == null ) && ( getter == null ) ) { |
| 509 | return false; |
| 510 | } |
| 511 | String name = field.getName(); |
| 512 | Descriptor descriptor = |
| 513 | getDescriptor( |
| 514 | annotation, |
| 515 | propertyDescriptor, |
| 516 | name, |
| 517 | name, |
| 518 | name, |
| 519 | "attribute" ); |
| 520 | if( getter != null ) { |
| 521 | descriptor.setField( "getMethod", getter.getName() ); |
| 522 | } |
| 523 | if( setter != null ) { |
| 524 | descriptor.setField( "setMethod", setter.getName() ); |
| 525 | } |
| 526 | if( attributeInfos == null ) { |
| 527 | attributeInfos = new Vector<ModelMBeanAttributeInfo>(); |
| 528 | } |
| 529 | attributeInfos.add( |
| 530 | new ModelMBeanAttributeInfo( |
| 531 | ( String )descriptor.getFieldValue( "name" ), |
| 532 | ( String )descriptor.getFieldValue( "description" ), |
| 533 | getter, |
| 534 | setter, |
| 535 | descriptor ) ); |
| 536 | if( debug ) { |
| 537 | System.out.println( ( ( DescriptorSupport )descriptor ).toXMLString() ); |
| 538 | } |
| 539 | return false; |
| 540 | } |
| 541 | |
| 542 | /** |
| 543 | * Gets the BeanInfe PropertyDescriptor for the field. |
| 544 | * |
| 545 | * @param features the BeanInfo PropertyDescriptors |
| 546 | * @param field the Field |
| 547 | * @return the PropertyDescriptor, null if none |
| 548 | */ |
| 549 | public PropertyDescriptor getPropertyDescriptor( |
| 550 | PropertyDescriptor[] features, |
| 551 | Field field ) |
| 552 | { |
| 553 | for( PropertyDescriptor feature : features ) { |
| 554 | if( feature.getName().equals( field.getName() ) ) { |
| 555 | return feature; |
| 556 | } |
| 557 | } |
| 558 | return null; |
| 559 | } |
| 560 | |
| 561 | /** |
| 562 | * Inquires the getter method for a field. |
| 563 | * |
| 564 | * @param inspected the inspected Class |
| 565 | * @param field the inspected Field |
| 566 | * @return getter, null if none defined |
| 567 | */ |
| 568 | public static Method getGetter( Class inspected, Field field ) |
| 569 | { |
| 570 | String name = field.getName(); |
| 571 | String upper = |
| 572 | Character.toUpperCase( name.charAt( 0 ) ) + name.substring( 1 ); |
| 573 | Method methods[] = inspected.getDeclaredMethods(); |
| 574 | for( Method method : methods ) { |
| 575 | if( !Modifier.isPublic( method.getModifiers() ) ) { |
| 576 | continue; |
| 577 | } |
| 578 | if( boolean.class.equals( field.getType() ) |
| 579 | && boolean.class.equals( method.getReturnType() ) |
| 580 | && ( method.getParameterTypes().length == 0 ) |
| 581 | && ( "is" + upper ).equals( method.getName() ) ) { |
| 582 | return method; |
| 583 | } |
| 584 | if( field.getType().equals( method.getReturnType() ) |
| 585 | && ( method.getParameterTypes().length == 0 ) |
| 586 | && ( "get" + upper ).equals( method.getName() ) ) { |
| 587 | return method; |
| 588 | } |
| 589 | } |
| 590 | return null; |
| 591 | } |
| 592 | |
| 593 | /** |
| 594 | * Inquires the setter method for a field. |
| 595 | * |
| 596 | * @param inspected the inspected Class |
| 597 | * @param field the inspected Field |
| 598 | * @return setter, null if none defined |
| 599 | */ |
| 600 | public static Method getSetter( Class inspected, Field field ) |
| 601 | { |
| 602 | String name = field.getName(); |
| 603 | String upper = |
| 604 | Character.toUpperCase( name.charAt( 0 ) ) + name.substring( 1 ); |
| 605 | Method methods[] = inspected.getDeclaredMethods(); |
| 606 | for( Method method : methods ) { |
| 607 | if( !Modifier.isPublic( method.getModifiers() ) ) { |
| 608 | continue; |
| 609 | } |
| 610 | Class[] types = method.getParameterTypes(); |
| 611 | if( types.length != 1 ) { |
| 612 | continue; |
| 613 | } |
| 614 | if( field.getType().equals( types[0] ) |
| 615 | && ( "set" + upper ).equals( method.getName() ) ) { |
| 616 | return method; |
| 617 | } |
| 618 | } |
| 619 | return null; |
| 620 | } |
| 621 | |
| 622 | /** |
| 623 | * Inquires the JMX annotation. |
| 624 | * |
| 625 | * @param annotations the annotations to be looked up for the JMX annotation |
| 626 | * @return JMX |
| 627 | */ |
| 628 | public static JMX getJMXAnnotation( Annotation[] annotations ) |
| 629 | { |
| 630 | for( Annotation annotation : annotations ) { |
| 631 | if( annotation.annotationType().isAssignableFrom( |
| 632 | org.jsesoft.mmbi.JMX.class ) ) { |
| 633 | return( JMX )annotation; |
| 634 | } |
| 635 | } |
| 636 | return null; |
| 637 | } |
| 638 | |
| 639 | /** |
| 640 | * Handles the JMXNotification annotations. |
| 641 | * |
| 642 | * @param annotations the annotations to be looked up for the JMX annotation |
| 643 | * @throws Exception if descriptor cannot be created |
| 644 | */ |
| 645 | public void handleJMXNotificationAnnotations( Annotation[] annotations ) |
| 646 | throws Exception |
| 647 | { |
| 648 | for( Annotation annotation : annotations ) { |
| 649 | if( annotation.annotationType().isAssignableFrom( |
| 650 | org.jsesoft.mmbi.JMXNotification.class ) ) { |
| 651 | JMXNotification notification = ( JMXNotification )annotation; |
| 652 | String[] types = notification.types(); |
| 653 | Descriptor descriptor; |
| 654 | descriptor = new DescriptorSupport(); |
| 655 | descriptor.setField( "name", notification.name() ); |
| 656 | descriptor.setField( "displayName", notification.displayName() ); |
| 657 | descriptor.setField( "description", notification.description() ); |
| 658 | descriptor.setField( "severity", notification.severity() ); |
| 659 | descriptor.setField( "descriptorType", "notification" ); |
| 660 | if( notificationInfos == null ) { |
| 661 | notificationInfos = new Vector<ModelMBeanNotificationInfo>(); |
| 662 | } |
| 663 | notificationInfos.add( |
| 664 | new ModelMBeanNotificationInfo( |
| 665 | notification.types(), |
| 666 | notification.name(), |
| 667 | notification.description(), |
| 668 | descriptor ) ); |
| 669 | if( debug ) { |
| 670 | System.out.println( ( ( DescriptorSupport )descriptor ).toXMLString() ); |
| 671 | } |
| 672 | } |
| 673 | } |
| 674 | } |
| 675 | |
| 676 | /** |
| 677 | * Creates a JMX descriptor from the specified information. |
| 678 | * |
| 679 | * <p> |
| 680 | * Default information can be overruled by annotation information |
| 681 | * </p> |
| 682 | * <p> |
| 683 | * <b>Note:</b> Though this was designed as a helper function, |
| 684 | * it might be useful for customers, too. |
| 685 | * </p> |
| 686 | * |
| 687 | * @param annotation the JMX annotation containing overriding info |
| 688 | * @param feature the Javabeans feature descriptor containing overriding info |
| 689 | * @param defaultName the default name |
| 690 | * @param defaultDisplayName the default display name |
| 691 | * @param defaultDescription the default description |
| 692 | * @param descriptorType the type of the descriptor to create |
| 693 | * @return the created descriptor |
| 694 | * @throws MBeanException if descriptor cannot be created |
| 695 | * @throws XMLParseException if the annotation's XML is not valid |
| 696 | */ |
| 697 | public static Descriptor getDescriptor( |
| 698 | JMX annotation, |
| 699 | FeatureDescriptor feature, |
| 700 | String defaultName, |
| 701 | String defaultDisplayName, |
| 702 | String defaultDescription, |
| 703 | String descriptorType ) |
| 704 | throws |
| 705 | MBeanException, |
| 706 | XMLParseException |
| 707 | { |
| 708 | Descriptor descriptor; |
| 709 | if( annotation != null ) { |
| 710 | String xml = annotation.xml(); |
| 711 | if( "".equals( xml ) ) { |
| 712 | descriptor = new DescriptorSupport(); |
| 713 | descriptor.setField( "name", defaultName ); |
| 714 | } else { |
| 715 | descriptor = new DescriptorSupport( xml ); |
| 716 | } |
| 717 | } else { |
| 718 | descriptor = new DescriptorSupport(); |
| 719 | descriptor.setField( "name", defaultName ); |
| 720 | } |
| 721 | descriptor.setField( "descriptorType", descriptorType ); |
| 722 | String name = ( String )descriptor.getFieldValue( "name" ); |
| 723 | if( ( feature != null ) && ( feature.getName() != null ) ) { |
| 724 | name = feature.getName(); |
| 725 | } |
| 726 | if( ( annotation != null ) |
| 727 | && ( !"".equals( annotation.name() ) ) ) { |
| 728 | name = annotation.name(); |
| 729 | } |
| 730 | String displayName = ( String )descriptor.getFieldValue( "displayName" ); |
| 731 | if( ( feature != null ) && ( feature.getDisplayName() != null ) ) { |
| 732 | displayName = feature.getDisplayName(); |
| 733 | } |
| 734 | if( ( annotation != null ) |
| 735 | && ( !"".equals( annotation.displayName() ) ) ) { |
| 736 | displayName = annotation.displayName(); |
| 737 | } |
| 738 | if( ( displayName == null ) || "".equals( displayName ) ) { |
| 739 | displayName = defaultDisplayName; |
| 740 | } |
| 741 | String description = ( String )descriptor.getFieldValue( "description" ); |
| 742 | if( ( feature != null ) && ( feature.getShortDescription() != null ) ) { |
| 743 | description = feature.getShortDescription(); |
| 744 | } |
| 745 | if( ( annotation != null ) |
| 746 | && ( !"".equals( annotation.description() ) ) ) { |
| 747 | description = annotation.description(); |
| 748 | } |
| 749 | if( ( description == null ) || "".equals( description ) ) { |
| 750 | description = defaultDescription; |
| 751 | } |
| 752 | descriptor.setField( "name", name ); |
| 753 | descriptor.setField( "displayName", displayName ); |
| 754 | descriptor.setField( "description", description ); |
| 755 | return descriptor; |
| 756 | } |
| 757 | |
| 758 | /** |
| 759 | * Gets the created constructor infos. |
| 760 | * |
| 761 | * @return ModelMBeanConstructorInfo[] |
| 762 | */ |
| 763 | public ModelMBeanConstructorInfo[] getConstructorInfos() |
| 764 | { |
| 765 | if( constructorInfos == null ) { |
| 766 | return null; |
| 767 | } |
| 768 | return constructorInfos.toArray( constructors ); |
| 769 | } |
| 770 | |
| 771 | /** |
| 772 | * Gets the created operation info. |
| 773 | * |
| 774 | * @return ModelMBeanOperationInfo[] |
| 775 | */ |
| 776 | public ModelMBeanOperationInfo[] getOperationInfos() |
| 777 | { |
| 778 | if( operationInfos == null ) { |
| 779 | return null; |
| 780 | } |
| 781 | return operationInfos.toArray( operations ); |
| 782 | } |
| 783 | |
| 784 | /** |
| 785 | * Gets the created attribute info. |
| 786 | * |
| 787 | * @return ModelMBeanAttributeInfo[] |
| 788 | */ |
| 789 | public ModelMBeanAttributeInfo[] getAttributeInfos() |
| 790 | { |
| 791 | if( attributeInfos == null ) { |
| 792 | return null; |
| 793 | } |
| 794 | return attributeInfos.toArray( attributes ); |
| 795 | } |
| 796 | |
| 797 | /** |
| 798 | * Gets the created attribute info. |
| 799 | * |
| 800 | * @return ModelMBeanAttributeInfo[] |
| 801 | */ |
| 802 | public ModelMBeanNotificationInfo[] getNotificationInfos() |
| 803 | { |
| 804 | if( notificationInfos == null ) { |
| 805 | return null; |
| 806 | } |
| 807 | return notificationInfos.toArray( notifications ); |
| 808 | } |
| 809 | |
| 810 | /** |
| 811 | * Gets the created MBeanInfo. |
| 812 | * |
| 813 | * <p> |
| 814 | * This function creates the MBean Info, if not already done. |
| 815 | * </p> |
| 816 | * @return ModelMBeanInfo |
| 817 | */ |
| 818 | public ModelMBeanInfo getMBeanInfo() |
| 819 | { |
| 820 | if( mbeanInfo != null ) { |
| 821 | return mbeanInfo; |
| 822 | } |
| 823 | return mbeanInfo = |
| 824 | new ModelMBeanInfoSupport( |
| 825 | ( String )mbeanDescriptor.getFieldValue( "name" ), |
| 826 | ( String )mbeanDescriptor.getFieldValue( "description" ), |
| 827 | getAttributeInfos(), |
| 828 | getConstructorInfos(), |
| 829 | getOperationInfos(), |
| 830 | getNotificationInfos(), |
| 831 | mbeanDescriptor ); |
| 832 | } |
| 833 | |
| 834 | /** |
| 835 | * Inquires the inspector used for reflection traversal. |
| 836 | * |
| 837 | * @return the used <code>ReflectionInspector</code> |
| 838 | */ |
| 839 | public ReflectionInspector getInspector() |
| 840 | { |
| 841 | return inspector; |
| 842 | } |
| 843 | } |