| 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 | } |