001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.tomcat.util.modeler.modules;
019:
020: import java.lang.reflect.Method;
021: import java.lang.reflect.Modifier;
022: import java.math.BigDecimal;
023: import java.math.BigInteger;
024: import java.util.ArrayList;
025: import java.util.Enumeration;
026: import java.util.Hashtable;
027: import java.util.List;
028:
029: import javax.management.ObjectName;
030:
031: import org.apache.juli.logging.Log;
032: import org.apache.juli.logging.LogFactory;
033: import org.apache.tomcat.util.modeler.AttributeInfo;
034: import org.apache.tomcat.util.modeler.ManagedBean;
035: import org.apache.tomcat.util.modeler.OperationInfo;
036: import org.apache.tomcat.util.modeler.ParameterInfo;
037: import org.apache.tomcat.util.modeler.Registry;
038:
039: public class MbeansDescriptorsIntrospectionSource extends ModelerSource {
040: private static Log log = LogFactory
041: .getLog(MbeansDescriptorsIntrospectionSource.class);
042:
043: Registry registry;
044: String location;
045: String type;
046: Object source;
047: List mbeans = new ArrayList();
048:
049: public void setRegistry(Registry reg) {
050: this .registry = reg;
051: }
052:
053: public void setLocation(String loc) {
054: this .location = loc;
055: }
056:
057: /** Used if a single component is loaded
058: *
059: * @param type
060: */
061: public void setType(String type) {
062: this .type = type;
063: }
064:
065: public void setSource(Object source) {
066: this .source = source;
067: }
068:
069: public List loadDescriptors(Registry registry, String location,
070: String type, Object source) throws Exception {
071: setRegistry(registry);
072: setLocation(location);
073: setType(type);
074: setSource(source);
075: execute();
076: return mbeans;
077: }
078:
079: public void execute() throws Exception {
080: if (registry == null)
081: registry = Registry.getRegistry();
082: try {
083: ManagedBean managed = createManagedBean(registry, null,
084: (Class) source, type);
085: if (managed == null)
086: return;
087: managed.setName(type);
088:
089: mbeans.add(managed);
090:
091: } catch (Exception ex) {
092: log.error("Error reading descriptors ", ex);
093: }
094: }
095:
096: // ------------ Implementation for non-declared introspection classes
097:
098: static Hashtable specialMethods = new Hashtable();
099: static {
100: specialMethods.put("preDeregister", "");
101: specialMethods.put("postDeregister", "");
102: }
103:
104: private static String strArray[] = new String[0];
105: private static ObjectName objNameArray[] = new ObjectName[0];
106: // createMBean == registerClass + registerMBean
107:
108: private static Class[] supportedTypes = new Class[] {
109: Boolean.class, Boolean.TYPE, Byte.class, Byte.TYPE,
110: Character.class, Character.TYPE, Short.class, Short.TYPE,
111: Integer.class, Integer.TYPE, Long.class, Long.TYPE,
112: Float.class, Float.TYPE, Double.class, Double.TYPE,
113: String.class, strArray.getClass(), BigDecimal.class,
114: BigInteger.class, ObjectName.class,
115: objNameArray.getClass(), java.io.File.class, };
116:
117: /**
118: * Check if this class is one of the supported types.
119: * If the class is supported, returns true. Otherwise,
120: * returns false.
121: * @param ret The class to check
122: * @return boolean True if class is supported
123: */
124: private boolean supportedType(Class ret) {
125: for (int i = 0; i < supportedTypes.length; i++) {
126: if (ret == supportedTypes[i]) {
127: return true;
128: }
129: }
130: if (isBeanCompatible(ret)) {
131: return true;
132: }
133: return false;
134: }
135:
136: /**
137: * Check if this class conforms to JavaBeans specifications.
138: * If the class is conformant, returns true.
139: *
140: * @param javaType The class to check
141: * @return boolean True if the class is compatible.
142: */
143: protected boolean isBeanCompatible(Class javaType) {
144: // Must be a non-primitive and non array
145: if (javaType.isArray() || javaType.isPrimitive()) {
146: return false;
147: }
148:
149: // Anything in the java or javax package that
150: // does not have a defined mapping is excluded.
151: if (javaType.getName().startsWith("java.")
152: || javaType.getName().startsWith("javax.")) {
153: return false;
154: }
155:
156: try {
157: javaType.getConstructor(new Class[] {});
158: } catch (java.lang.NoSuchMethodException e) {
159: return false;
160: }
161:
162: // Make sure superclass is compatible
163: Class super Class = javaType.getSuperclass();
164: if (super Class != null && super Class != java.lang.Object.class
165: && super Class != java.lang.Exception.class
166: && super Class != java.lang.Throwable.class) {
167: if (!isBeanCompatible(super Class)) {
168: return false;
169: }
170: }
171: return true;
172: }
173:
174: /**
175: * Process the methods and extract 'attributes', methods, etc
176: *
177: * @param realClass The class to process
178: * @param methods The methods to process
179: * @param attMap The attribute map (complete)
180: * @param getAttMap The readable attributess map
181: * @param setAttMap The settable attributes map
182: * @param invokeAttMap The invokable attributes map
183: */
184: private void initMethods(Class realClass, Method methods[],
185: Hashtable attMap, Hashtable getAttMap, Hashtable setAttMap,
186: Hashtable invokeAttMap) {
187: for (int j = 0; j < methods.length; ++j) {
188: String name = methods[j].getName();
189:
190: if (Modifier.isStatic(methods[j].getModifiers()))
191: continue;
192: if (!Modifier.isPublic(methods[j].getModifiers())) {
193: if (log.isDebugEnabled())
194: log.debug("Not public " + methods[j]);
195: continue;
196: }
197: if (methods[j].getDeclaringClass() == Object.class)
198: continue;
199: Class params[] = methods[j].getParameterTypes();
200:
201: if (name.startsWith("get") && params.length == 0) {
202: Class ret = methods[j].getReturnType();
203: if (!supportedType(ret)) {
204: if (log.isDebugEnabled())
205: log.debug("Unsupported type " + methods[j]);
206: continue;
207: }
208: name = unCapitalize(name.substring(3));
209:
210: getAttMap.put(name, methods[j]);
211: // just a marker, we don't use the value
212: attMap.put(name, methods[j]);
213: } else if (name.startsWith("is") && params.length == 0) {
214: Class ret = methods[j].getReturnType();
215: if (Boolean.TYPE != ret) {
216: if (log.isDebugEnabled())
217: log.debug("Unsupported type " + methods[j]
218: + " " + ret);
219: continue;
220: }
221: name = unCapitalize(name.substring(2));
222:
223: getAttMap.put(name, methods[j]);
224: // just a marker, we don't use the value
225: attMap.put(name, methods[j]);
226:
227: } else if (name.startsWith("set") && params.length == 1) {
228: if (!supportedType(params[0])) {
229: if (log.isDebugEnabled())
230: log.debug("Unsupported type " + methods[j]
231: + " " + params[0]);
232: continue;
233: }
234: name = unCapitalize(name.substring(3));
235: setAttMap.put(name, methods[j]);
236: attMap.put(name, methods[j]);
237: } else {
238: if (params.length == 0) {
239: if (specialMethods.get(methods[j].getName()) != null)
240: continue;
241: invokeAttMap.put(name, methods[j]);
242: } else {
243: boolean supported = true;
244: for (int i = 0; i < params.length; i++) {
245: if (!supportedType(params[i])) {
246: supported = false;
247: break;
248: }
249: }
250: if (supported)
251: invokeAttMap.put(name, methods[j]);
252: }
253: }
254: }
255: }
256:
257: /**
258: * XXX Find if the 'className' is the name of the MBean or
259: * the real class ( I suppose first )
260: * XXX Read (optional) descriptions from a .properties, generated
261: * from source
262: * XXX Deal with constructors
263: *
264: * @param registry The Bean registry (not used)
265: * @param domain The bean domain (not used)
266: * @param realClass The class to analyze
267: * @param type The bean type
268: * @return ManagedBean The create MBean
269: */
270: public ManagedBean createManagedBean(Registry registry,
271: String domain, Class realClass, String type) {
272: ManagedBean mbean = new ManagedBean();
273:
274: Method methods[] = null;
275:
276: Hashtable attMap = new Hashtable();
277: // key: attribute val: getter method
278: Hashtable getAttMap = new Hashtable();
279: // key: attribute val: setter method
280: Hashtable setAttMap = new Hashtable();
281: // key: operation val: invoke method
282: Hashtable invokeAttMap = new Hashtable();
283:
284: methods = realClass.getMethods();
285:
286: initMethods(realClass, methods, attMap, getAttMap, setAttMap,
287: invokeAttMap);
288:
289: try {
290:
291: Enumeration en = attMap.keys();
292: while (en.hasMoreElements()) {
293: String name = (String) en.nextElement();
294: AttributeInfo ai = new AttributeInfo();
295: ai.setName(name);
296: Method gm = (Method) getAttMap.get(name);
297: if (gm != null) {
298: //ai.setGetMethodObj( gm );
299: ai.setGetMethod(gm.getName());
300: Class t = gm.getReturnType();
301: if (t != null)
302: ai.setType(t.getName());
303: }
304: Method sm = (Method) setAttMap.get(name);
305: if (sm != null) {
306: //ai.setSetMethodObj(sm);
307: Class t = sm.getParameterTypes()[0];
308: if (t != null)
309: ai.setType(t.getName());
310: ai.setSetMethod(sm.getName());
311: }
312: ai.setDescription("Introspected attribute " + name);
313: if (log.isDebugEnabled())
314: log.debug("Introspected attribute " + name + " "
315: + gm + " " + sm);
316: if (gm == null)
317: ai.setReadable(false);
318: if (sm == null)
319: ai.setWriteable(false);
320: if (sm != null || gm != null)
321: mbean.addAttribute(ai);
322: }
323:
324: en = invokeAttMap.keys();
325: while (en.hasMoreElements()) {
326: String name = (String) en.nextElement();
327: Method m = (Method) invokeAttMap.get(name);
328: if (m != null && name != null) {
329: OperationInfo op = new OperationInfo();
330: op.setName(name);
331: op.setReturnType(m.getReturnType().getName());
332: op.setDescription("Introspected operation " + name);
333: Class parms[] = m.getParameterTypes();
334: for (int i = 0; i < parms.length; i++) {
335: ParameterInfo pi = new ParameterInfo();
336: pi.setType(parms[i].getName());
337: pi.setName("param" + i);
338: pi
339: .setDescription("Introspected parameter param"
340: + i);
341: op.addParameter(pi);
342: }
343: mbean.addOperation(op);
344: } else {
345: log.error("Null arg " + name + " " + m);
346: }
347: }
348:
349: /*Constructor[] constructors = realClass.getConstructors();
350: for(int i=0;i<constructors.length;i++) {
351: ConstructorInfo info = new ConstructorInfo();
352: String className = realClass.getName();
353: int nIndex = -1;
354: if((nIndex = className.lastIndexOf('.'))!=-1) {
355: className = className.substring(nIndex+1);
356: }
357: info.setName(className);
358: info.setDescription(constructors[i].getName());
359: Class classes[] = constructors[i].getParameterTypes();
360: for(int j=0;j<classes.length;j++) {
361: ParameterInfo pi = new ParameterInfo();
362: pi.setType(classes[j].getName());
363: pi.setName("param" + j);
364: pi.setDescription("Introspected parameter param" + j);
365: info.addParameter(pi);
366: }
367: mbean.addConstructor(info);
368: }
369: */
370:
371: if (log.isDebugEnabled())
372: log.debug("Setting name: " + type);
373: mbean.setName(type);
374:
375: return mbean;
376: } catch (Exception ex) {
377: ex.printStackTrace();
378: return null;
379: }
380: }
381:
382: // -------------------- Utils --------------------
383: /**
384: * Converts the first character of the given
385: * String into lower-case.
386: *
387: * @param name The string to convert
388: * @return String
389: */
390: private static String unCapitalize(String name) {
391: if (name == null || name.length() == 0) {
392: return name;
393: }
394: char chars[] = name.toCharArray();
395: chars[0] = Character.toLowerCase(chars[0]);
396: return new String(chars);
397: }
398:
399: }
400:
401: // End of class: MbeanDescriptorsIntrospectionSource
|