Frames | No Frames |
1: /* StandardMBean.java -- A standard reflection-based management bean. 2: Copyright (C) 2006 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package javax.management; 39: 40: import java.lang.reflect.Constructor; 41: import java.lang.reflect.InvocationTargetException; 42: import java.lang.reflect.Method; 43: 44: import java.util.ArrayList; 45: import java.util.HashMap; 46: import java.util.Iterator; 47: import java.util.List; 48: import java.util.Map; 49: 50: /** 51: * Provides a dynamic management bean by using reflection on an 52: * interface and an implementing class. By default, a bean instance 53: * is paired up with its interface based on specific naming 54: * conventions (if the implementation is called X, the interface must 55: * be XMBean). Using this class removes the need to use a specific 56: * naming system to match up the two. Instead, an instance of this 57: * bean is created either via explicit construction or subclassing, 58: * and this provides access to the attributes, constructors and 59: * operations of the implementation via reflection. Various hooks are 60: * provided in order to allow customization of this process. 61: * 62: * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 63: * @since 1.5 64: */ 65: public class StandardMBean 66: implements DynamicMBean 67: { 68: 69: /** 70: * The interface for this bean. 71: */ 72: private Class<?> iface; 73: 74: /** 75: * The implementation of the interface. 76: */ 77: private Object impl; 78: 79: /** 80: * Cached bean information. 81: */ 82: private MBeanInfo info; 83: 84: /** 85: * Constructs a new {@link StandardMBean} using the specified 86: * interface and <code>this</code> as the instance. This should 87: * be used to create an instance via subclassing. 88: * 89: * @param iface the interface this bean implements, or <code>null</code> 90: * if the interface should be determined using the naming 91: * convention (class X has interface XMBean). 92: * @throws NotCompliantMBeanException if this class doesn't implement 93: * the interface or a method appears 94: * in the interface that doesn't comply 95: * with the naming conventions. 96: */ 97: protected StandardMBean(Class<?> iface) 98: throws NotCompliantMBeanException 99: { 100: if (iface == null) 101: { 102: String className = getClass().getName(); 103: try 104: { 105: iface = Class.forName(className + "MBean"); 106: } 107: catch (ClassNotFoundException e) 108: { 109: for (Class<?> nextIface : getClass().getInterfaces()) 110: { 111: if (JMX.isMXBeanInterface(nextIface)) 112: { 113: iface = nextIface; 114: break; 115: } 116: } 117: if (iface == null) 118: throw (NotCompliantMBeanException) 119: (new NotCompliantMBeanException("An interface for the class " 120: + className + 121: " was not found.").initCause(e)); 122: } 123: } 124: if (!(iface.isInstance(this))) 125: throw new NotCompliantMBeanException("The instance, " + impl + 126: ", is not an instance of " + iface); 127: impl = this; 128: this.iface = iface; 129: } 130: 131: /** 132: * Constructs a new {@link StandardMBean} using the specified 133: * interface and the supplied instance as the implementation. 134: * 135: * @param impl the implementation. 136: * @param iface the interface the bean implements, or <code>null</code> 137: * if the interface should be determined using the naming 138: * convention (class X has interface XMBean). 139: * @throws IllegalArgumentException if <code>impl</code> is <code>null</code>. 140: * @throws NotCompliantMBeanException if <code>impl</code> doesn't implement 141: * the interface or a method appears 142: * in the interface that doesn't comply 143: * with the naming conventions. 144: */ 145: public <T> StandardMBean(T impl, Class<T> iface) 146: throws NotCompliantMBeanException 147: { 148: if (impl == null) 149: throw new IllegalArgumentException("The specified implementation is null."); 150: if (iface == null) 151: { 152: Class<?> implClass = impl.getClass(); 153: String className = implClass.getName(); 154: try 155: { 156: this.iface = Class.forName(className + "MBean", true, 157: implClass.getClassLoader()); 158: } 159: catch (ClassNotFoundException e) 160: { 161: for (Class<?> nextIface : implClass.getInterfaces()) 162: { 163: if (JMX.isMXBeanInterface(nextIface)) 164: { 165: this.iface = nextIface; 166: break; 167: } 168: } 169: if (this.iface == null) 170: throw (NotCompliantMBeanException) 171: (new NotCompliantMBeanException("An interface for the class " + 172: className + 173: " was not found.").initCause(e)); 174: } 175: } 176: else 177: this.iface = iface; 178: if (!(this.iface.isInstance(impl))) 179: throw new NotCompliantMBeanException("The instance, " + impl + 180: ", is not an instance of " + iface); 181: this.impl = impl; 182: } 183: 184: /** 185: * Caches the {@link MBeanInfo} instance for this object. This is a 186: * customization hook, so that subclasses can choose the caching policy 187: * used. The default implementation caches the value in the instance 188: * itself. Subclasses may override this so as to not cache the data 189: * at all, or so as to use a cache shared between multiple beans. 190: * 191: * @param info the {@link MBeanInfo} instance to cache, or <code>null</code> 192: * if there is no new value to cache. When the value is not 193: * <code>null</code>, the cache should replace the current value 194: * with the value supplied here. 195: * @see #getCachedMBeanInfo() 196: */ 197: protected void cacheMBeanInfo(MBeanInfo info) 198: { 199: if (info != null) 200: this.info = info; 201: } 202: 203: /** 204: * Obtains the value of the specified attribute of the 205: * management bean. The management bean should perform 206: * a lookup for the named attribute, and return its value 207: * by calling the appropriate getter method, if possible. 208: * 209: * @param name the name of the attribute to retrieve. 210: * @return the value of the specified attribute. 211: * @throws AttributeNotFoundException if the name does not 212: * correspond to an attribute 213: * of the bean. 214: * @throws MBeanException if retrieving the attribute causes 215: * the bean to throw an exception (which 216: * becomes the cause of this exception). 217: * @throws ReflectionException if an exception occurred in trying 218: * to use the reflection interface 219: * to lookup the attribute. The 220: * thrown exception is the cause of 221: * this exception. 222: * @see #setAttribute(String) 223: */ 224: public Object getAttribute(String name) 225: throws AttributeNotFoundException, MBeanException, 226: ReflectionException 227: { 228: Method getter; 229: try 230: { 231: getter = iface.getMethod("get" + name); 232: } 233: catch (NoSuchMethodException e) 234: { 235: try 236: { 237: getter = iface.getMethod("is" + name); 238: } 239: catch (NoSuchMethodException ex) 240: { 241: throw ((AttributeNotFoundException) 242: new AttributeNotFoundException("The attribute, " + name + 243: ", was not found.").initCause(ex)); 244: } 245: } 246: Object result; 247: try 248: { 249: result = getter.invoke(impl); 250: } 251: catch (IllegalAccessException e) 252: { 253: throw new ReflectionException(e, "Failed to retrieve " + name); 254: } 255: catch (IllegalArgumentException e) 256: { 257: throw new ReflectionException(e, "Failed to retrieve " + name); 258: } 259: catch (InvocationTargetException e) 260: { 261: throw new MBeanException((Exception) e.getCause(), 262: "The getter of " + name + 263: " threw an exception"); 264: } 265: return result; 266: } 267: 268: /** 269: * Obtains the values of each of the specified attributes 270: * of the management bean. The returned list includes 271: * those attributes that were retrieved and their 272: * corresponding values. 273: * 274: * @param names the names of the attributes to retrieve. 275: * @return a list of the retrieved attributes. 276: * @see #setAttributes(AttributeList) 277: */ 278: public AttributeList getAttributes(String[] names) 279: { 280: AttributeList list = new AttributeList(names.length); 281: for (int a = 0; a < names.length; ++a) 282: { 283: try 284: { 285: Object value = getAttribute(names[a]); 286: list.add(new Attribute(names[a], value)); 287: } 288: catch (AttributeNotFoundException e) 289: { 290: /* Ignored */ 291: } 292: catch (ReflectionException e) 293: { 294: /* Ignored */ 295: } 296: catch (MBeanException e) 297: { 298: /* Ignored */ 299: } 300: } 301: return list; 302: } 303: 304: /** 305: * Returns the cached {@link MBeanInfo} instance for this object. This is a 306: * customization hook, so that subclasses can choose the caching policy 307: * used. The default implementation caches the value in the instance 308: * itself, and returns this value on calls to this method. 309: * 310: * @return the cached {@link MBeanInfo} instance, or <code>null</code> 311: * if no value is cached. 312: * @see #cacheMBeanInfo(javax.management.MBeanInfo) 313: */ 314: protected MBeanInfo getCachedMBeanInfo() 315: { 316: return info; 317: } 318: 319: /** 320: * Returns the class name that will be used in the {@link MBeanInfo} 321: * instance. This is a customization hook, so that subclasses can 322: * provide a custom class name. By default, this returns the class 323: * name from the supplied {@link MBeanInfo} instance. 324: * 325: * @param info the {@link MBeanInfo} instance constructed via 326: * reflection. 327: * @return the class name to use in the instance. 328: */ 329: protected String getClassName(MBeanInfo info) 330: { 331: return info.getClassName(); 332: } 333: 334: /** 335: * Returns information on the constructors that will be used in 336: * the {@link MBeanInfo} instance. This is a customization hook, 337: * so that subclasses can provide their own information on the 338: * bean's constructors, if necessary. By default, this method 339: * returns <code>null</code> unless the implementation supplied 340: * is either <code>null</code> or <code>this</code>. This default 341: * implementation prevents the use of 342: * {@link MBeanServer#createMBean} in cases where the bean is 343: * not created as a subclass of {@link StandardMBean}. 344: * 345: * @param constructors the constructor information created via 346: * reflection. 347: * @param impl the implementation, or <code>null</code> if this 348: * should be ignored. 349: * @return the constructor information to use. 350: */ 351: protected MBeanConstructorInfo[] getConstructors(MBeanConstructorInfo[] 352: constructors, Object impl) 353: { 354: if (impl == null || impl == this) 355: return constructors; 356: return null; 357: } 358: 359: /** 360: * Returns the description of the attribute that will be used in 361: * the supplied {@link MBeanAttributeInfo} instance. This is a 362: * customization hook, so that subclasses can provide a custom 363: * description. By default, this calls 364: * {@link #getDescription(MBeanFeatureInfo)} with the supplied 365: * {@link MBeanAttributeInfo} instance. 366: * 367: * @param info the {@link MBeanAttributeInfo} instance constructed 368: * via reflection. 369: * @return the description to use in the instance. 370: */ 371: protected String getDescription(MBeanAttributeInfo info) 372: { 373: return getDescription((MBeanFeatureInfo) info); 374: } 375: 376: /** 377: * Returns the description of the constructor that will be used in 378: * the supplied {@link MBeanConstructorInfo} instance. This is a 379: * customization hook, so that subclasses can provide a custom 380: * description. By default, this calls 381: * {@link #getDescription(MBeanFeatureInfo)} with the supplied 382: * {@link MBeanConstructorInfo} instance. 383: * 384: * @param info the {@link MBeanConstructorInfo} instance constructed 385: * via reflection. 386: * @return the description to use in the instance. 387: */ 388: protected String getDescription(MBeanConstructorInfo info) 389: { 390: return getDescription((MBeanFeatureInfo) info); 391: } 392: 393: /** 394: * Returns the description of the nth parameter of the constructor 395: * that will be used in the supplied {@link MBeanParameterInfo} 396: * instance. This is a customization hook, so that subclasses 397: * can provide a custom description. By default, this calls 398: * <code>param.getDescription()</code>. 399: * 400: * @param info the {@link MBeanConstructorInfo} instance constructed 401: * via reflection. 402: * @param param the {@link MBeanParameterInfo} instance constructed 403: * via reflection. 404: * @param n the number of the parameter, in order to link it to the 405: * information on the constructor. 406: * @return the description to use in the instance. 407: */ 408: protected String getDescription(MBeanConstructorInfo info, 409: MBeanParameterInfo param, int n) 410: { 411: return param.getDescription(); 412: } 413: 414: /** 415: * Returns the description of the supplied feature that 416: * will be used in the supplied {@link MBeanFeatureInfo} 417: * instance. This is a customization hook, so that subclasses 418: * can provide a custom description. By default, this calls 419: * <code>info.getDescription()</code>. This method is also called 420: * by default for the more specific description methods for attributes, 421: * constructors and operations. 422: * 423: * @param info the {@link MBeanFeatureInfo} instance constructed 424: * via reflection. 425: * @return the description to use in the instance. 426: */ 427: protected String getDescription(MBeanFeatureInfo info) 428: { 429: return info.getDescription(); 430: } 431: 432: /** 433: * Returns the description of the bean that will be used in the 434: * supplied {@link MBeanInfo} instance. This is a customization 435: * hook, so that subclasses can provide a custom description. By 436: * default, this calls <code>info.getDescription()</code>. 437: * 438: * @param info the {@link MBeanInfo} instance constructed 439: * via reflection. 440: * @return the description to use in the instance. 441: */ 442: protected String getDescription(MBeanInfo info) 443: { 444: return info.getDescription(); 445: } 446: 447: /** 448: * Returns the description of the operation that will be used in 449: * the supplied {@link MBeanOperationInfo} instance. This is a 450: * customization hook, so that subclasses can provide a custom 451: * description. By default, this calls 452: * {@link #getDescription(MBeanFeatureInfo)} with the supplied 453: * {@link MBeanOperationInfo} instance. 454: * 455: * @param info the {@link MBeanOperationInfo} instance constructed 456: * via reflection. 457: * @return the description to use in the instance. 458: */ 459: protected String getDescription(MBeanOperationInfo info) 460: { 461: return getDescription((MBeanFeatureInfo) info); 462: } 463: 464: /** 465: * Returns the description of the nth parameter of the operation 466: * that will be used in the supplied {@link MBeanParameterInfo} 467: * instance. This is a customization hook, so that subclasses 468: * can provide a custom description. By default, this calls 469: * <code>param.getDescription()</code>. 470: * 471: * @param info the {@link MBeanOperationInfo} instance constructed 472: * via reflection. 473: * @param param the {@link MBeanParameterInfo} instance constructed 474: * via reflection. 475: * @param n the number of the parameter, in order to link it to the 476: * information on the operation. 477: * @return the description to use in the instance. 478: */ 479: protected String getDescription(MBeanOperationInfo info, 480: MBeanParameterInfo param, int n) 481: { 482: return param.getDescription(); 483: } 484: 485: /** 486: * Returns the impact of the operation that will be used in the 487: * supplied {@link MBeanOperationInfo} instance. This is a 488: * customization hook, so that subclasses can provide a custom 489: * impact flag. By default, this returns 490: * <code>info.getImpact()</code>. 491: * 492: * @param info the {@link MBeanOperationInfo} instance constructed 493: * via reflection. 494: * @return the impact flag to use in the instance. 495: */ 496: protected int getImpact(MBeanOperationInfo info) 497: { 498: return info.getImpact(); 499: } 500: 501: /** 502: * Returns the instance that implements this bean. 503: * 504: * @return the implementation. 505: */ 506: public Object getImplementation() 507: { 508: return impl; 509: } 510: 511: /** 512: * Returns the class of the instance that implements this bean. 513: * 514: * @return the implementation class. 515: */ 516: public Class<?> getImplementationClass() 517: { 518: return impl.getClass(); 519: } 520: 521: /** 522: * <p> 523: * Returns an information object which lists the attributes 524: * and actions associated with the management bean. This 525: * implementation proceeds as follows: 526: * </p> 527: * <ol> 528: * <li>{@link #getCachedMBeanInfo()} is called to obtain 529: * the cached instance. If this returns a non-null value, 530: * this value is returned.</li> 531: * <li>If there is no cached value, then the method proceeds 532: * to create one. During this process, the customization hooks 533: * detailed in this class are called to allow the values used 534: * to be overrided: 535: * <ul> 536: * <li>For each attribute, 537: * {@link #getDescription(MBeanAttributeInfo)} is called.</li> 538: * <li>For each constructor, 539: * {@link #getDescription(MBeanConstructorInfo)} is called, 540: * along with {@link #getDescription(MBeanConstructorInfo, 541: * MBeanParameterInfo, int)} and 542: * {@link #getParameterName(MBeanConstructorInfo, 543: * MBeanParameterInfo, int)} for each parameter.</li> 544: * <li>The constructors may be replaced as a whole by 545: * a call to 546: * {@link #getConstructors(MBeanConstructorInfo[], Object)}.</li> 547: * <li>For each operation, 548: * {@link #getDescription(MBeanOperationInfo)} and 549: * {@link #getImpact(MBeanOperationInfo)} are called, 550: * along with {@link #getDescription(MBeanOperationInfo, 551: * MBeanParameterInfo, int)} and 552: * {@link #getParameterName(MBeanOperationInfo, 553: * MBeanParameterInfo, int)} for each parameter.</li> 554: * <li>{@link #getClassName(MBeanInfo)} and 555: * {@link #getDescription(MBeanInfo)} are called to customise 556: * the basic information about the class.</li> 557: * </ul> 558: * </li> 559: * <li>Finally, {@link #cacheMBeanInfo(MBeanInfo)} is called 560: * with the created instance before it is returned.</li> 561: * </ol> 562: * 563: * @return a description of the management bean, including 564: * all exposed attributes and actions. 565: */ 566: public MBeanInfo getMBeanInfo() 567: { 568: MBeanInfo info = getCachedMBeanInfo(); 569: if (info != null) 570: return info; 571: Method[] methods = iface.getMethods(); 572: Map<String,Method[]> attributes = new HashMap<String,Method[]>(); 573: List<MBeanOperationInfo> operations = new ArrayList<MBeanOperationInfo>(); 574: for (int a = 0; a < methods.length; ++a) 575: { 576: String name = methods[a].getName(); 577: if (((name.startsWith("get") && 578: methods[a].getReturnType() != Void.TYPE) || 579: (name.startsWith("is") && 580: methods[a].getReturnType() == Boolean.TYPE)) && 581: methods[a].getParameterTypes().length == 0) 582: { 583: Method[] amethods; 584: String attrib; 585: if (name.startsWith("is")) 586: attrib = name.substring(2); 587: else 588: attrib = name.substring(3); 589: if (attributes.containsKey(attrib)) 590: amethods = (Method[]) attributes.get(attrib); 591: else 592: { 593: amethods = new Method[2]; 594: attributes.put(attrib, amethods); 595: } 596: amethods[0] = methods[a]; 597: } 598: else if (name.startsWith("set") && 599: methods[a].getReturnType() == Void.TYPE && 600: methods[a].getParameterTypes().length == 1) 601: { 602: Method[] amethods; 603: String attrib = name.substring(3); 604: if (attributes.containsKey(attrib)) 605: amethods = (Method[]) attributes.get(attrib); 606: else 607: { 608: amethods = new Method[2]; 609: attributes.put(attrib, amethods); 610: } 611: amethods[1] = methods[a]; 612: } 613: else 614: operations.add(new MBeanOperationInfo(methods[a].getName(), 615: methods[a])); 616: } 617: List<MBeanAttributeInfo> attribs = new ArrayList<MBeanAttributeInfo>(attributes.size()); 618: for (Map.Entry<String,Method[]> entry : attributes.entrySet()) 619: { 620: Method[] amethods = entry.getValue(); 621: try 622: { 623: attribs.add(new MBeanAttributeInfo(entry.getKey(), 624: entry.getKey(), 625: amethods[0], amethods[1])); 626: } 627: catch (IntrospectionException e) 628: { 629: /* Shouldn't happen; both shouldn't be null */ 630: throw new IllegalStateException("The two methods passed to " + 631: "the MBeanAttributeInfo " + 632: "constructor for " + entry + 633: "were null.", e); 634: } 635: } 636: MBeanAttributeInfo[] ainfo = new MBeanAttributeInfo[attribs.size()]; 637: for (int a = 0; a < ainfo.length; ++a) 638: { 639: MBeanAttributeInfo oldInfo = (MBeanAttributeInfo) attribs.get(a); 640: String desc = getDescription(oldInfo); 641: ainfo[a] = new MBeanAttributeInfo(oldInfo.getName(), 642: oldInfo.getType(), desc, 643: oldInfo.isReadable(), 644: oldInfo.isWritable(), 645: oldInfo.isIs()); 646: } 647: Constructor<?>[] cons = impl.getClass().getConstructors(); 648: MBeanConstructorInfo[] cinfo = new MBeanConstructorInfo[cons.length]; 649: for (int a = 0; a < cinfo.length; ++a) 650: { 651: MBeanConstructorInfo oldInfo = new MBeanConstructorInfo(cons[a].getName(), 652: cons[a]); 653: String desc = getDescription(oldInfo); 654: MBeanParameterInfo[] params = oldInfo.getSignature(); 655: MBeanParameterInfo[] pinfo = new MBeanParameterInfo[params.length]; 656: for (int b = 0; b < pinfo.length; ++b) 657: { 658: String pdesc = getDescription(oldInfo, params[b], b); 659: String pname = getParameterName(oldInfo, params[b], b); 660: pinfo[b] = new MBeanParameterInfo(pname, params[b].getType(), 661: pdesc); 662: } 663: cinfo[a] = new MBeanConstructorInfo(oldInfo.getName(), desc, 664: pinfo); 665: } 666: cinfo = getConstructors(cinfo, impl); 667: MBeanOperationInfo[] oinfo = new MBeanOperationInfo[operations.size()]; 668: for (int a = 0; a < oinfo.length; ++a) 669: { 670: MBeanOperationInfo oldInfo = (MBeanOperationInfo) operations.get(a); 671: String desc = getDescription(oldInfo); 672: int impact = getImpact(oldInfo); 673: MBeanParameterInfo[] params = oldInfo.getSignature(); 674: MBeanParameterInfo[] pinfo = new MBeanParameterInfo[params.length]; 675: for (int b = 0; b < pinfo.length; ++b) 676: { 677: String pdesc = getDescription(oldInfo, params[b], b); 678: String pname = getParameterName(oldInfo, params[b], b); 679: pinfo[b] = new MBeanParameterInfo(pname, params[b].getType(), 680: pdesc); 681: } 682: oinfo[a] = new MBeanOperationInfo(oldInfo.getName(), desc, pinfo, 683: oldInfo.getReturnType(), impact); 684: } 685: info = new MBeanInfo(impl.getClass().getName(), impl.getClass().getName(), 686: ainfo, cinfo, oinfo, null); 687: String cname = getClassName(info); 688: String desc = getDescription(info); 689: MBeanNotificationInfo[] ninfo = null; 690: if (impl instanceof NotificationBroadcaster) 691: ninfo = ((NotificationBroadcaster) impl).getNotificationInfo(); 692: info = new MBeanInfo(cname, desc, ainfo, cinfo, oinfo, ninfo); 693: cacheMBeanInfo(info); 694: return info; 695: } 696: 697: /** 698: * Returns the interface for this management bean. 699: * 700: * @return the management interface. 701: */ 702: public final Class<?> getMBeanInterface() 703: { 704: return iface; 705: } 706: 707: /** 708: * Returns the name of the nth parameter of the constructor 709: * that will be used in the supplied {@link MBeanParameterInfo} 710: * instance. This is a customization hook, so that subclasses 711: * can provide a custom name. By default, this calls 712: * <code>param.getName()</code>. 713: * 714: * @param info the {@link MBeanConstructorInfo} instance constructed 715: * via reflection. 716: * @param param the {@link MBeanParameterInfo} instance constructed 717: * via reflection. 718: * @param n the number of the parameter, in order to link it to the 719: * information on the constructor. 720: * @return the name to use in the instance. 721: */ 722: protected String getParameterName(MBeanConstructorInfo info, 723: MBeanParameterInfo param, int n) 724: { 725: return param.getName(); 726: } 727: 728: /** 729: * Returns the name of the nth parameter of the operation 730: * that will be used in the supplied {@link MBeanParameterInfo} 731: * instance. This is a customization hook, so that subclasses 732: * can provide a custom name. By default, this calls 733: * <code>param.getName()</code>. 734: * 735: * @param info the {@link MBeanOperationInfo} instance constructed 736: * via reflection. 737: * @param param the {@link MBeanParameterInfo} instance constructed 738: * via reflection. 739: * @param n the number of the parameter, in order to link it to the 740: * information on the operation. 741: * @return the name to use in the instance. 742: */ 743: protected String getParameterName(MBeanOperationInfo info, 744: MBeanParameterInfo param, int n) 745: { 746: return param.getName(); 747: } 748: 749: /** 750: * Invokes the specified action on the management bean using 751: * the supplied parameters. The signature of the action is 752: * specified by a {@link String} array, which lists the classes 753: * corresponding to each parameter. The class loader used to 754: * load these classes is the same as that used for loading the 755: * management bean itself. 756: * 757: * @param name the name of the action to invoke. 758: * @param params the parameters used to call the action. 759: * @param signature the signature of the action. 760: * @return the return value of the action. 761: * @throws MBeanException if the action throws an exception. The 762: * thrown exception is the cause of this 763: * exception. 764: * @throws ReflectionException if an exception occurred in trying 765: * to use the reflection interface 766: * to invoke the action. The 767: * thrown exception is the cause of 768: * this exception. 769: */ 770: public Object invoke(String name, Object[] params, String[] signature) 771: throws MBeanException, ReflectionException 772: { 773: if (name.startsWith("get") || name.startsWith("is") || 774: name.startsWith("set")) 775: throw new ReflectionException(new NoSuchMethodException(), 776: "Invocation of an attribute " + 777: "method is disallowed."); 778: ClassLoader loader = getClass().getClassLoader(); 779: Class<?>[] sigTypes; 780: if (signature != null) 781: { 782: sigTypes = new Class<?>[signature.length]; 783: for (int a = 0; a < signature.length; ++a) 784: try 785: { 786: sigTypes[a] = Class.forName(signature[a], true, loader); 787: } 788: catch (ClassNotFoundException e) 789: { 790: throw new ReflectionException(e, "The class, " + signature[a] + 791: ", in the method signature " + 792: "could not be loaded."); 793: } 794: } 795: else 796: sigTypes = null; 797: Method method; 798: try 799: { 800: method = iface.getMethod(name, sigTypes); 801: } 802: catch (NoSuchMethodException e) 803: { 804: throw new ReflectionException(e, "The method, " + name + 805: ", could not be found."); 806: } 807: Object result; 808: try 809: { 810: result = method.invoke(impl, params); 811: } 812: catch (IllegalAccessException e) 813: { 814: throw new ReflectionException(e, "Failed to call " + name); 815: } 816: catch (IllegalArgumentException e) 817: { 818: throw new ReflectionException(e, "Failed to call " + name); 819: } 820: catch (InvocationTargetException e) 821: { 822: throw new MBeanException((Exception) e.getCause(), "The method " 823: + name + " threw an exception"); 824: } 825: return result; 826: } 827: 828: /** 829: * Sets the value of the specified attribute of the 830: * management bean. The management bean should perform 831: * a lookup for the named attribute, and sets its value 832: * using the associated setter method, if possible. 833: * 834: * @param attribute the attribute to set. 835: * @throws AttributeNotFoundException if the attribute does not 836: * correspond to an attribute 837: * of the bean. 838: * @throws InvalidAttributeValueException if the value is invalid 839: * for this particular 840: * attribute of the bean. 841: * @throws MBeanException if setting the attribute causes 842: * the bean to throw an exception (which 843: * becomes the cause of this exception). 844: * @throws ReflectionException if an exception occurred in trying 845: * to use the reflection interface 846: * to lookup the attribute. The 847: * thrown exception is the cause of 848: * this exception. 849: * @see #getAttribute(String) 850: */ 851: public void setAttribute(Attribute attribute) 852: throws AttributeNotFoundException, InvalidAttributeValueException, 853: MBeanException, ReflectionException 854: { 855: String name = attribute.getName(); 856: String attName = name.substring(0, 1).toUpperCase() + name.substring(1); 857: Object val = attribute.getValue(); 858: try 859: { 860: getMutator(attName, val.getClass()).invoke(impl, new Object[] { val }); 861: } 862: catch (IllegalAccessException e) 863: { 864: throw new ReflectionException(e, "Failed to set " + name); 865: } 866: catch (IllegalArgumentException e) 867: { 868: throw ((InvalidAttributeValueException) 869: new InvalidAttributeValueException(attribute.getValue() + 870: " is an invalid value for " + 871: name).initCause(e)); 872: } 873: catch (InvocationTargetException e) 874: { 875: throw new MBeanException(e, "The getter of " + name + 876: " threw an exception"); 877: } 878: } 879: 880: /** 881: * Sets the value of each of the specified attributes 882: * to that supplied by the {@link Attribute} object. 883: * The returned list contains the attributes that were 884: * set and their new values. 885: * 886: * @param attributes the attributes to set. 887: * @return a list of the changed attributes. 888: * @see #getAttributes(AttributeList) 889: */ 890: public AttributeList setAttributes(AttributeList attributes) 891: { 892: AttributeList list = new AttributeList(attributes.size()); 893: Iterator<Object> it = attributes.iterator(); 894: while (it.hasNext()) 895: { 896: try 897: { 898: Attribute attrib = (Attribute) it.next(); 899: setAttribute(attrib); 900: list.add(attrib); 901: } 902: catch (AttributeNotFoundException e) 903: { 904: /* Ignored */ 905: } 906: catch (InvalidAttributeValueException e) 907: { 908: /* Ignored */ 909: } 910: catch (ReflectionException e) 911: { 912: /* Ignored */ 913: } 914: catch (MBeanException e) 915: { 916: /* Ignored */ 917: } 918: } 919: return list; 920: } 921: 922: /** 923: * Replaces the implementation of the interface used by this 924: * instance with the one specified. The new implementation 925: * must be non-null and implement the interface specified on 926: * construction of this instance. 927: * 928: * @throws IllegalArgumentException if <code>impl</code> is <code>null</code>. 929: * @throws NotCompliantMBeanException if <code>impl</code> doesn't implement 930: * the interface or a method appears 931: * in the interface that doesn't comply 932: * with the naming conventions. 933: */ 934: public void setImplementation(Object impl) 935: throws NotCompliantMBeanException 936: { 937: if (impl == null) 938: throw new IllegalArgumentException("The specified implementation is null."); 939: if (!(iface.isInstance(impl))) 940: throw new NotCompliantMBeanException("The instance, " + impl + 941: ", is not an instance of " + iface); 942: this.impl = impl; 943: } 944: 945: /** 946: * Returns the mutator method for a particular attribute name 947: * with a parameter type matching that of the given value. 948: * 949: * @param name the name of the attribute. 950: * @param type the type of the parameter. 951: * @return the appropriate mutator method. 952: * @throws AttributeNotFoundException if a method can't be found. 953: */ 954: private Method getMutator(String name, Class<?> type) 955: throws AttributeNotFoundException 956: { 957: String mutator = "set" + name; 958: Exception ex = null; 959: try 960: { 961: return iface.getMethod(mutator, type); 962: } 963: catch (NoSuchMethodException e) 964: { 965: /* Ignored; we'll try harder instead */ 966: ex = e; 967: } 968: /* Special cases */ 969: if (type == Boolean.class) 970: try 971: { 972: return iface.getMethod(mutator, Boolean.TYPE); 973: } 974: catch (NoSuchMethodException e) 975: { 976: throw ((AttributeNotFoundException) 977: new AttributeNotFoundException("The attribute, " + name + 978: ", was not found.").initCause(e)); 979: } 980: if (type == Byte.class) 981: try 982: { 983: return iface.getMethod(mutator, Byte.TYPE); 984: } 985: catch (NoSuchMethodException e) 986: { 987: throw ((AttributeNotFoundException) 988: new AttributeNotFoundException("The attribute, " + name + 989: ", was not found.").initCause(e)); 990: } 991: if (type == Character.class) 992: try 993: { 994: return iface.getMethod(mutator, Character.TYPE); 995: } 996: catch (NoSuchMethodException e) 997: { 998: throw ((AttributeNotFoundException) 999: new AttributeNotFoundException("The attribute, " + name + 1000: ", was not found.").initCause(e)); 1001: } 1002: if (type == Double.class) 1003: try 1004: { 1005: return iface.getMethod(mutator, Double.TYPE); 1006: } 1007: catch (NoSuchMethodException e) 1008: { 1009: throw ((AttributeNotFoundException) 1010: new AttributeNotFoundException("The attribute, " + name + 1011: ", was not found.").initCause(e)); 1012: } 1013: if (type == Float.class) 1014: try 1015: { 1016: return iface.getMethod(mutator, Float.TYPE); 1017: } 1018: catch (NoSuchMethodException e) 1019: { 1020: throw ((AttributeNotFoundException) 1021: new AttributeNotFoundException("The attribute, " + name + 1022: ", was not found.").initCause(e)); 1023: } 1024: if (type == Integer.class) 1025: try 1026: { 1027: return iface.getMethod(mutator, Integer.TYPE); 1028: } 1029: catch (NoSuchMethodException e) 1030: { 1031: throw ((AttributeNotFoundException) 1032: new AttributeNotFoundException("The attribute, " + name + 1033: ", was not found.").initCause(e)); 1034: } 1035: if (type == Long.class) 1036: try 1037: { 1038: return iface.getMethod(mutator, Long.TYPE); 1039: } 1040: catch (NoSuchMethodException e) 1041: { 1042: throw ((AttributeNotFoundException) 1043: new AttributeNotFoundException("The attribute, " + name + 1044: ", was not found.").initCause(e)); 1045: } 1046: if (type == Short.class) 1047: try 1048: { 1049: return iface.getMethod(mutator, Short.TYPE); 1050: } 1051: catch (NoSuchMethodException e) 1052: { 1053: throw ((AttributeNotFoundException) 1054: new AttributeNotFoundException("The attribute, " + name + 1055: ", was not found.").initCause(e)); 1056: } 1057: /* Superclasses and interfaces */ 1058: for (Class<?> i : type.getInterfaces()) 1059: try 1060: { 1061: return getMutator(name, i); 1062: } 1063: catch (AttributeNotFoundException e) 1064: { 1065: ex = e; 1066: } 1067: Class<?> sclass = type.getSuperclass(); 1068: if (sclass != null && sclass != Object.class) 1069: try 1070: { 1071: return getMutator(name, sclass); 1072: } 1073: catch (AttributeNotFoundException e) 1074: { 1075: ex = e; 1076: } 1077: /* If we get this far, give up */ 1078: throw ((AttributeNotFoundException) 1079: new AttributeNotFoundException("The attribute, " + name + 1080: ", was not found.").initCause(ex)); 1081: } 1082: 1083: }