KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * @(#)MBeanIntrospector.java 1.10 06/01/23
3  *
4  * Copyright 2006 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 import static com.sun.jmx.mbeanserver.Util.*;
12
13 import java.lang.ref.WeakReference JavaDoc;
14 import java.lang.reflect.Array JavaDoc;
15 import java.lang.reflect.Constructor JavaDoc;
16 import java.lang.reflect.InvocationTargetException JavaDoc;
17 import java.lang.reflect.Method JavaDoc;
18 import java.lang.reflect.Type JavaDoc;
19 import java.util.List JavaDoc;
20 import java.util.WeakHashMap JavaDoc;
21
22 import javax.management.Descriptor JavaDoc;
23 import javax.management.ImmutableDescriptor JavaDoc;
24 import javax.management.InvalidAttributeValueException JavaDoc;
25 import javax.management.MBeanAttributeInfo JavaDoc;
26 import javax.management.MBeanConstructorInfo JavaDoc;
27 import javax.management.MBeanException JavaDoc;
28 import javax.management.MBeanInfo JavaDoc;
29 import javax.management.MBeanNotificationInfo JavaDoc;
30 import javax.management.MBeanOperationInfo JavaDoc;
31 import javax.management.NotCompliantMBeanException JavaDoc;
32 import javax.management.NotificationBroadcaster JavaDoc;
33 import javax.management.ReflectionException JavaDoc;
34
35 /**
36  * An introspector for MBeans of a certain type. There is one instance
37  * of this class for Standard MBeans and one for MXBeans, characterized
38  * by the two concrete subclasses of this abstract class.
39  *
40  * @param <M> the representation of methods for this kind of MBean:
41  * Method for Standard MBeans, ConvertingMethod for MXBeans.
42  *
43  * @since 1.6
44  */

45 /*
46  * Using a type parameter <M> allows us to deal with the fact that
47  * Method and ConvertingMethod have no useful common ancestor, on
48  * which we could call getName, getGenericReturnType, etc. A simpler approach
49  * would be to wrap every Method in an object that does have a common
50  * ancestor with ConvertingMethod. But that would mean an extra object
51  * for every Method in every Standard MBean interface.
52  */

53 abstract class MBeanIntrospector<M> {
54     static final class PerInterfaceMap<M>
55             extends WeakHashMap JavaDoc<Class JavaDoc<?>, WeakReference JavaDoc<PerInterface<M>>> {}
56     
57     /** The map from interface to PerInterface for this type of MBean. */
58     abstract PerInterfaceMap<M> getPerInterfaceMap();
59     /**
60      * The map from concrete implementation class and interface to
61      * MBeanInfo for this type of MBean.
62      */

63     abstract MBeanInfoMap getMBeanInfoMap();
64     
65     /** Make an interface analyzer for this type of MBean. */
66     abstract MBeanAnalyzer<M> getAnalyzer(Class JavaDoc<?> mbeanInterface)
67     throws NotCompliantMBeanException JavaDoc;
68     
69     /** True if MBeans with this kind of introspector are MXBeans. */
70     abstract boolean isMXBean();
71     
72     /** Find the M corresponding to the given Method. */
73     abstract M mFrom(Method JavaDoc m);
74     
75     /** Get the name of this method. */
76     abstract String JavaDoc getName(M m);
77     
78     /**
79      * Get the return type of this method. This is the return type
80      * of a method in a Java interface, so for MXBeans it is the
81      * declared Java type, not the mapped Open Type.
82      */

83     abstract Type JavaDoc getGenericReturnType(M m);
84     
85     /**
86      * Get the parameter types of this method in the Java interface
87      * it came from.
88      */

89     abstract Type JavaDoc[] getGenericParameterTypes(M m);
90     
91     /**
92      * Get the signature of this method as a caller would have to supply
93      * it in MBeanServer.invoke. For MXBeans, the named types will be
94      * the mapped Open Types for the parameters.
95      */

96     abstract String JavaDoc[] getSignature(M m);
97     
98     /**
99      * Check that this method is valid. For example, a method in an
100      * MXBean interface is not valid if one of its parameters cannot be
101      * mapped to an Open Type.
102      */

103     abstract void checkMethod(M m) throws IllegalArgumentException JavaDoc;
104     
105     /**
106      * Invoke the method with the given target and arguments.
107      *
108      * @param cookie Additional information about the target. For an
109      * MXBean, this is the MXBeanLookup associated with the MXBean.
110      */

111     /*
112      * It would be cleaner if the type of the cookie were a
113      * type parameter to this class, but that would involve a lot of
114      * messy type parameter propagation just to avoid a couple of casts.
115      */

116     abstract Object JavaDoc invokeM2(M m, Object JavaDoc target, Object JavaDoc[] args, Object JavaDoc cookie)
117     throws InvocationTargetException JavaDoc, IllegalAccessException JavaDoc,
118             MBeanException JavaDoc;
119     
120     /**
121      * Test whether the given value is valid for the given parameter of this
122      * M.
123      */

124     abstract boolean validParameter(M m, Object JavaDoc value, int paramNo,
125             Object JavaDoc cookie);
126     
127     /**
128      * Construct an MBeanAttributeInfo for the given attribute based on the
129      * given getter and setter. One but not both of the getter and setter
130      * may be null.
131      */

132     abstract MBeanAttributeInfo JavaDoc getMBeanAttributeInfo(String JavaDoc attributeName,
133             M getter, M setter);
134     /**
135      * Construct an MBeanOperationInfo for the given operation based on
136      * the M it was derived from.
137      */

138     abstract MBeanOperationInfo JavaDoc getMBeanOperationInfo(String JavaDoc operationName,
139             M operation);
140     
141     /**
142      * Get a Descriptor containing fields that MBeans of this kind will
143      * always have. For example, MXBeans will always have "mxbean=true".
144      */

145     abstract Descriptor JavaDoc getBasicMBeanDescriptor();
146     
147     /**
148      * Get a Descriptor containing additional fields beyond the ones
149      * from getBasicMBeanDescriptor that MBeans whose concrete class
150      * is resourceClass will always have.
151      */

152     abstract Descriptor JavaDoc getMBeanDescriptor(Class JavaDoc<?> resourceClass);
153     
154     
155     final PerInterface<M> getPerInterface(Class JavaDoc<?> mbeanInterface)
156     throws NotCompliantMBeanException JavaDoc {
157         PerInterfaceMap<M> map = getPerInterfaceMap();
158         synchronized (map) {
159             WeakReference JavaDoc<PerInterface<M>> wr = map.get(mbeanInterface);
160             PerInterface<M> pi = (wr == null) ? null : wr.get();
161             if (pi == null) {
162                 try {
163                     MBeanAnalyzer<M> analyzer = getAnalyzer(mbeanInterface);
164                     MBeanInfo JavaDoc mbeanInfo =
165                             makeInterfaceMBeanInfo(mbeanInterface, analyzer);
166                     pi = new PerInterface<M>(mbeanInterface, this, analyzer,
167                             mbeanInfo);
168                     wr = new WeakReference JavaDoc<PerInterface<M>>(pi);
169                     map.put(mbeanInterface, wr);
170                 } catch (Exception JavaDoc x) {
171                     throw Introspector.throwException(mbeanInterface,x);
172                 }
173             }
174             return pi;
175         }
176     }
177     
178     /**
179      * Make the MBeanInfo skeleton for the given MBean interface using
180      * the given analyzer. This will never be the MBeanInfo of any real
181      * MBean (because the getClassName() must be a concrete class), but
182      * its MBeanAttributeInfo[] and MBeanOperationInfo[] can be inserted
183      * into such an MBeanInfo, and its Descriptor can be the basis for
184      * the MBeanInfo's Descriptor.
185      */

186     private MBeanInfo JavaDoc makeInterfaceMBeanInfo(Class JavaDoc<?> mbeanInterface,
187             MBeanAnalyzer<M> analyzer) {
188         final MBeanInfoMaker maker = new MBeanInfoMaker();
189         analyzer.visit(maker);
190         final String JavaDoc description =
191                 "Information on the management interface of the MBean";
192         return maker.makeMBeanInfo(mbeanInterface, description);
193     }
194     
195     /** True if the given getter and setter are consistent. */
196     final boolean consistent(M getter, M setter) {
197         return (getter == null || setter == null ||
198                 getGenericReturnType(getter).equals(getGenericParameterTypes(setter)[0]));
199     }
200     
201     /**
202      * Invoke the given M on the given target with the given args and cookie.
203      * Wrap exceptions appropriately.
204      */

205     final Object JavaDoc invokeM(M m, Object JavaDoc target, Object JavaDoc[] args, Object JavaDoc cookie)
206     throws MBeanException JavaDoc, ReflectionException JavaDoc {
207         try {
208             return invokeM2(m, target, args, cookie);
209         } catch (InvocationTargetException JavaDoc e) {
210             unwrapInvocationTargetException(e);
211             throw new RuntimeException JavaDoc(e); // not reached
212
} catch (IllegalAccessException JavaDoc e) {
213             throw new ReflectionException JavaDoc(e, e.toString());
214         }
215         /* We do not catch and wrap RuntimeException or Error,
216          * because we're in a DynamicMBean, so the logic for DynamicMBeans
217          * will do the wrapping.
218          */

219     }
220     
221     /**
222      * Invoke the given setter on the given target with the given argument
223      * and cookie. Wrap exceptions appropriately.
224      */

225     /* If the value is of the wrong type for the method we are about to
226      * invoke, we are supposed to throw an InvalidAttributeValueException.
227      * Rather than making the check always, we invoke the method, then
228      * if it throws an exception we check the type to see if that was
229      * what caused the exception. The assumption is that an exception
230      * from an invalid type will arise before any user method is ever
231      * called (either in reflection or in OpenConverter).
232      */

233     final void invokeSetter(String JavaDoc name, M setter, Object JavaDoc target, Object JavaDoc arg,
234             Object JavaDoc cookie)
235             throws MBeanException JavaDoc, ReflectionException JavaDoc,
236             InvalidAttributeValueException JavaDoc {
237         try {
238             invokeM2(setter, target, new Object JavaDoc[] {arg}, cookie);
239         } catch (IllegalAccessException JavaDoc e) {
240             throw new ReflectionException JavaDoc(e, e.toString());
241         } catch (RuntimeException JavaDoc e) {
242             maybeInvalidParameter(name, setter, arg, cookie);
243             throw e;
244         } catch (InvocationTargetException JavaDoc e) {
245             maybeInvalidParameter(name, setter, arg, cookie);
246             unwrapInvocationTargetException(e);
247         }
248     }
249     
250     private void maybeInvalidParameter(String JavaDoc name, M setter, Object JavaDoc arg,
251             Object JavaDoc cookie)
252             throws InvalidAttributeValueException JavaDoc {
253         if (!validParameter(setter, arg, 0, cookie)) {
254             final String JavaDoc msg =
255                     "Invalid value for attribute " + name + ": " + arg;
256             throw new InvalidAttributeValueException JavaDoc(msg);
257         }
258     }
259     
260     static boolean isValidParameter(Method JavaDoc m, Object JavaDoc value, int paramNo) {
261         Class JavaDoc<?> c = m.getParameterTypes()[paramNo];
262         try {
263             // Following is expensive but we only call this method to determine
264
// if an exception is due to an incompatible parameter type.
265
// Plain old c.isInstance doesn't work for primitive types.
266
Object JavaDoc a = Array.newInstance(c, 1);
267             Array.set(a, 0, value);
268             return true;
269         } catch (IllegalArgumentException JavaDoc e) {
270             return false;
271         }
272     }
273     
274     private static void
275             unwrapInvocationTargetException(InvocationTargetException JavaDoc e)
276             throws MBeanException JavaDoc {
277         Throwable JavaDoc t = e.getCause();
278         if (t instanceof RuntimeException JavaDoc)
279             throw (RuntimeException JavaDoc) t;
280         else if (t instanceof Error JavaDoc)
281             throw (Error JavaDoc) t;
282         else
283             throw new MBeanException JavaDoc((Exception JavaDoc) t,
284                     (t == null ? null : t.toString()));
285     }
286     
287     /** A visitor that constructs the per-interface MBeanInfo. */
288     private class MBeanInfoMaker implements MBeanAnalyzer.MBeanVisitor<M> {
289         
290         public void visitAttribute(String JavaDoc attributeName,
291                 M getter,
292                 M setter) {
293             MBeanAttributeInfo JavaDoc mbai =
294                     getMBeanAttributeInfo(attributeName, getter, setter);
295             
296             attrs.add(mbai);
297         }
298         
299         public void visitOperation(String JavaDoc operationName,
300                 M operation) {
301             MBeanOperationInfo JavaDoc mboi =
302                     getMBeanOperationInfo(operationName, operation);
303             
304             ops.add(mboi);
305         }
306         
307         /** Make an MBeanInfo based on the attributes and operations
308          * found in the interface. */

309         MBeanInfo JavaDoc makeMBeanInfo(Class JavaDoc<?> mbeanInterface,
310                 String JavaDoc description) {
311             final MBeanAttributeInfo JavaDoc[] attrArray =
312                     attrs.toArray(new MBeanAttributeInfo JavaDoc[0]);
313             final MBeanOperationInfo JavaDoc[] opArray =
314                     ops.toArray(new MBeanOperationInfo JavaDoc[0]);
315             final String JavaDoc interfaceClassName =
316                     "interfaceClassName=" + mbeanInterface.getName();
317             final Descriptor JavaDoc interfDescriptor =
318                     new ImmutableDescriptor JavaDoc(interfaceClassName);
319             final Descriptor JavaDoc mbeanDescriptor = getBasicMBeanDescriptor();
320             final Descriptor JavaDoc annotatedDescriptor =
321                     Introspector.descriptorForElement(mbeanInterface);
322             final Descriptor JavaDoc descriptor =
323                     DescriptorCache.getInstance().union(interfDescriptor,
324                     mbeanDescriptor,
325                     annotatedDescriptor);
326             
327             return new MBeanInfo JavaDoc(mbeanInterface.getName(),
328                     description,
329                     attrArray,
330                     null,
331                     opArray,
332                     null,
333                     descriptor);
334         }
335         
336         private final List JavaDoc<MBeanAttributeInfo JavaDoc> attrs = newList();
337         private final List JavaDoc<MBeanOperationInfo JavaDoc> ops = newList();
338     }
339     
340     /*
341      * Looking up the MBeanInfo for a given base class (implementation class)
342      * is complicated by the fact that we may use the same base class with
343      * several different explicit MBean interfaces via the
344      * javax.management.StandardMBean class. It is further complicated
345      * by the fact that we have to be careful not to retain a strong reference
346      * to any Class object for fear we would prevent a ClassLoader from being
347      * garbage-collected. So we have a first lookup from the base class
348      * to a map for each interface that base class might specify giving
349      * the MBeanInfo constructed for that base class and interface.
350      */

351     static class MBeanInfoMap
352             extends WeakHashMap JavaDoc<Class JavaDoc<?>, WeakHashMap JavaDoc<Class JavaDoc<?>, MBeanInfo JavaDoc>> {
353     }
354     
355     /**
356      * Return the MBeanInfo for the given resource, based on the given
357      * per-interface data.
358      */

359     final MBeanInfo JavaDoc getMBeanInfo(Object JavaDoc resource, PerInterface<M> perInterface) {
360         MBeanInfo JavaDoc mbi =
361                 getClassMBeanInfo(resource.getClass(), perInterface);
362         MBeanNotificationInfo JavaDoc[] notifs = findNotifications(resource);
363         if (notifs == null || notifs.length == 0)
364             return mbi;
365         else {
366             return new MBeanInfo JavaDoc(mbi.getClassName(),
367                     mbi.getDescription(),
368                     mbi.getAttributes(),
369                     mbi.getConstructors(),
370                     mbi.getOperations(),
371                     notifs,
372                     mbi.getDescriptor());
373         }
374     }
375     
376     /**
377      * Return the basic MBeanInfo for resources of the given class and
378      * per-interface data. This MBeanInfo might not be the final MBeanInfo
379      * for instances of the class, because if the class is a
380      * NotificationBroadcaster then each instance gets to decide what
381      * MBeanNotificationInfo[] to put in its own MBeanInfo.
382      */

383     final MBeanInfo JavaDoc getClassMBeanInfo(Class JavaDoc<?> resourceClass,
384             PerInterface<M> perInterface) {
385         MBeanInfoMap map = getMBeanInfoMap();
386         synchronized (map) {
387             WeakHashMap JavaDoc<Class JavaDoc<?>, MBeanInfo JavaDoc> intfMap = map.get(resourceClass);
388             if (intfMap == null) {
389                 intfMap = new WeakHashMap JavaDoc<Class JavaDoc<?>, MBeanInfo JavaDoc>();
390                 map.put(resourceClass, intfMap);
391             }
392             Class JavaDoc<?> intfClass = perInterface.getMBeanInterface();
393             MBeanInfo JavaDoc mbi = intfMap.get(intfClass);
394             if (mbi == null) {
395                 MBeanInfo JavaDoc imbi = perInterface.getMBeanInfo();
396                 Descriptor JavaDoc descriptor =
397                         ImmutableDescriptor.union(imbi.getDescriptor(),
398                         getMBeanDescriptor(resourceClass));
399                 mbi = new MBeanInfo JavaDoc(resourceClass.getName(),
400                         imbi.getDescription(),
401                         imbi.getAttributes(),
402                         findConstructors(resourceClass),
403                         imbi.getOperations(),
404                         (MBeanNotificationInfo JavaDoc[]) null,
405                         descriptor);
406                 intfMap.put(intfClass, mbi);
407             }
408             return mbi;
409         }
410     }
411     
412     static MBeanNotificationInfo JavaDoc[] findNotifications(Object JavaDoc moi) {
413         if (!(moi instanceof NotificationBroadcaster JavaDoc))
414             return null;
415         MBeanNotificationInfo JavaDoc[] mbn =
416                 ((NotificationBroadcaster JavaDoc) moi).getNotificationInfo();
417         if (mbn == null)
418             return null;
419         MBeanNotificationInfo JavaDoc[] result =
420                 new MBeanNotificationInfo JavaDoc[mbn.length];
421         for (int i = 0; i < mbn.length; i++) {
422             MBeanNotificationInfo JavaDoc ni = mbn[i];
423             if (ni.getClass() != MBeanNotificationInfo JavaDoc.class)
424                 ni = (MBeanNotificationInfo JavaDoc) ni.clone();
425             result[i] = ni;
426         }
427         return result;
428     }
429     
430     private static MBeanConstructorInfo JavaDoc[] findConstructors(Class JavaDoc<?> c) {
431         Constructor JavaDoc[] cons = c.getConstructors();
432         MBeanConstructorInfo JavaDoc[] mbc = new MBeanConstructorInfo JavaDoc[cons.length];
433         for (int i = 0; i < cons.length; i++) {
434             final String JavaDoc descr = "Public constructor of the MBean";
435             mbc[i] = new MBeanConstructorInfo JavaDoc(descr, cons[i]);
436         }
437         return mbc;
438     }
439     
440 }
441
Popular Tags