KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * @(#)MBeanAnalyzer.java 1.17 07/01/29
3  *
4  * Copyright 2007 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 import static com.sun.jmx.mbeanserver.Util.*;
11
12 import java.lang.reflect.Method JavaDoc;
13 import java.util.Arrays JavaDoc;
14 import java.util.Collection JavaDoc;
15 import java.util.Comparator JavaDoc;
16 import java.util.HashSet JavaDoc;
17 import java.util.LinkedHashMap JavaDoc;
18 import java.util.List JavaDoc;
19 import java.util.Map JavaDoc;
20 import java.util.Set JavaDoc;
21
22 import javax.management.NotCompliantMBeanException JavaDoc;
23
24 /**
25  * <p>An analyzer for a given MBean interface. The analyzer can
26  * be for Standard MBeans or MXBeans, depending on the MBeanIntrospector
27  * passed at construction.
28  *
29  * <p>The analyzer can
30  * visit the attributes and operations of the interface, calling
31  * a caller-supplied visitor method for each one.</p>
32  *
33  * @param <M> Method or ConvertingMethod according as this is a
34  * Standard MBean or an MXBean.
35  *
36  * @since 1.6
37  */

38 class MBeanAnalyzer<M> {
39
40     static interface MBeanVisitor<M> {
41         public void visitAttribute(String JavaDoc attributeName,
42                 M getter,
43                 M setter);
44         public void visitOperation(String JavaDoc operationName,
45                 M operation);
46     }
47
48     void visit(MBeanVisitor<M> visitor) {
49         // visit attributes
50
for (Map.Entry JavaDoc<String JavaDoc, AttrMethods<M>> entry : attrMap.entrySet()) {
51             String JavaDoc name = entry.getKey();
52             AttrMethods<M> am = entry.getValue();
53             visitor.visitAttribute(name, am.getter, am.setter);
54         }
55
56         // visit operations
57
for (Map.Entry JavaDoc<String JavaDoc, List JavaDoc<M>> entry : opMap.entrySet()) {
58             for (M m : entry.getValue())
59                 visitor.visitOperation(entry.getKey(), m);
60         }
61     }
62
63     /* Map op name to method */
64     private Map JavaDoc<String JavaDoc, List JavaDoc<M>> opMap = newInsertionOrderMap();
65     /* Map attr name to getter and/or setter */
66     private Map JavaDoc<String JavaDoc, AttrMethods<M>> attrMap = newInsertionOrderMap();
67     
68     private static class AttrMethods<M> {
69         M getter;
70         M setter;
71     }
72
73     /**
74      * <p>Return an MBeanAnalyzer for the given MBean interface and
75      * MBeanIntrospector. Calling this method twice with the same
76      * parameters may return the same object or two different but
77      * equivalent objects.
78      */

79     // Currently it's two different but equivalent objects. This only
80
// really impacts proxy generation. For MBean creation, the
81
// cached PerInterface object for an MBean interface means that
82
// an analyzer will not be recreated for a second MBean using the
83
// same interface.
84
static <M> MBeanAnalyzer<M> analyzer(Class JavaDoc<?> mbeanInterface,
85             MBeanIntrospector<M> introspector)
86             throws NotCompliantMBeanException JavaDoc {
87         return new MBeanAnalyzer<M>(mbeanInterface, introspector);
88     }
89
90     private MBeanAnalyzer(Class JavaDoc<?> mbeanInterface,
91             MBeanIntrospector<M> introspector)
92             throws NotCompliantMBeanException JavaDoc {
93         if (!mbeanInterface.isInterface()) {
94             throw new NotCompliantMBeanException JavaDoc("Not an interface: " +
95                     mbeanInterface.getName());
96         }
97
98         try {
99             initMaps(mbeanInterface, introspector);
100         } catch (Exception JavaDoc x) {
101             throw Introspector.throwException(mbeanInterface,x);
102         }
103     }
104
105     // Introspect the mbeanInterface and initialize this object's maps.
106
//
107
private void initMaps(Class JavaDoc<?> mbeanInterface,
108             MBeanIntrospector<M> introspector) throws Exception JavaDoc {
109         final Method JavaDoc[] methodArray = mbeanInterface.getMethods();
110
111         final List JavaDoc<Method JavaDoc> methods = eliminateCovariantMethods(methodArray);
112         
113         /* Run through the methods to detect inconsistencies and to enable
114            us to give getter and setter together to visitAttribute. */

115         for (Method JavaDoc m : methods) {
116             String JavaDoc name = m.getName();
117
118             final M cm = introspector.mFrom(m);
119
120             String JavaDoc attrName = "";
121             if (name.startsWith("get"))
122                 attrName = name.substring(3);
123             else if (name.startsWith("is")
124             && m.getReturnType() == boolean.class)
125                 attrName = name.substring(2);
126
127             if (attrName.length() != 0 && m.getParameterTypes().length == 0
128                     && m.getReturnType() != void.class) {
129                 // It's a getter
130
// Check we don't have both isX and getX
131
AttrMethods<M> am = attrMap.get(attrName);
132                 if (am == null)
133                     am = new AttrMethods<M>();
134                 else {
135                     if (am.getter != null) {
136                         final String JavaDoc msg = "Attribute " + attrName +
137                                 " has more than one getter";
138                         throw new NotCompliantMBeanException JavaDoc(msg);
139                     }
140                 }
141                 am.getter = cm;
142                 attrMap.put(attrName, am);
143             } else if (name.startsWith("set") && name.length() > 3
144                     && m.getParameterTypes().length == 1 &&
145                     m.getReturnType() == void.class) {
146                 // It's a setter
147
attrName = name.substring(3);
148                 AttrMethods<M> am = attrMap.get(attrName);
149                 if (am == null)
150                     am = new AttrMethods<M>();
151                 else if (am.setter != null) {
152                     final String JavaDoc msg = "Attribute " + attrName +
153                             " has more than one setter";
154                     throw new NotCompliantMBeanException JavaDoc(msg);
155                 }
156                 am.setter = cm;
157                 attrMap.put(attrName, am);
158             } else {
159                 // It's an operation
160
List JavaDoc<M> cms = opMap.get(name);
161                 if (cms == null)
162                     cms = newList();
163                 cms.add(cm);
164                 opMap.put(name, cms);
165             }
166         }
167         /* Check that getters and setters are consistent. */
168         for (Map.Entry JavaDoc<String JavaDoc, AttrMethods<M>> entry : attrMap.entrySet()) {
169             AttrMethods<M> am = entry.getValue();
170             if (!introspector.consistent(am.getter, am.setter)) {
171                 final String JavaDoc msg = "Getter and setter for " + entry.getKey() +
172                         " have inconsistent types";
173                 throw new NotCompliantMBeanException JavaDoc(msg);
174             }
175         }
176     }
177     
178     /**
179      * A comparator that defines a total order so that methods have the
180      * same name and identical signatures appear next to each others.
181      * The methods are sorted in such a way that methods which
182      * override each other will sit next to each other, with the
183      * overridden method first - e.g. Object getFoo() is placed before
184      * Integer getFoo(). This makes it possible to determine whether
185      * a method overrides another one simply by looking at the method(s)
186      * that precedes it in the list. (see eliminateCovariantMethods).
187      **/

188     private static class MethodOrder implements Comparator JavaDoc<Method JavaDoc> {
189         public int compare(Method JavaDoc a, Method JavaDoc b) {
190             final int cmp = a.getName().compareTo(b.getName());
191             if (cmp != 0) return cmp;
192             final Class JavaDoc<?>[] aparams = a.getParameterTypes();
193             final Class JavaDoc<?>[] bparams = b.getParameterTypes();
194             if (aparams.length != bparams.length)
195                 return aparams.length - bparams.length;
196             if (!Arrays.equals(aparams, bparams)) {
197                 return Arrays.toString(aparams).
198                         compareTo(Arrays.toString(bparams));
199             }
200             final Class JavaDoc<?> aret = a.getReturnType();
201             final Class JavaDoc<?> bret = b.getReturnType();
202             if (aret == bret) return 0;
203             
204             // Super type comes first: Object, Number, Integer
205
if (aret.isAssignableFrom(bret))
206                 return -1;
207             return +1; // could assert bret.isAssignableFrom(aret)
208
}
209         public final static MethodOrder instance = new MethodOrder();
210     }
211
212
213     /* Eliminate methods that are overridden with a covariant return type.
214        Reflection will return both the original and the overriding method
215        but only the overriding one is of interest. We return the methods
216        in the same order they arrived in. This isn't required by the spec
217        but existing code may depend on it and users may be used to seeing
218        operations or attributes appear in a particular order. */

219     static List JavaDoc<Method JavaDoc>
220             eliminateCovariantMethods(Method JavaDoc[] methodArray) {
221         // We are assuming that you never have very many methods with the
222
// same name, so it is OK to use algorithms that are quadratic
223
// in the number of methods with the same name.
224

225         final int len = methodArray.length;
226         final Method JavaDoc[] sorted = methodArray.clone();
227         Arrays.sort(sorted,MethodOrder.instance);
228         final Set JavaDoc<Method JavaDoc> overridden = newSet();
229         for (int i=1;i<len;i++) {
230             final Method JavaDoc m0 = sorted[i-1];
231             final Method JavaDoc m1 = sorted[i];
232             
233             // Methods that don't have the same name can't override each others
234
if (!m0.getName().equals(m1.getName())) continue;
235             
236             // Methods that have the same name and same signature override
237
// each other. In that case, the second method overrides the first,
238
// due to the way we have sorted them in MethodOrder.
239
if (Arrays.equals(m0.getParameterTypes(),
240                     m1.getParameterTypes())) {
241                 overridden.add(m0);
242             }
243         }
244         
245         final List JavaDoc<Method JavaDoc> methods = newList(Arrays.asList(methodArray));
246         methods.removeAll(overridden);
247         return methods;
248     }
249     
250     
251 }
252
Popular Tags