001: /*
002: * Copyright (C) The MX4J Contributors.
003: * All rights reserved.
004: *
005: * This software is distributed under the terms of the MX4J License version 1.0.
006: * See the terms of the MX4J License in the documentation provided with this software.
007: */
008:
009: package mx4j;
010:
011: import java.lang.reflect.InvocationTargetException;
012: import java.lang.reflect.Method;
013: import java.util.Arrays;
014: import javax.management.Attribute;
015: import javax.management.AttributeList;
016: import javax.management.AttributeNotFoundException;
017: import javax.management.DynamicMBean;
018: import javax.management.InvalidAttributeValueException;
019: import javax.management.MBeanAttributeInfo;
020: import javax.management.MBeanConstructorInfo;
021: import javax.management.MBeanException;
022: import javax.management.MBeanInfo;
023: import javax.management.MBeanNotificationInfo;
024: import javax.management.MBeanOperationInfo;
025: import javax.management.MBeanParameterInfo;
026: import javax.management.ReflectionException;
027: import javax.management.RuntimeErrorException;
028: import javax.management.RuntimeMBeanException;
029:
030: import mx4j.util.Utils;
031:
032: /**
033: * Utility class that allow the user to easily write DynamicMBeans. <br>
034: * By extending this class, the developer does not have to implement the methods of the DynamicMBean interface, but
035: * has instead to provide only the metadata (by overriding few methods) and the implementation (by implementing
036: * the methods) of the MBean itself. <br>
037: * The methods to override that provides metadata information are usually the following:
038: * <ul>
039: * <li> <code>createMBeanAttributeInfo</code>, if the MBeans has manageable attributes </li>
040: * <li> <code>createMBeanOperationInfo</code>, if the MBeans has manageable operations </li>
041: * <li> <code>createMBeanNotificationInfo</code>, if the MBeans has manageable notifications </li>
042: * <li> <code>createMBeanConstructorInfo</code>, if the MBeans has manageable constructors </li>
043: * <li> <code>getMBeanDescription</code> </li>
044: * </ul>
045: * For example, the following MBean only has one manageable attribute:
046: * <pre>
047: * public class SimpleDynamic extends AbstractDynamicMBean
048: * {
049: * protected MBeanAttributeInfo[] createMBeanAttributeInfo()
050: * {
051: * return new MBeanAttributeInfo[]
052: * {
053: * new MBeanAttributeInfo("Name", String.class.getName(), "The name", true, true, false)
054: * };
055: * }
056: * <p/>
057: * protected String getMBeanDescription()
058: * {
059: * return "A simple DynamicMBean";
060: * }
061: * <p/>
062: * public String getName() { ... }
063: * <p/>
064: * public void setName(String name) { ... }
065: * }
066: * </pre>
067: * It is responsibility of the developer to specify the metadata <b>and</b> implement the methods specified by the
068: * metadata, that will be invoked via reflection by the AbstractDynamicMBean class. For this reason, the methods
069: * belonging to the MBean implementation (in the case above <code>getName()</code> and <code>setName(...)</code>)
070: * must be public.
071: *
072: * @version $Revision: 1.7 $
073: */
074: public abstract class AbstractDynamicMBean implements DynamicMBean {
075: private MBeanInfo info;
076: private Object resource;
077:
078: /**
079: * Only subclasses can create a new instance of an AbstractDynamicMBean.
080: *
081: * @see #createMBeanConstructorInfo
082: */
083: protected AbstractDynamicMBean() {
084: }
085:
086: /**
087: * Returns the value of the manageable attribute, as specified by the DynamicMBean interface.
088: *
089: * @see #createMBeanAttributeInfo
090: */
091: public Object getAttribute(String attribute)
092: throws AttributeNotFoundException, MBeanException,
093: ReflectionException {
094: if (attribute == null)
095: throw new AttributeNotFoundException("Attribute "
096: + attribute + " not found");
097:
098: Object resource = null;
099: MBeanInfo info = null;
100: synchronized (this ) {
101: resource = getResourceOrThis();
102: info = getMBeanInfo();
103: }
104:
105: MBeanAttributeInfo[] attrs = info.getAttributes();
106: if (attrs == null || attrs.length == 0)
107: throw new AttributeNotFoundException(
108: "No attributes defined for this MBean");
109:
110: for (int i = 0; i < attrs.length; ++i) {
111: MBeanAttributeInfo attr = attrs[i];
112: if (attr == null)
113: continue;
114:
115: if (attribute.equals(attr.getName())) {
116: if (!attr.isReadable())
117: throw new ReflectionException(
118: new NoSuchMethodException(
119: "No getter defined for attribute: "
120: + attribute));
121:
122: // Found, invoke via reflection
123: String prefix = null;
124: if (attr.isIs())
125: prefix = "is";
126: else
127: prefix = "get";
128:
129: try {
130: return invoke(resource, prefix + attr.getName(),
131: new Class[0], new Object[0]);
132: } catch (InvalidAttributeValueException x) {
133: throw new ReflectionException(x);
134: }
135: }
136: }
137:
138: throw new AttributeNotFoundException("Attribute " + attribute
139: + " not found");
140: }
141:
142: /**
143: * Returns the manageable attributes, as specified by the DynamicMBean interface.
144: */
145: public AttributeList getAttributes(String[] attributes) {
146: AttributeList list = new AttributeList();
147:
148: if (attributes != null) {
149: for (int i = 0; i < attributes.length; ++i) {
150: String attribute = attributes[i];
151: try {
152: Object result = getAttribute(attribute);
153: list.add(new Attribute(attribute, result));
154: } catch (AttributeNotFoundException ignored) {
155: } catch (MBeanException ignored) {
156: } catch (ReflectionException ignored) {
157: }
158: }
159: }
160:
161: return list;
162: }
163:
164: /**
165: * Returns the MBeaInfo, as specified by the DynamicMBean interface; the default implementation caches the value
166: * returned by {@link #createMBeanInfo} (that is thus called only once).
167: *
168: * @see #createMBeanInfo
169: * @see #setMBeanInfo
170: */
171: public synchronized MBeanInfo getMBeanInfo() {
172: if (info == null)
173: setMBeanInfo(createMBeanInfo());
174: return info;
175: }
176:
177: /**
178: * Returns the value of the manageable operation as specified by the DynamicMBean interface
179: *
180: * @see #createMBeanOperationInfo
181: */
182: public Object invoke(String method, Object[] arguments,
183: String[] params) throws MBeanException, ReflectionException {
184: if (method == null)
185: throw new IllegalArgumentException(
186: "Method name cannot be null");
187: if (arguments == null)
188: arguments = new Object[0];
189: if (params == null)
190: params = new String[0];
191:
192: Object resource = null;
193: MBeanInfo info = null;
194: synchronized (this ) {
195: resource = getResourceOrThis();
196: info = getMBeanInfo();
197: }
198:
199: MBeanOperationInfo[] opers = info.getOperations();
200: if (opers == null || opers.length == 0)
201: throw new ReflectionException(new NoSuchMethodException(
202: "No operations defined for this MBean"));
203:
204: for (int i = 0; i < opers.length; ++i) {
205: MBeanOperationInfo oper = opers[i];
206: if (oper == null)
207: continue;
208:
209: if (method.equals(oper.getName())) {
210: MBeanParameterInfo[] parameters = oper.getSignature();
211: if (params.length != parameters.length)
212: continue;
213:
214: String[] signature = new String[parameters.length];
215: for (int j = 0; j < signature.length; ++j) {
216: MBeanParameterInfo param = parameters[j];
217: if (param == null)
218: signature[j] = null;
219: else
220: signature[j] = param.getType();
221: }
222:
223: if (Utils.arrayEquals(params, signature)) {
224: // Found the right operation
225: try {
226: Class[] classes = Utils
227: .loadClasses(resource.getClass()
228: .getClassLoader(), signature);
229: return invoke(resource, method, classes,
230: arguments);
231: } catch (ClassNotFoundException x) {
232: throw new ReflectionException(x);
233: } catch (InvalidAttributeValueException x) {
234: throw new ReflectionException(x);
235: }
236: }
237: }
238: }
239:
240: throw new ReflectionException(new NoSuchMethodException(
241: "Operation " + method + " with signature "
242: + Arrays.asList(params)
243: + " is not defined for this MBean"));
244: }
245:
246: /**
247: * Sets the value of the manageable attribute, as specified by the DynamicMBean interface.
248: *
249: * @see #createMBeanAttributeInfo
250: */
251: public void setAttribute(Attribute attribute)
252: throws AttributeNotFoundException,
253: InvalidAttributeValueException, MBeanException,
254: ReflectionException {
255: if (attribute == null)
256: throw new AttributeNotFoundException("Attribute "
257: + attribute + " not found");
258:
259: Object resource = null;
260: MBeanInfo info = null;
261: synchronized (this ) {
262: resource = getResourceOrThis();
263: info = getMBeanInfo();
264: }
265:
266: MBeanAttributeInfo[] attrs = info.getAttributes();
267: if (attrs == null || attrs.length == 0)
268: throw new AttributeNotFoundException(
269: "No attributes defined for this MBean");
270:
271: for (int i = 0; i < attrs.length; ++i) {
272: MBeanAttributeInfo attr = attrs[i];
273: if (attr == null)
274: continue;
275:
276: if (attribute.getName().equals(attr.getName())) {
277: if (!attr.isWritable())
278: throw new ReflectionException(
279: new NoSuchMethodException(
280: "No setter defined for attribute: "
281: + attribute));
282:
283: try {
284: String signature = attr.getType();
285: Class cls = Utils.loadClass(resource.getClass()
286: .getClassLoader(), signature);
287: invoke(resource, "set" + attr.getName(),
288: new Class[] { cls },
289: new Object[] { attribute.getValue() });
290: return;
291: } catch (ClassNotFoundException x) {
292: throw new ReflectionException(x);
293: }
294: }
295: }
296:
297: throw new AttributeNotFoundException("Attribute " + attribute
298: + " not found");
299: }
300:
301: /**
302: * Sets the manageable attributes, as specified by the DynamicMBean interface.
303: */
304: public AttributeList setAttributes(AttributeList attributes) {
305: AttributeList list = new AttributeList();
306:
307: if (attributes != null) {
308: for (int i = 0; i < attributes.size(); ++i) {
309: Attribute attribute = (Attribute) attributes.get(i);
310: try {
311: setAttribute(attribute);
312: list.add(attribute);
313: } catch (AttributeNotFoundException ignored) {
314: } catch (InvalidAttributeValueException ignored) {
315: } catch (MBeanException ignored) {
316: } catch (ReflectionException ignored) {
317: }
318: }
319: }
320:
321: return list;
322: }
323:
324: /**
325: * @deprecated Replaced by {@link #invoke(Object,String,Class[],Object[])}. <br>
326: * The resource passed is the resource as set by {@link #setResource} or - if it is null - 'this' instance. <br>
327: * This method is deprecated because it is not thread safe.
328: */
329: protected Object invoke(String name, Class[] params, Object[] args)
330: throws InvalidAttributeValueException, MBeanException,
331: ReflectionException {
332: Object resource = getResourceOrThis();
333: return invoke(resource, name, params, args);
334: }
335:
336: /**
337: * Looks up the method to call on given resource and invokes it.
338: * The default implementation requires that the methods that implement attribute and operation behavior
339: * on the resource object are public, but it is possible to override this behavior, and call
340: * also private methods.
341: *
342: * @see #findMethod
343: * @see #invokeMethod
344: */
345: protected Object invoke(Object resource, String name,
346: Class[] params, Object[] args)
347: throws InvalidAttributeValueException, MBeanException,
348: ReflectionException {
349: try {
350: Class cls = resource.getClass();
351: Method method = findMethod(cls, name, params);
352: return invokeMethod(method, resource, args);
353: } catch (NoSuchMethodException x) {
354: throw new ReflectionException(x);
355: } catch (IllegalAccessException x) {
356: throw new ReflectionException(x);
357: } catch (IllegalArgumentException x) {
358: throw new InvalidAttributeValueException(x.toString());
359: } catch (InvocationTargetException x) {
360: Throwable t = x.getTargetException();
361: if (t instanceof RuntimeException)
362: throw new RuntimeMBeanException((RuntimeException) t);
363: else if (t instanceof Exception)
364: throw new MBeanException((Exception) t);
365: throw new RuntimeErrorException((Error) t);
366: }
367: }
368:
369: /**
370: * Returns the (public) method with the given name and signature on the given class. <br>
371: * Override to return non-public methods, or to map methods to other classes, or to map methods with
372: * different signatures
373: *
374: * @see #invoke(String, Class[], Object[])
375: * @see #invokeMethod
376: */
377: protected Method findMethod(Class cls, String name, Class[] params)
378: throws NoSuchMethodException {
379: return cls.getMethod(name, params);
380: }
381:
382: /**
383: * Invokes the given method on the given resource object with the given arguments. <br>
384: * Override to map methods to other objects, or to map methods with different arguments
385: *
386: * @see #invoke(String, Class[], Object[])
387: * @see #findMethod
388: */
389: protected Object invokeMethod(Method method, Object resource,
390: Object[] args) throws IllegalAccessException,
391: IllegalArgumentException, InvocationTargetException {
392: return method.invoke(resource, args);
393: }
394:
395: private Object getResourceOrThis() {
396: Object resource = getResource();
397: if (resource == null)
398: resource = this ;
399: return resource;
400: }
401:
402: /**
403: * Returns the resource object on which invoke attribute's getters, attribute's setters and operation's methods
404: *
405: * @see #setResource
406: */
407: protected synchronized Object getResource() {
408: return resource;
409: }
410:
411: /**
412: * Specifies the resource object on which invoke attribute's getters, attribute's setters and operation's methods.
413: *
414: * @see #getResource
415: */
416: public synchronized void setResource(Object resource) {
417: this .resource = resource;
418: }
419:
420: /**
421: * Sets the MBeanInfo object cached by this instance. <br>
422: * The given MBeanInfo is not cloned.
423: *
424: * @see #getMBeanInfo
425: */
426: protected synchronized void setMBeanInfo(MBeanInfo info) {
427: this .info = info;
428: }
429:
430: /**
431: * Creates the MBeanInfo for this instance, calling in succession factory methods that the user can override.
432: * Information to create MBeanInfo are taken calling the following methods:
433: * <ul>
434: * <li><code>{@link #createMBeanAttributeInfo}</code></li>
435: * <li><code>{@link #createMBeanConstructorInfo}</code></li>
436: * <li><code>{@link #createMBeanOperationInfo}</code></li>
437: * <li><code>{@link #createMBeanNotificationInfo}</code></li>
438: * <li><code>{@link #getMBeanClassName}</code></li>
439: * <li><code>{@link #getMBeanDescription}</code></li>
440: * </ul>
441: */
442: protected MBeanInfo createMBeanInfo() {
443: MBeanAttributeInfo[] attrs = createMBeanAttributeInfo();
444: MBeanConstructorInfo[] ctors = createMBeanConstructorInfo();
445: MBeanOperationInfo[] opers = createMBeanOperationInfo();
446: MBeanNotificationInfo[] notifs = createMBeanNotificationInfo();
447: String className = getMBeanClassName();
448: String description = getMBeanDescription();
449: return new MBeanInfo(className, description, attrs, ctors,
450: opers, notifs);
451: }
452:
453: /**
454: * To be overridden to return metadata information about manageable attributes.
455: */
456: protected MBeanAttributeInfo[] createMBeanAttributeInfo() {
457: return null;
458: }
459:
460: /**
461: * To be overridden to return metadata information about manageable constructors.
462: */
463: protected MBeanConstructorInfo[] createMBeanConstructorInfo() {
464: return null;
465: }
466:
467: /**
468: * To be overridden to return metadata information about manageable operations.
469: */
470: protected MBeanOperationInfo[] createMBeanOperationInfo() {
471: return null;
472: }
473:
474: /**
475: * To be overridden to return metadata information about manageable notifications.
476: */
477: protected MBeanNotificationInfo[] createMBeanNotificationInfo() {
478: return null;
479: }
480:
481: /**
482: * To be overridden to return metadata information about the class name of this MBean;
483: * by default returns this class' name.
484: */
485: protected String getMBeanClassName() {
486: return getClass().getName();
487: }
488:
489: /**
490: * To be overridden to return metadata information about the description of this MBean.
491: */
492: protected String getMBeanDescription() {
493: return null;
494: }
495: }
|