001: /*
002: * JBoss, Home of Professional Open Source.
003: * Copyright 2006, Red Hat Middleware LLC, and individual contributors
004: * as indicated by the @author tags. See the copyright.txt file in the
005: * distribution for a full listing of individual contributors.
006: *
007: * This is free software; you can redistribute it and/or modify it
008: * under the terms of the GNU Lesser General Public License as
009: * published by the Free Software Foundation; either version 2.1 of
010: * the License, or (at your option) any later version.
011: *
012: * This software is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this software; if not, write to the Free
019: * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
021: */
022: package org.jboss.mx.metadata;
023:
024: import java.lang.reflect.Constructor;
025: import java.lang.reflect.Method;
026: import java.util.ArrayList;
027: import java.util.HashMap;
028: import java.util.Iterator;
029: import java.util.List;
030:
031: import javax.management.IntrospectionException;
032: import javax.management.MBeanAttributeInfo;
033: import javax.management.MBeanConstructorInfo;
034: import javax.management.MBeanInfo;
035: import javax.management.MBeanNotificationInfo;
036: import javax.management.MBeanOperationInfo;
037: import javax.management.NotCompliantMBeanException;
038: import javax.management.NotificationBroadcaster;
039:
040: /**
041: * This metadata builder implementation builds a MBean info based on the
042: * naming rules of the Standard MBeans. The MBean server uses this builder
043: * to generate the metadata for Standard MBeans. <p>
044: *
045: * In cooperation with the
046: * {@link MBeanInfoConversion#toModelMBeanInfo MBeanInfoConversion} class you
047: * can use this builder as a migration tool from Standard to Model MBeans, or
048: * for cases where you want the management interface be based on a compile-time
049: * type safe interface. It is also possible to subclass this builder
050: * implementation to extend it to support more sophisticated introspection rules
051: * such as adding descriptors to management interface elements.
052: *
053: * @author <a href="mailto:juha@jboss.org">Juha Lindfors</a>.
054: * @author <a href="mailto:trevor@protocool.com">Trevor Squires</a>.
055: * @author <a href="mailto:thomas.diesler@jboss.com">Thomas Diesler</a>.
056: * @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>.
057: */
058: public class StandardMetaData extends AbstractBuilder {
059:
060: // Attributes ----------------------------------------------------
061:
062: /**
063: * The MBean object instance.
064: * Can be <tt>null</tt>.
065: */
066: private Object mbeanInstance = null;
067:
068: /**
069: * The class of the MBean instance.
070: */
071: private Class mbeanClass = null;
072:
073: /**
074: * The interface used as a basis for constructing the MBean metadata.
075: */
076: private Class mbeanInterface = null;
077:
078: // Static --------------------------------------------------------
079:
080: /**
081: * Locates an interface for a class that matches the Standard MBean naming
082: * convention.
083: *
084: * @param mbeanClass the class to investigate
085: *
086: * @return the Standard MBean interface class or <tt>null</tt> if not found
087: */
088: public static Class findStandardInterface(Class mbeanClass) {
089: Class concrete = mbeanClass;
090: Class stdInterface = null;
091: while (null != concrete) {
092: stdInterface = findStandardInterface(concrete, concrete
093: .getInterfaces());
094: if (null != stdInterface) {
095: return stdInterface;
096: }
097: concrete = concrete.getSuperclass();
098: }
099: return null;
100: }
101:
102: private static Class findStandardInterface(Class concrete,
103: Class[] interfaces) {
104: String stdName = concrete.getName() + "MBean";
105: Class retval = null;
106:
107: // look to see if this class implements MBean std interface
108: for (int i = 0; i < interfaces.length; ++i) {
109: if (interfaces[i].getName().equals(stdName)) {
110: retval = interfaces[i];
111: break;
112: }
113: }
114:
115: return retval;
116: }
117:
118: // Constructors --------------------------------------------------
119:
120: /**
121: * Initializes the Standard metadata builder. The JMX metadata is based
122: * on the class of the given resource instance.
123: *
124: * @param mbeanInstance MBean instance
125: */
126: public StandardMetaData(Object mbeanInstance)
127: throws NotCompliantMBeanException {
128: this (mbeanInstance.getClass());
129: this .mbeanInstance = mbeanInstance;
130: }
131:
132: /**
133: * Initializes the Standard metadata builder. The JMX metadata is based
134: * on the given class.
135: *
136: * @param mbeanClass resource class that implements an interface
137: * adhering to the Standard MBean naming conventions
138: */
139: public StandardMetaData(Class mbeanClass)
140: throws NotCompliantMBeanException {
141: this .mbeanClass = mbeanClass;
142: this .mbeanInterface = StandardMetaData
143: .findStandardInterface(mbeanClass);
144: if (this .mbeanInterface == null)
145: throw new NotCompliantMBeanException(
146: "Cannot obtain management interface for: "
147: + mbeanClass);
148: }
149:
150: /**
151: * Initializes the Standard metadata builder. The JMX metadata is based
152: * on the passed mbean interface.
153: *
154: * @param mbInstance MBean instance
155: * @param mbInterface the management interface
156: */
157: public StandardMetaData(Object mbInstance, Class mbInterface)
158: throws NotCompliantMBeanException {
159: this .mbeanInstance = mbInstance;
160: this .mbeanClass = mbInstance.getClass();
161: this .mbeanInterface = mbInterface;
162:
163: // search for it
164: if (this .mbeanInterface == null)
165: this .mbeanInterface = StandardMetaData
166: .findStandardInterface(mbeanClass);
167:
168: if (this .mbeanInterface == null)
169: throw new NotCompliantMBeanException(
170: "Cannot obtain management interface for: "
171: + mbeanClass);
172: if (this .mbeanInterface.isInterface() == false)
173: throw new NotCompliantMBeanException(
174: "Management interface is not an interface: "
175: + mbeanInterface);
176: }
177:
178: /**
179: * Retrieve the management interface
180: */
181: public Class getMBeanInterface() {
182: return mbeanInterface;
183: }
184:
185: // MetaDataBuilder implementation --------------------------------
186:
187: public MBeanInfo build() throws NotCompliantMBeanException {
188: try {
189: // First check the mbean instance implements the interface
190: if (mbeanInterface == null)
191: throw new NotCompliantMBeanException(
192: "The mbean does not implement a management interface");
193: if (mbeanInstance != null
194: && mbeanInterface.isInstance(mbeanInstance) == false)
195: throw new NotCompliantMBeanException(
196: "The mbean does not implement its management interface "
197: + mbeanInterface.getName());
198:
199: // First build the constructors
200: Constructor[] constructors = mbeanClass.getConstructors();
201: MBeanConstructorInfo[] constructorInfo = new MBeanConstructorInfo[constructors.length];
202: for (int i = 0; i < constructors.length; ++i) {
203: constructorInfo[i] = new MBeanConstructorInfo(
204: "MBean Constructor.", constructors[i]);
205: }
206:
207: // Next we have to figure out how the methods in the mbean class map
208: // to attributes and operations
209: Method[] methods = mbeanInterface.getMethods();
210: HashMap getters = new HashMap();
211: HashMap setters = new HashMap();
212:
213: HashMap operInfo = new HashMap();
214: List attrInfo = new ArrayList();
215:
216: for (int i = 0; i < methods.length; ++i) {
217: String methodName = methods[i].getName();
218: Class[] signature = methods[i].getParameterTypes();
219: Class returnType = methods[i].getReturnType();
220:
221: if (methodName.startsWith("set")
222: && methodName.length() > 3
223: && signature.length == 1
224: && returnType == Void.TYPE) {
225: String key = methodName.substring(3, methodName
226: .length());
227: Method setter = (Method) setters.get(key);
228: if (setter != null
229: && setter.getParameterTypes()[0]
230: .equals(signature[0]) == false) {
231: throw new IntrospectionException(
232: "overloaded type for attribute set: "
233: + key);
234: }
235: setters.put(key, methods[i]);
236: } else if (methodName.startsWith("get")
237: && methodName.length() > 3
238: && signature.length == 0
239: && returnType != Void.TYPE) {
240: String key = methodName.substring(3, methodName
241: .length());
242: Method getter = (Method) getters.get(key);
243: if (getter != null
244: && getter.getName().startsWith("is")) {
245: throw new IntrospectionException(
246: "mixed use of get/is for attribute "
247: + key);
248: }
249: getters.put(key, methods[i]);
250: } else if (methodName.startsWith("is")
251: && methodName.length() > 2
252: && signature.length == 0
253: && isBooleanReturn(returnType)) {
254: String key = methodName.substring(2, methodName
255: .length());
256: Method getter = (Method) getters.get(key);
257: if (getter != null
258: && getter.getName().startsWith("get")) {
259: throw new IntrospectionException(
260: "mixed use of get/is for attribute "
261: + key);
262: }
263: getters.put(key, methods[i]);
264: } else {
265: MBeanOperationInfo info = new MBeanOperationInfo(
266: "MBean Operation.", methods[i]);
267: operInfo.put(getSignatureString(methods[i]), info);
268: }
269: }
270:
271: Object[] keys = getters.keySet().toArray();
272: for (int i = 0; i < keys.length; ++i) {
273: String attrName = (String) keys[i];
274: Method getter = (Method) getters.remove(attrName);
275: Method setter = (Method) setters.remove(attrName);
276: MBeanAttributeInfo info = new MBeanAttributeInfo(
277: attrName, "MBean Attribute.", getter, setter);
278: attrInfo.add(info);
279: }
280:
281: Iterator it = setters.keySet().iterator();
282: while (it.hasNext()) {
283: String attrName = (String) it.next();
284: Method setter = (Method) setters.get(attrName);
285: MBeanAttributeInfo info = new MBeanAttributeInfo(
286: attrName, "MBean Attribute.", null, setter);
287: attrInfo.add(info);
288: }
289:
290: // save away the attribute and operation info objects
291: MBeanAttributeInfo[] attributeInfo = (MBeanAttributeInfo[]) attrInfo
292: .toArray(new MBeanAttributeInfo[0]);
293: MBeanOperationInfo[] operationInfo = (MBeanOperationInfo[]) operInfo
294: .values().toArray(new MBeanOperationInfo[0]);
295:
296: // if the builder was initialized with the resource instance, check if
297: // it is a notification broadcaster, and add the appropriate notifications
298: // to the interface.
299: MBeanNotificationInfo[] notifications = null;
300: if (mbeanInstance instanceof NotificationBroadcaster) {
301: notifications = ((NotificationBroadcaster) mbeanInstance)
302: .getNotificationInfo();
303: } else {
304: notifications = new MBeanNotificationInfo[0];
305: }
306:
307: return new MBeanInfo(mbeanClass.getName(),
308: "Management Bean.", attributeInfo, constructorInfo,
309: operationInfo, notifications);
310:
311: } catch (IntrospectionException e) {
312: throw new NotCompliantMBeanException(e.getMessage());
313: }
314: }
315:
316: /**
317: * JMX standard specifies that only "boolean isX()" style methods
318: * represent attributes. "Boolean isX()" methods map to operations.
319: */
320: private boolean isBooleanReturn(Class returnType) {
321: return returnType == Boolean.TYPE;
322: }
323:
324: protected String getSignatureString(Method method) {
325: String name = method.getName();
326: Class[] signature = method.getParameterTypes();
327: StringBuffer buffer = new StringBuffer(512);
328: buffer.append(name);
329: buffer.append("(");
330: if (signature != null) {
331: for (int i = 0; i < signature.length; i++) {
332: buffer.append(signature[i].getName());
333: if (i < signature.length - 1)
334: buffer.append(",");
335: }
336: }
337: buffer.append(")");
338: return buffer.toString();
339: }
340: }
|