001 /* StandardMBean.java -- A standard reflection-based management bean. 002 Copyright (C) 2006 Free Software Foundation, Inc. 003 004 This file is part of GNU Classpath. 005 006 GNU Classpath is free software; you can redistribute it and/or modify 007 it under the terms of the GNU General Public License as published by 008 the Free Software Foundation; either version 2, or (at your option) 009 any later version. 010 011 GNU Classpath is distributed in the hope that it will be useful, but 012 WITHOUT ANY WARRANTY; without even the implied warranty of 013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 General Public License for more details. 015 016 You should have received a copy of the GNU General Public License 017 along with GNU Classpath; see the file COPYING. If not, write to the 018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 02110-1301 USA. 020 021 Linking this library statically or dynamically with other modules is 022 making a combined work based on this library. Thus, the terms and 023 conditions of the GNU General Public License cover the whole 024 combination. 025 026 As a special exception, the copyright holders of this library give you 027 permission to link this library with independent modules to produce an 028 executable, regardless of the license terms of these independent 029 modules, and to copy and distribute the resulting executable under 030 terms of your choice, provided that you also meet, for each linked 031 independent module, the terms and conditions of the license of that 032 module. An independent module is a module which is not derived from 033 or based on this library. If you modify this library, you may extend 034 this exception to your version of the library, but you are not 035 obligated to do so. If you do not wish to do so, delete this 036 exception statement from your version. */ 037 038 package javax.management; 039 040 import java.lang.reflect.Constructor; 041 import java.lang.reflect.InvocationTargetException; 042 import java.lang.reflect.Method; 043 044 import java.util.ArrayList; 045 import java.util.HashMap; 046 import java.util.Iterator; 047 import java.util.List; 048 import java.util.Map; 049 050 /** 051 * Provides a dynamic management bean by using reflection on an 052 * interface and an implementing class. By default, a bean instance 053 * is paired up with its interface based on specific naming 054 * conventions (if the implementation is called X, the interface must 055 * be XMBean). Using this class removes the need to use a specific 056 * naming system to match up the two. Instead, an instance of this 057 * bean is created either via explicit construction or subclassing, 058 * and this provides access to the attributes, constructors and 059 * operations of the implementation via reflection. Various hooks are 060 * provided in order to allow customization of this process. 061 * 062 * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 063 * @since 1.5 064 */ 065 public class StandardMBean 066 implements DynamicMBean 067 { 068 069 /** 070 * The interface for this bean. 071 */ 072 private Class<?> iface; 073 074 /** 075 * The implementation of the interface. 076 */ 077 private Object impl; 078 079 /** 080 * Cached bean information. 081 */ 082 private MBeanInfo info; 083 084 /** 085 * Constructs a new {@link StandardMBean} using the specified 086 * interface and <code>this</code> as the instance. This should 087 * be used to create an instance via subclassing. 088 * 089 * @param iface the interface this bean implements, or <code>null</code> 090 * if the interface should be determined using the naming 091 * convention (class X has interface XMBean). 092 * @throws NotCompliantMBeanException if this class doesn't implement 093 * the interface or a method appears 094 * in the interface that doesn't comply 095 * with the naming conventions. 096 */ 097 protected StandardMBean(Class<?> iface) 098 throws NotCompliantMBeanException 099 { 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, null); 232 } 233 catch (NoSuchMethodException e) 234 { 235 try 236 { 237 getter = iface.getMethod("is" + name, null); 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, null); 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 attributes = new HashMap(); 573 List operations = new ArrayList(); 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 attribs = new ArrayList(attributes.size()); 618 Iterator it = attributes.entrySet().iterator(); 619 while (it.hasNext()) 620 { 621 Map.Entry entry = (Map.Entry) it.next(); 622 Method[] amethods = (Method[]) entry.getValue(); 623 try 624 { 625 attribs.add(new MBeanAttributeInfo((String) entry.getKey(), 626 (String) entry.getKey(), 627 amethods[0], amethods[1])); 628 } 629 catch (IntrospectionException e) 630 { 631 /* Shouldn't happen; both shouldn't be null */ 632 throw new IllegalStateException("The two methods passed to " + 633 "the MBeanAttributeInfo " + 634 "constructor for " + entry + 635 "were null.", e); 636 } 637 } 638 MBeanAttributeInfo[] ainfo = new MBeanAttributeInfo[attribs.size()]; 639 for (int a = 0; a < ainfo.length; ++a) 640 { 641 MBeanAttributeInfo oldInfo = (MBeanAttributeInfo) attribs.get(a); 642 String desc = getDescription(oldInfo); 643 ainfo[a] = new MBeanAttributeInfo(oldInfo.getName(), 644 oldInfo.getType(), desc, 645 oldInfo.isReadable(), 646 oldInfo.isWritable(), 647 oldInfo.isIs()); 648 } 649 Constructor[] cons = impl.getClass().getConstructors(); 650 MBeanConstructorInfo[] cinfo = new MBeanConstructorInfo[cons.length]; 651 for (int a = 0; a < cinfo.length; ++a) 652 { 653 MBeanConstructorInfo oldInfo = new MBeanConstructorInfo(cons[a].getName(), 654 cons[a]); 655 String desc = getDescription(oldInfo); 656 MBeanParameterInfo[] params = oldInfo.getSignature(); 657 MBeanParameterInfo[] pinfo = new MBeanParameterInfo[params.length]; 658 for (int b = 0; b < pinfo.length; ++b) 659 { 660 String pdesc = getDescription(oldInfo, params[b], b); 661 String pname = getParameterName(oldInfo, params[b], b); 662 pinfo[b] = new MBeanParameterInfo(pname, params[b].getType(), 663 pdesc); 664 } 665 cinfo[a] = new MBeanConstructorInfo(oldInfo.getName(), desc, 666 pinfo); 667 } 668 cinfo = getConstructors(cinfo, impl); 669 MBeanOperationInfo[] oinfo = new MBeanOperationInfo[operations.size()]; 670 for (int a = 0; a < oinfo.length; ++a) 671 { 672 MBeanOperationInfo oldInfo = (MBeanOperationInfo) operations.get(a); 673 String desc = getDescription(oldInfo); 674 int impact = getImpact(oldInfo); 675 MBeanParameterInfo[] params = oldInfo.getSignature(); 676 MBeanParameterInfo[] pinfo = new MBeanParameterInfo[params.length]; 677 for (int b = 0; b < pinfo.length; ++b) 678 { 679 String pdesc = getDescription(oldInfo, params[b], b); 680 String pname = getParameterName(oldInfo, params[b], b); 681 pinfo[b] = new MBeanParameterInfo(pname, params[b].getType(), 682 pdesc); 683 } 684 oinfo[a] = new MBeanOperationInfo(oldInfo.getName(), desc, pinfo, 685 oldInfo.getReturnType(), impact); 686 } 687 info = new MBeanInfo(impl.getClass().getName(), impl.getClass().getName(), 688 ainfo, cinfo, oinfo, null); 689 String cname = getClassName(info); 690 String desc = getDescription(info); 691 MBeanNotificationInfo[] ninfo = null; 692 if (impl instanceof NotificationBroadcaster) 693 ninfo = ((NotificationBroadcaster) impl).getNotificationInfo(); 694 info = new MBeanInfo(cname, desc, ainfo, cinfo, oinfo, ninfo); 695 cacheMBeanInfo(info); 696 return info; 697 } 698 699 /** 700 * Returns the interface for this management bean. 701 * 702 * @return the management interface. 703 */ 704 public final Class<?> getMBeanInterface() 705 { 706 return iface; 707 } 708 709 /** 710 * Returns the name of the nth parameter of the constructor 711 * that will be used in the supplied {@link MBeanParameterInfo} 712 * instance. This is a customization hook, so that subclasses 713 * can provide a custom name. By default, this calls 714 * <code>param.getName()</code>. 715 * 716 * @param info the {@link MBeanConstructorInfo} instance constructed 717 * via reflection. 718 * @param param the {@link MBeanParameterInfo} instance constructed 719 * via reflection. 720 * @param n the number of the parameter, in order to link it to the 721 * information on the constructor. 722 * @return the name to use in the instance. 723 */ 724 protected String getParameterName(MBeanConstructorInfo info, 725 MBeanParameterInfo param, int n) 726 { 727 return param.getName(); 728 } 729 730 /** 731 * Returns the name of the nth parameter of the operation 732 * that will be used in the supplied {@link MBeanParameterInfo} 733 * instance. This is a customization hook, so that subclasses 734 * can provide a custom name. By default, this calls 735 * <code>param.getName()</code>. 736 * 737 * @param info the {@link MBeanOperationInfo} instance constructed 738 * via reflection. 739 * @param param the {@link MBeanParameterInfo} instance constructed 740 * via reflection. 741 * @param n the number of the parameter, in order to link it to the 742 * information on the operation. 743 * @return the name to use in the instance. 744 */ 745 protected String getParameterName(MBeanOperationInfo info, 746 MBeanParameterInfo param, int n) 747 { 748 return param.getName(); 749 } 750 751 /** 752 * Invokes the specified action on the management bean using 753 * the supplied parameters. The signature of the action is 754 * specified by a {@link String} array, which lists the classes 755 * corresponding to each parameter. The class loader used to 756 * load these classes is the same as that used for loading the 757 * management bean itself. 758 * 759 * @param name the name of the action to invoke. 760 * @param params the parameters used to call the action. 761 * @param signature the signature of the action. 762 * @return the return value of the action. 763 * @throws MBeanException if the action throws an exception. The 764 * thrown exception is the cause of this 765 * exception. 766 * @throws ReflectionException if an exception occurred in trying 767 * to use the reflection interface 768 * to invoke the action. The 769 * thrown exception is the cause of 770 * this exception. 771 */ 772 public Object invoke(String name, Object[] params, String[] signature) 773 throws MBeanException, ReflectionException 774 { 775 if (name.startsWith("get") || name.startsWith("is") || 776 name.startsWith("set")) 777 throw new ReflectionException(new NoSuchMethodException(), 778 "Invocation of an attribute " + 779 "method is disallowed."); 780 ClassLoader loader = getClass().getClassLoader(); 781 Class[] sigTypes; 782 if (signature != null) 783 { 784 sigTypes = new Class[signature.length]; 785 for (int a = 0; a < signature.length; ++a) 786 try 787 { 788 sigTypes[a] = Class.forName(signature[a], true, loader); 789 } 790 catch (ClassNotFoundException e) 791 { 792 throw new ReflectionException(e, "The class, " + signature[a] + 793 ", in the method signature " + 794 "could not be loaded."); 795 } 796 } 797 else 798 sigTypes = null; 799 Method method; 800 try 801 { 802 method = iface.getMethod(name, sigTypes); 803 } 804 catch (NoSuchMethodException e) 805 { 806 throw new ReflectionException(e, "The method, " + name + 807 ", could not be found."); 808 } 809 Object result; 810 try 811 { 812 result = method.invoke(impl, params); 813 } 814 catch (IllegalAccessException e) 815 { 816 throw new ReflectionException(e, "Failed to call " + name); 817 } 818 catch (IllegalArgumentException e) 819 { 820 throw new ReflectionException(e, "Failed to call " + name); 821 } 822 catch (InvocationTargetException e) 823 { 824 throw new MBeanException((Exception) e.getCause(), "The method " 825 + name + " threw an exception"); 826 } 827 return result; 828 } 829 830 /** 831 * Sets the value of the specified attribute of the 832 * management bean. The management bean should perform 833 * a lookup for the named attribute, and sets its value 834 * using the associated setter method, if possible. 835 * 836 * @param attribute the attribute to set. 837 * @throws AttributeNotFoundException if the attribute does not 838 * correspond to an attribute 839 * of the bean. 840 * @throws InvalidAttributeValueException if the value is invalid 841 * for this particular 842 * attribute of the bean. 843 * @throws MBeanException if setting the attribute causes 844 * the bean to throw an exception (which 845 * becomes the cause of this exception). 846 * @throws ReflectionException if an exception occurred in trying 847 * to use the reflection interface 848 * to lookup the attribute. The 849 * thrown exception is the cause of 850 * this exception. 851 * @see #getAttribute(String) 852 */ 853 public void setAttribute(Attribute attribute) 854 throws AttributeNotFoundException, InvalidAttributeValueException, 855 MBeanException, ReflectionException 856 { 857 String name = attribute.getName(); 858 String attName = name.substring(0, 1).toUpperCase() + name.substring(1); 859 Object val = attribute.getValue(); 860 try 861 { 862 getMutator(attName, val.getClass()).invoke(impl, new Object[] { val }); 863 } 864 catch (IllegalAccessException e) 865 { 866 throw new ReflectionException(e, "Failed to set " + name); 867 } 868 catch (IllegalArgumentException e) 869 { 870 throw ((InvalidAttributeValueException) 871 new InvalidAttributeValueException(attribute.getValue() + 872 " is an invalid value for " + 873 name).initCause(e)); 874 } 875 catch (InvocationTargetException e) 876 { 877 throw new MBeanException(e, "The getter of " + name + 878 " threw an exception"); 879 } 880 } 881 882 /** 883 * Sets the value of each of the specified attributes 884 * to that supplied by the {@link Attribute} object. 885 * The returned list contains the attributes that were 886 * set and their new values. 887 * 888 * @param attributes the attributes to set. 889 * @return a list of the changed attributes. 890 * @see #getAttributes(AttributeList) 891 */ 892 public AttributeList setAttributes(AttributeList attributes) 893 { 894 AttributeList list = new AttributeList(attributes.size()); 895 Iterator it = attributes.iterator(); 896 while (it.hasNext()) 897 { 898 try 899 { 900 Attribute attrib = (Attribute) it.next(); 901 setAttribute(attrib); 902 list.add(attrib); 903 } 904 catch (AttributeNotFoundException e) 905 { 906 /* Ignored */ 907 } 908 catch (InvalidAttributeValueException e) 909 { 910 /* Ignored */ 911 } 912 catch (ReflectionException e) 913 { 914 /* Ignored */ 915 } 916 catch (MBeanException e) 917 { 918 /* Ignored */ 919 } 920 } 921 return list; 922 } 923 924 /** 925 * Replaces the implementation of the interface used by this 926 * instance with the one specified. The new implementation 927 * must be non-null and implement the interface specified on 928 * construction of this instance. 929 * 930 * @throws IllegalArgumentException if <code>impl</code> is <code>null</code>. 931 * @throws NotCompliantMBeanException if <code>impl</code> doesn't implement 932 * the interface or a method appears 933 * in the interface that doesn't comply 934 * with the naming conventions. 935 */ 936 public void setImplementation(Object impl) 937 throws NotCompliantMBeanException 938 { 939 if (impl == null) 940 throw new IllegalArgumentException("The specified implementation is null."); 941 if (!(iface.isInstance(impl))) 942 throw new NotCompliantMBeanException("The instance, " + impl + 943 ", is not an instance of " + iface); 944 this.impl = impl; 945 } 946 947 /** 948 * Returns the mutator method for a particular attribute name 949 * with a parameter type matching that of the given value. 950 * 951 * @param name the name of the attribute. 952 * @param type the type of the parameter. 953 * @return the appropriate mutator method. 954 * @throws AttributeNotFoundException if a method can't be found. 955 */ 956 private Method getMutator(String name, Class<?> type) 957 throws AttributeNotFoundException 958 { 959 String mutator = "set" + name; 960 Exception ex = null; 961 try 962 { 963 return iface.getMethod(mutator, type); 964 } 965 catch (NoSuchMethodException e) 966 { 967 /* Ignored; we'll try harder instead */ 968 ex = e; 969 } 970 /* Special cases */ 971 if (type == Boolean.class) 972 try 973 { 974 return iface.getMethod(mutator, Boolean.TYPE); 975 } 976 catch (NoSuchMethodException e) 977 { 978 throw ((AttributeNotFoundException) 979 new AttributeNotFoundException("The attribute, " + name + 980 ", was not found.").initCause(e)); 981 } 982 if (type == Byte.class) 983 try 984 { 985 return iface.getMethod(mutator, Byte.TYPE); 986 } 987 catch (NoSuchMethodException e) 988 { 989 throw ((AttributeNotFoundException) 990 new AttributeNotFoundException("The attribute, " + name + 991 ", was not found.").initCause(e)); 992 } 993 if (type == Character.class) 994 try 995 { 996 return iface.getMethod(mutator, Character.TYPE); 997 } 998 catch (NoSuchMethodException e) 999 { 1000 throw ((AttributeNotFoundException) 1001 new AttributeNotFoundException("The attribute, " + name + 1002 ", was not found.").initCause(e)); 1003 } 1004 if (type == Double.class) 1005 try 1006 { 1007 return iface.getMethod(mutator, Double.TYPE); 1008 } 1009 catch (NoSuchMethodException e) 1010 { 1011 throw ((AttributeNotFoundException) 1012 new AttributeNotFoundException("The attribute, " + name + 1013 ", was not found.").initCause(e)); 1014 } 1015 if (type == Float.class) 1016 try 1017 { 1018 return iface.getMethod(mutator, Float.TYPE); 1019 } 1020 catch (NoSuchMethodException e) 1021 { 1022 throw ((AttributeNotFoundException) 1023 new AttributeNotFoundException("The attribute, " + name + 1024 ", was not found.").initCause(e)); 1025 } 1026 if (type == Integer.class) 1027 try 1028 { 1029 return iface.getMethod(mutator, Integer.TYPE); 1030 } 1031 catch (NoSuchMethodException e) 1032 { 1033 throw ((AttributeNotFoundException) 1034 new AttributeNotFoundException("The attribute, " + name + 1035 ", was not found.").initCause(e)); 1036 } 1037 if (type == Long.class) 1038 try 1039 { 1040 return iface.getMethod(mutator, Long.TYPE); 1041 } 1042 catch (NoSuchMethodException e) 1043 { 1044 throw ((AttributeNotFoundException) 1045 new AttributeNotFoundException("The attribute, " + name + 1046 ", was not found.").initCause(e)); 1047 } 1048 if (type == Short.class) 1049 try 1050 { 1051 return iface.getMethod(mutator, Short.TYPE); 1052 } 1053 catch (NoSuchMethodException e) 1054 { 1055 throw ((AttributeNotFoundException) 1056 new AttributeNotFoundException("The attribute, " + name + 1057 ", was not found.").initCause(e)); 1058 } 1059 /* Superclasses and interfaces */ 1060 for (Class<?> i : type.getInterfaces()) 1061 try 1062 { 1063 return getMutator(name, i); 1064 } 1065 catch (AttributeNotFoundException e) 1066 { 1067 ex = e; 1068 } 1069 Class<?> sclass = type.getSuperclass(); 1070 if (sclass != null && sclass != Object.class) 1071 try 1072 { 1073 return getMutator(name, sclass); 1074 } 1075 catch (AttributeNotFoundException e) 1076 { 1077 ex = e; 1078 } 1079 /* If we get this far, give up */ 1080 throw ((AttributeNotFoundException) 1081 new AttributeNotFoundException("The attribute, " + name + 1082 ", was not found.").initCause(ex)); 1083 } 1084 1085 }