KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > jmx > mbeanserver > Introspector


1 /*
2  * @(#)Introspector.java 1.68 03/12/19
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package com.sun.jmx.mbeanserver;
9
10
11 // Java import
12
import java.lang.reflect.Constructor JavaDoc;
13 import java.lang.reflect.Method JavaDoc;
14 import java.lang.reflect.Modifier JavaDoc;
15 import java.util.ArrayList JavaDoc;
16 import java.util.List JavaDoc;
17 import java.util.Iterator JavaDoc;
18
19 // RI Import
20
import javax.management.IntrospectionException JavaDoc;
21 import javax.management.MBeanAttributeInfo JavaDoc;
22 import javax.management.MBeanConstructorInfo JavaDoc;
23 import javax.management.MBeanInfo JavaDoc;
24 import javax.management.MBeanOperationInfo JavaDoc;
25 import javax.management.MBeanParameterInfo JavaDoc;
26 import javax.management.NotCompliantMBeanException JavaDoc;
27
28
29 /**
30  * This class contains the methods for performing all the tests needed to verify
31  * that a class represents a JMX compliant MBean.
32  *
33  * @since 1.5
34  */

35 public class Introspector {
36     
37
38     /*
39      * ------------------------------------------
40      * PRIVATE VARIABLES
41      * ------------------------------------------
42      */

43
44     private static final String JavaDoc attributeDescription =
45     "Attribute exposed for management";
46     private static final String JavaDoc operationDescription =
47     "Operation exposed for management";
48     private static final String JavaDoc constructorDescription =
49     "Public constructor of the MBean";
50     private static final String JavaDoc mbeanInfoDescription =
51     "Information on the management interface of the MBean";
52     
53
54      /*
55      * ------------------------------------------
56      * PRIVATE CONSTRUCTORS
57      * ------------------------------------------
58      */

59
60     // private constructor defined to "hide" the default public constructor
61
private Introspector() {
62
63     // ------------------------------
64
// ------------------------------
65

66     }
67     
68     /*
69      * ------------------------------------------
70      * PUBLIC METHODS
71      * ------------------------------------------
72      */

73
74     /**
75      * Tell whether a MBean of the given class is a Dynamic MBean.
76      * This method does nothing more than returning
77      * <pre>
78      * javax.management.DynamicMBean.class.isAssignableFrom(c)
79      * </pre>
80      * This method does not check for any JMX MBean compliance:
81      * <ul><li>If <code>true</code> is returned, then instances of
82      * <code>c</code> are DynamicMBean.</li>
83      * <li>If <code>false</code> is returned, then no further
84      * assumption can be made on instances of <code>c</code>.
85      * In particular, instances of <code>c</code> may, or may not
86      * be JMX standard MBeans.</li>
87      * </ul>
88      * @param c The class of the MBean under examination.
89      * @return <code>true</code> if instances of <code>c</code> are
90      * Dynamic MBeans, <code>false</code> otherwise.
91      *
92      * @since.unbundled JMX RI 1.2
93      **/

94     public static final boolean isDynamic(final Class JavaDoc c) {
95     // Check if the MBean implements the DynamicMBean interface
96
return javax.management.DynamicMBean JavaDoc.class.isAssignableFrom(c);
97     }
98
99     /**
100      * Basic method for testing that a MBean of a given class can be
101      * instantiated by the MBean server.<p>
102      * This method checks that:
103      * <ul><li>The given class is a concrete class.</li>
104      * <li>The given class exposes at least one public constructor.</li>
105      * </ul>
106      * If these conditions are not met, throws a NotCompliantMBeanException.
107      * @param c The class of the MBean we want to create.
108      * @exception NotCompliantMBeanException if the MBean class makes it
109      * impossible to instantiate the MBean from within the
110      * MBeanServer.
111      *
112      * @since.unbundled JMX RI 1.2
113      **/

114     public static void testCreation(Class JavaDoc c)
115     throws NotCompliantMBeanException JavaDoc {
116     // Check if the class is a concrete class
117
final int mods = c.getModifiers();
118     if (Modifier.isAbstract(mods) || Modifier.isInterface(mods)) {
119         throw new NotCompliantMBeanException JavaDoc("MBean class must be concrete");
120     }
121
122     // Check if the MBean has a public constructor
123
final Constructor JavaDoc[] consList = c.getConstructors();
124     if (consList.length == 0) {
125         throw new NotCompliantMBeanException JavaDoc("MBean class must have public constructor");
126     }
127     }
128     
129     /**
130      * Basic method for testing if a given class is a JMX compliant MBean.
131      *
132      * @param baseClass The class to be tested
133      *
134      * @return <code>null</code> if the MBean is a DynamicMBean,
135      * the computed {@link javax.management.MBeanInfo} otherwise.
136      * @exception NotCompliantMBeanException The specified class is not a
137      * JMX compliant MBean
138      */

139     public static MBeanInfo JavaDoc testCompliance(Class JavaDoc baseClass)
140     throws NotCompliantMBeanException JavaDoc {
141
142     // ------------------------------
143
// ------------------------------
144

145     // Check if the MBean implements the MBean or the Dynamic
146
// MBean interface
147
if (isDynamic(baseClass))
148         return null;
149     
150     return testCompliance(baseClass, null);
151     }
152
153     
154     /**
155      * Basic method for testing if a given class is a JMX compliant MBean.
156      *
157      * @param baseClass The class to be tested
158      *
159      * @return <code>null</code> if the MBean is a DynamicMBean,
160      * the computed {@link javax.management.MBeanInfo} otherwise.
161      * @exception NotCompliantMBeanException The specified class is not a
162      * JMX compliant MBean
163      */

164     static MBeanInfo JavaDoc testCompliance(final Class JavaDoc baseClass,
165                     Class JavaDoc mbeanInterface)
166         throws NotCompliantMBeanException JavaDoc {
167     
168     if (baseClass.isInterface())
169         throw new NotCompliantMBeanException JavaDoc(baseClass.getName() +
170                          " must be a class.");
171     // ------------------------------
172
// ------------------------------
173
if (mbeanInterface == null)
174         // No interface specified: look for default MBean interface.
175
mbeanInterface = getStandardMBeanInterface(baseClass);
176     else if (! mbeanInterface.isAssignableFrom(baseClass)) {
177         // specified interface not implemented by given class
178
final String JavaDoc msg =
179         baseClass.getName() + " does not implement the " +
180         mbeanInterface.getName() + " interface";
181         throw new NotCompliantMBeanException JavaDoc(msg);
182     } else if (! mbeanInterface.isInterface()) {
183         // Base class X, but XMBean is not an interface
184
final String JavaDoc msg =
185         baseClass.getName() + ": " + mbeanInterface.getName() +
186         " is not an interface";
187         throw new NotCompliantMBeanException JavaDoc(msg);
188     }
189
190
191     if (mbeanInterface == null) {
192         // Error: MBean does not implement javax.management.DynamicMBean
193
// nor MBean interface
194
final String JavaDoc baseClassName = baseClass.getName();
195         final String JavaDoc msg =
196         baseClassName + " does not implement the " + baseClassName +
197         "MBean interface or the DynamicMBean interface";
198         throw new NotCompliantMBeanException JavaDoc(msg);
199     }
200     
201     final int mods = mbeanInterface.getModifiers();
202     if (!Modifier.isPublic(mods))
203         throw new NotCompliantMBeanException JavaDoc(mbeanInterface.getName() +
204                          " implemented by " +
205                          baseClass.getName() +
206                          " must be public");
207
208     return (introspect(baseClass, mbeanInterface));
209     }
210
211     
212     /**
213      * Get the MBean interface implemented by a JMX standard MBean
214      * class.
215      *
216      * @param baseClass The class to be tested
217      *
218      * @return The MBean interface implemented by the MBean.
219      * Return <code>null</code> if the MBean is a DynamicMBean,
220      * or if no MBean interface is found.
221      *
222      */

223     public static Class JavaDoc getMBeanInterface(Class JavaDoc baseClass) {
224
225     // ------------------------------
226
// ------------------------------
227

228     // Check if the MBean implements the MBean or the Dynamic
229
// MBean interface
230
if (isDynamic(baseClass)) return null;
231
232     return getStandardMBeanInterface(baseClass);
233     }
234     
235     /**
236      * Get the MBean interface implemented by a JMX standard MBean
237      * class.
238      *
239      * @param baseClass The class to be tested
240      *
241      * @return The MBean interface implemented by the MBean.
242      * Return <code>null</code> if no MBean interface is found.
243      * Does not check whether the MBean is a DynamicMBean.
244      *
245      */

246     static Class JavaDoc getStandardMBeanInterface(Class JavaDoc baseClass) {
247
248     // ------------------------------
249
// ------------------------------
250

251     Class JavaDoc current = baseClass;
252     Class JavaDoc mbeanInterface = null;
253     
254     while (current != null) {
255         mbeanInterface =
256         findMBeanInterface(current, current.getName());
257         if (mbeanInterface != null) break;
258         current = current.getSuperclass();
259     }
260     return mbeanInterface;
261     }
262
263     /*
264      * ------------------------------------------
265      * PRIVATE METHODS
266      * ------------------------------------------
267      */

268     
269
270     /**
271      * Try to find the MBean interface corresponding to the class aName
272      * - i.e. <i>aName</i>MBean, from within aClass and its superclasses.
273      **/

274     private static Class JavaDoc findMBeanInterface(Class JavaDoc aClass, String JavaDoc aName) {
275     Class JavaDoc current = aClass;
276     while (current != null) {
277         final Class JavaDoc[] interfaces = current.getInterfaces();
278         final int len = interfaces.length;
279         for (int i=0;i<len;i++) {
280         final Class JavaDoc inter =
281             implementsMBean(interfaces[i], aName);
282         if (inter != null) return inter;
283         }
284         current = current.getSuperclass();
285     }
286     return null;
287     }
288
289
290     /**
291      * Discovers the getters, setters, operations of the class
292      *
293      * @param baseClass The XX base class.
294      * @param beanClass The XXMBean interface implemented by the tested class.
295      *
296      * @exception NotCompliantMBeanException The tested class is not a
297      * JMX compliant MBean
298      */

299     private static MBeanInfo JavaDoc introspect(Class JavaDoc baseClass, Class JavaDoc beanClass)
300         throws NotCompliantMBeanException JavaDoc {
301
302     // ------------------------------
303
// ------------------------------
304

305     List JavaDoc/*<MBeanAttributeInfo>*/ attributes =
306         new ArrayList JavaDoc/*<MBeanAttributeInfo>*/();
307     List JavaDoc/*<MBeanOperationInfo>*/ operations =
308         new ArrayList JavaDoc/*<MBeanOperationInfo>*/();
309     
310     Method JavaDoc methodList[] = beanClass.getMethods();
311     
312     // Now analyze each method.
313
for (int i = 0; i < methodList.length; i++) {
314         Method JavaDoc method = methodList[i];
315         String JavaDoc name = method.getName();
316         Class JavaDoc argTypes[] = method.getParameterTypes();
317         Class JavaDoc resultType = method.getReturnType();
318         int argCount = argTypes.length;
319
320         try {
321         final MBeanAttributeInfo JavaDoc attr;
322
323         if (name.startsWith("get") && !name.equals("get")
324             && argCount == 0 && !resultType.equals(void.class)) {
325             // if the method is "T getX()" it is a getter
326
attr = new MBeanAttributeInfo JavaDoc(name.substring(3),
327                           attributeDescription,
328                           method, null);
329         } else if (name.startsWith("set") && !name.equals("set")
330                && argCount == 1 && resultType.equals(void.class)) {
331             // if the method is "void setX(T x)" it is a setter
332
attr = new MBeanAttributeInfo JavaDoc(name.substring(3),
333                           attributeDescription,
334                           null, method);
335         } else if (name.startsWith("is") && !name.equals("is")
336                && argCount == 0
337                && resultType.equals(boolean.class)) {
338             // if the method is "boolean isX()" it is a getter
339
attr = new MBeanAttributeInfo JavaDoc(name.substring(2),
340                           attributeDescription,
341                           method, null);
342         } else {
343             // in all other cases it is an operation
344
attr = null;
345         }
346
347         if (attr != null) {
348             if (testConsistency(attributes, attr))
349             attributes.add(attr);
350         } else {
351             final MBeanOperationInfo JavaDoc oper =
352             new MBeanOperationInfo JavaDoc(operationDescription, method);
353             operations.add(oper);
354         }
355         } catch (IntrospectionException JavaDoc e) {
356         // Should not happen (MBeanAttributeInfo constructor)
357
error("introspect", e);
358         }
359     }
360
361     return constructResult(baseClass, attributes, operations);
362     }
363
364     /**
365      * Checks if the types and the signatures of
366      * getters/setters/operations are conform to the MBean design
367      * patterns.
368      *
369      * Error cases:
370      * - It exposes a method void Y getXX() AND a method void setXX(Z)
371      * (parameter type mismatch)
372      * - It exposes a method void setXX(Y) AND a method void setXX(Z)
373      * (parameter type mismatch)
374      * - It exposes a boolean isXX() method AND a YY getXX() or a void setXX(Y).
375      * Returns false if the attribute is already in attributes List
376      */

377     private static boolean testConsistency(List JavaDoc/*<MBeanAttributeInfo>*/attributes,
378                        MBeanAttributeInfo JavaDoc attr)
379     throws NotCompliantMBeanException JavaDoc {
380     for (Iterator JavaDoc it = attributes.iterator(); it.hasNext(); ) {
381         MBeanAttributeInfo JavaDoc mb = (MBeanAttributeInfo JavaDoc) it.next();
382         if (mb.getName().equals(attr.getName())) {
383         if ((attr.isReadable() && mb.isReadable()) &&
384             (attr.isIs() != mb.isIs())) {
385             final String JavaDoc msg =
386             "Conflicting getters for attribute " + mb.getName();
387             throw new NotCompliantMBeanException JavaDoc(msg);
388         }
389         if (!mb.getType().equals(attr.getType())) {
390             if (mb.isWritable() && attr.isWritable()) {
391             final String JavaDoc msg =
392                 "Type mismatch between parameters of set" +
393                 mb.getName() + " methods";
394             throw new NotCompliantMBeanException JavaDoc(msg);
395             } else {
396             final String JavaDoc msg =
397                 "Type mismatch between parameters of get or is" +
398                 mb.getName() + ", set" + mb.getName() + " methods";
399             throw new NotCompliantMBeanException JavaDoc(msg);
400             }
401         }
402         if (attr.isReadable() && mb.isReadable()) {
403             return false;
404         }
405         if (attr.isWritable() && mb.isWritable()) {
406             return false;
407         }
408         }
409     }
410     return true;
411     }
412
413     /**
414      * Discovers the constructors of the MBean
415      */

416     static MBeanConstructorInfo JavaDoc[] getConstructors(Class JavaDoc baseClass) {
417     Constructor JavaDoc[] consList = baseClass.getConstructors();
418     List JavaDoc constructors = new ArrayList JavaDoc();
419     
420     // Now analyze each Constructor.
421
for (int i = 0; i < consList.length; i++) {
422         Constructor JavaDoc constructor = consList[i];
423         MBeanConstructorInfo JavaDoc mc = null;
424         try {
425         mc = new MBeanConstructorInfo JavaDoc(constructorDescription, constructor);
426         } catch (Exception JavaDoc ex) {
427         mc = null;
428         }
429         if (mc != null) {
430         constructors.add(mc);
431         }
432     }
433     // Allocate and populate the result array.
434
MBeanConstructorInfo JavaDoc[] resultConstructors =
435         new MBeanConstructorInfo JavaDoc[constructors.size()];
436     constructors.toArray(resultConstructors);
437     return resultConstructors;
438     }
439     
440     /**
441      * Constructs the MBeanInfo of the MBean.
442      */

443     private static MBeanInfo JavaDoc constructResult(Class JavaDoc baseClass,
444                          List JavaDoc/*<MBeanAttributeInfo>*/ attributes,
445                          List JavaDoc/*<MBeanOperationInfo>*/ operations) {
446     
447     final int len = attributes.size();
448     final MBeanAttributeInfo JavaDoc[] attrlist = new MBeanAttributeInfo JavaDoc[len];
449     attributes.toArray(attrlist);
450     final ArrayList JavaDoc mergedAttributes = new ArrayList JavaDoc();
451     
452     for (int i=0;i<len;i++) {
453         final MBeanAttributeInfo JavaDoc bi = attrlist[i];
454         
455         // bi can be null if it has already been eliminated
456
// by the loop below at an earlier iteration
457
// (cf. attrlist[j]=null;) In this case, just skip it.
458
//
459
if (bi == null) continue;
460
461         // Placeholder for the final attribute info we're going to
462
// keep.
463
//
464
MBeanAttributeInfo JavaDoc att = bi;
465
466         // The loop below will try to find whether bi is also present
467
// elsewhere further down the list.
468
// If it is not, att will be left unchanged.
469
// Otherwise, the found attribute info will be merged with
470
// att and `removed' from the array by setting them to `null'
471
//
472
for (int j=i+1;j<len;j++) {
473         MBeanAttributeInfo JavaDoc mi = attrlist[j];
474         
475         // mi can be null if it has already been eliminated
476
// by this loop at an earlier iteration.
477
// (cf. attrlist[j]=null;) In this case, just skip it.
478
//
479
if (mi == null) continue;
480                 if ((mi.getName().compareTo(bi.getName()) == 0)) {
481             // mi and bi have the same name, which means that
482
// that the attribute has been inserted twice in
483
// the list, which means that it is a read-write
484
// attribute.
485
// So we're going to replace att with a new
486
// attribute info with read-write mode.
487
// We also set attrlist[j] to null in order to avoid
488
// duplicates (attrlist[j] and attrlist[i] are now
489
// merged into att).
490
//
491
attrlist[j]=null;
492             att = new MBeanAttributeInfo JavaDoc(bi.getName(),
493                          bi.getType(),
494                          attributeDescription,
495                          true, true, bi.isIs());
496             // I think we could break, but it is probably
497
// safer not to...
498
//
499
// break;
500
}
501         }
502                 
503         // Now all attributes info which had the same name than bi
504
// have been merged together in att.
505
// Simply add att to the merged list.
506
//
507
mergedAttributes.add(att);
508         }
509  
510         final MBeanAttributeInfo JavaDoc[] resultAttributes =
511         new MBeanAttributeInfo JavaDoc[mergedAttributes.size()];
512     mergedAttributes.toArray(resultAttributes);
513
514         final MBeanOperationInfo JavaDoc[] resultOperations =
515         new MBeanOperationInfo JavaDoc[operations.size()];
516     operations.toArray(resultOperations);
517
518     final MBeanConstructorInfo JavaDoc[] resultConstructors =
519         getConstructors(baseClass);
520
521         final MBeanInfo JavaDoc resultMBeanInfo =
522         new MBeanInfo JavaDoc(baseClass.getName(), mbeanInfoDescription,
523               resultAttributes, resultConstructors,
524               resultOperations, null);
525     return resultMBeanInfo;
526     }
527
528     /**
529      * Returns the XXMBean interface or null if no such interface exists
530      *
531      * @param c The interface to be tested
532      * @param clName The name of the class implementing this interface
533      */

534     static Class JavaDoc implementsMBean(Class JavaDoc c, String JavaDoc clName) {
535     if (c.getName().compareTo(clName + "MBean") == 0) {
536         return c;
537     }
538     Class JavaDoc current = c;
539     Class JavaDoc[] interfaces = c.getInterfaces();
540     for (int i = 0;i < interfaces.length; i++) {
541
542         try {
543         if (interfaces[i].getName().compareTo(clName + "MBean") == 0) {
544             return interfaces[i];
545         }
546         } catch (Exception JavaDoc e) {
547         return null;
548         }
549     }
550     
551     return null;
552     }
553
554     private static void error(String JavaDoc method,Throwable JavaDoc t) {
555     com.sun.jmx.trace.Trace.send(com.sun.jmx.trace.Trace.LEVEL_ERROR,
556                      com.sun.jmx.trace.Trace.INFO_MBEANSERVER,
557                      "Introspector",
558                      method,
559                      t);
560                      
561     }
562 }
563
Popular Tags