1 /* 2 * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.jmx.mbeanserver; 27 28 import java.lang.annotation.Annotation; 29 import java.lang.ref.SoftReference; 30 import java.lang.reflect.AnnotatedElement; 31 import java.lang.reflect.Constructor; 32 import java.lang.reflect.Method; 33 import java.lang.reflect.Modifier; 34 import java.lang.reflect.Proxy; 35 import java.lang.reflect.UndeclaredThrowableException; 36 import java.util.Arrays; 37 import java.util.Collections; 38 import java.util.HashMap; 39 import java.util.List; 40 import java.util.LinkedList; 41 import java.util.Locale; 42 import java.util.Map; 43 import java.util.WeakHashMap; 44 45 import javax.management.Descriptor; 46 import javax.management.DescriptorKey; 47 import javax.management.DynamicMBean; 48 import javax.management.ImmutableDescriptor; 49 import javax.management.MBeanInfo; 50 import javax.management.NotCompliantMBeanException; 51 52 import com.sun.jmx.remote.util.EnvHelp; 53 import java.beans.BeanInfo; 54 import java.beans.PropertyDescriptor; 55 import java.lang.reflect.Array; 56 import java.lang.reflect.InvocationTargetException; 57 import javax.management.AttributeNotFoundException; 58 import javax.management.openmbean.CompositeData; 59 60 /** 61 * This class contains the methods for performing all the tests needed to verify 62 * that a class represents a JMX compliant MBean. 63 * 64 * @since 1.5 65 */ 66 public class Introspector { 67 68 69 /* 70 * ------------------------------------------ 71 * PRIVATE CONSTRUCTORS 72 * ------------------------------------------ 73 */ 74 75 // private constructor defined to "hide" the default public constructor 76 private Introspector() { 77 78 // ------------------------------ 79 // ------------------------------ 80 81 } 82 83 /* 84 * ------------------------------------------ 85 * PUBLIC METHODS 86 * ------------------------------------------ 87 */ 88 89 /** 90 * Tell whether a MBean of the given class is a Dynamic MBean. 91 * This method does nothing more than returning 92 * <pre> 93 * javax.management.DynamicMBean.class.isAssignableFrom(c) 94 * </pre> 95 * This method does not check for any JMX MBean compliance: 96 * <ul><li>If <code>true</code> is returned, then instances of 97 * <code>c</code> are DynamicMBean.</li> 98 * <li>If <code>false</code> is returned, then no further 99 * assumption can be made on instances of <code>c</code>. 100 * In particular, instances of <code>c</code> may, or may not 101 * be JMX standard MBeans.</li> 102 * </ul> 103 * @param c The class of the MBean under examination. 104 * @return <code>true</code> if instances of <code>c</code> are 105 * Dynamic MBeans, <code>false</code> otherwise. 106 * 107 **/ 108 public static final boolean isDynamic(final Class<?> c) { 109 // Check if the MBean implements the DynamicMBean interface 110 return javax.management.DynamicMBean.class.isAssignableFrom(c); 111 } 112 113 /** 114 * Basic method for testing that a MBean of a given class can be 115 * instantiated by the MBean server.<p> 116 * This method checks that: 117 * <ul><li>The given class is a concrete class.</li> 118 * <li>The given class exposes at least one public constructor.</li> 119 * </ul> 120 * If these conditions are not met, throws a NotCompliantMBeanException. 121 * @param c The class of the MBean we want to create. 122 * @exception NotCompliantMBeanException if the MBean class makes it 123 * impossible to instantiate the MBean from within the 124 * MBeanServer. 125 * 126 **/ 127 public static void testCreation(Class<?> c) 128 throws NotCompliantMBeanException { 129 // Check if the class is a concrete class 130 final int mods = c.getModifiers(); 131 if (Modifier.isAbstract(mods) || Modifier.isInterface(mods)) { 132 throw new NotCompliantMBeanException("MBean class must be concrete"); 133 } 134 135 // Check if the MBean has a public constructor 136 final Constructor<?>[] consList = c.getConstructors(); 137 if (consList.length == 0) { 138 throw new NotCompliantMBeanException("MBean class must have public constructor"); 139 } 140 } 141 142 public static void checkCompliance(Class<?> mbeanClass) 143 throws NotCompliantMBeanException { 144 // Is DynamicMBean? 145 // 146 if (DynamicMBean.class.isAssignableFrom(mbeanClass)) 147 return; 148 // Is Standard MBean? 149 // 150 final Exception mbeanException; 151 try { 152 getStandardMBeanInterface(mbeanClass); 153 return; 154 } catch (NotCompliantMBeanException e) { 155 mbeanException = e; 156 } 157 // Is MXBean? 158 // 159 final Exception mxbeanException; 160 try { 161 getMXBeanInterface(mbeanClass); 162 return; 163 } catch (NotCompliantMBeanException e) { 164 mxbeanException = e; 165 } 166 final String msg = 167 "MBean class " + mbeanClass.getName() + " does not implement " + 168 "DynamicMBean, and neither follows the Standard MBean conventions (" + 169 mbeanException.toString() + ") nor the MXBean conventions (" + 170 mxbeanException.toString() + ")"; 171 throw new NotCompliantMBeanException(msg); 172 } 173 174 public static <T> DynamicMBean makeDynamicMBean(T mbean) 175 throws NotCompliantMBeanException { 176 if (mbean instanceof DynamicMBean) 177 return (DynamicMBean) mbean; 178 final Class<?> mbeanClass = mbean.getClass(); 179 Class<? super T> c = null; 180 try { 181 c = Util.cast(getStandardMBeanInterface(mbeanClass)); 182 } catch (NotCompliantMBeanException e) { 183 // Ignore exception - we need to check whether 184 // mbean is an MXBean first. 185 } 186 if (c != null) 187 return new StandardMBeanSupport(mbean, c); 188 189 try { 190 c = Util.cast(getMXBeanInterface(mbeanClass)); 191 } catch (NotCompliantMBeanException e) { 192 // Ignore exception - we cannot decide whether mbean was supposed 193 // to be an MBean or an MXBean. We will call checkCompliance() 194 // to generate the appropriate exception. 195 } 196 if (c != null) 197 return new MXBeanSupport(mbean, c); 198 checkCompliance(mbeanClass); 199 throw new NotCompliantMBeanException("Not compliant"); // not reached 200 } 201 202 /** 203 * Basic method for testing if a given class is a JMX compliant MBean. 204 * 205 * @param baseClass The class to be tested 206 * 207 * @return <code>null</code> if the MBean is a DynamicMBean, 208 * the computed {@link javax.management.MBeanInfo} otherwise. 209 * @exception NotCompliantMBeanException The specified class is not a 210 * JMX compliant MBean 211 */ 212 public static MBeanInfo testCompliance(Class<?> baseClass) 213 throws NotCompliantMBeanException { 214 215 // ------------------------------ 216 // ------------------------------ 217 218 // Check if the MBean implements the MBean or the Dynamic 219 // MBean interface 220 if (isDynamic(baseClass)) 221 return null; 222 223 return testCompliance(baseClass, null); 224 } 225 226 public static void testComplianceMXBeanInterface(Class<?> interfaceClass) 227 throws NotCompliantMBeanException { 228 MXBeanIntrospector.getInstance().getAnalyzer(interfaceClass); 229 } 230 231 /** 232 * Basic method for testing if a given class is a JMX compliant 233 * Standard MBean. This method is only called by the legacy code 234 * in com.sun.management.jmx. 235 * 236 * @param baseClass The class to be tested. 237 * 238 * @param mbeanInterface the MBean interface that the class implements, 239 * or null if the interface must be determined by introspection. 240 * 241 * @return the computed {@link javax.management.MBeanInfo}. 242 * @exception NotCompliantMBeanException The specified class is not a 243 * JMX compliant Standard MBean 244 */ 245 public static synchronized MBeanInfo 246 testCompliance(final Class<?> baseClass, 247 Class<?> mbeanInterface) 248 throws NotCompliantMBeanException { 249 if (mbeanInterface == null) 250 mbeanInterface = getStandardMBeanInterface(baseClass); 251 MBeanIntrospector<?> introspector = StandardMBeanIntrospector.getInstance(); 252 return getClassMBeanInfo(introspector, baseClass, mbeanInterface); 253 } 254 255 private static <M> MBeanInfo 256 getClassMBeanInfo(MBeanIntrospector<M> introspector, 257 Class<?> baseClass, Class<?> mbeanInterface) 258 throws NotCompliantMBeanException { 259 PerInterface<M> perInterface = introspector.getPerInterface(mbeanInterface); 260 return introspector.getClassMBeanInfo(baseClass, perInterface); 261 } 262 263 /** 264 * Get the MBean interface implemented by a JMX Standard 265 * MBean class. This method is only called by the legacy 266 * code in "com.sun.management.jmx". 267 * 268 * @param baseClass The class to be tested. 269 * 270 * @return The MBean interface implemented by the MBean. 271 * Return <code>null</code> if the MBean is a DynamicMBean, 272 * or if no MBean interface is found. 273 */ 274 public static Class<?> getMBeanInterface(Class<?> baseClass) { 275 // Check if the given class implements the MBean interface 276 // or the Dynamic MBean interface 277 if (isDynamic(baseClass)) return null; 278 try { 279 return getStandardMBeanInterface(baseClass); 280 } catch (NotCompliantMBeanException e) { 281 return null; 282 } 283 } 284 285 /** 286 * Get the MBean interface implemented by a JMX Standard MBean class. 287 * 288 * @param baseClass The class to be tested. 289 * 290 * @return The MBean interface implemented by the Standard MBean. 291 * 292 * @throws NotCompliantMBeanException The specified class is 293 * not a JMX compliant Standard MBean. 294 */ 295 public static <T> Class<? super T> getStandardMBeanInterface(Class<T> baseClass) 296 throws NotCompliantMBeanException { 297 Class<? super T> current = baseClass; 298 Class<? super T> mbeanInterface = null; 299 while (current != null) { 300 mbeanInterface = 301 findMBeanInterface(current, current.getName()); 302 if (mbeanInterface != null) break; 303 current = current.getSuperclass(); 304 } 305 if (mbeanInterface != null) { 306 return mbeanInterface; 307 } else { 308 final String msg = 309 "Class " + baseClass.getName() + 310 " is not a JMX compliant Standard MBean"; 311 throw new NotCompliantMBeanException(msg); 312 } 313 } 314 315 /** 316 * Get the MXBean interface implemented by a JMX MXBean class. 317 * 318 * @param baseClass The class to be tested. 319 * 320 * @return The MXBean interface implemented by the MXBean. 321 * 322 * @throws NotCompliantMBeanException The specified class is 323 * not a JMX compliant MXBean. 324 */ 325 public static <T> Class<? super T> getMXBeanInterface(Class<T> baseClass) 326 throws NotCompliantMBeanException { 327 try { 328 return MXBeanSupport.findMXBeanInterface(baseClass); 329 } catch (Exception e) { 330 throw throwException(baseClass,e); 331 } 332 } 333 334 /* 335 * ------------------------------------------ 336 * PRIVATE METHODS 337 * ------------------------------------------ 338 */ 339 340 341 /** 342 * Try to find the MBean interface corresponding to the class aName 343 * - i.e. <i>aName</i>MBean, from within aClass and its superclasses. 344 **/ 345 private static <T> Class<? super T> findMBeanInterface( 346 Class<T> aClass, String aName) { 347 Class<? super T> current = aClass; 348 while (current != null) { 349 final Class<?>[] interfaces = current.getInterfaces(); 350 final int len = interfaces.length; 351 for (int i=0;i<len;i++) { 352 Class<? super T> inter = Util.cast(interfaces[i]); 353 inter = implementsMBean(inter, aName); 354 if (inter != null) return inter; 355 } 356 current = current.getSuperclass(); 357 } 358 return null; 359 } 360 361 public static Descriptor descriptorForElement(final AnnotatedElement elmt) { 362 if (elmt == null) 363 return ImmutableDescriptor.EMPTY_DESCRIPTOR; 364 final Annotation[] annots = elmt.getAnnotations(); 365 return descriptorForAnnotations(annots); 366 } 367 368 public static Descriptor descriptorForAnnotations(Annotation[] annots) { 369 if (annots.length == 0) 370 return ImmutableDescriptor.EMPTY_DESCRIPTOR; 371 Map<String, Object> descriptorMap = new HashMap<String, Object>(); 372 for (Annotation a : annots) { 373 Class<? extends Annotation> c = a.annotationType(); 374 Method[] elements = c.getMethods(); 375 for (Method element : elements) { 376 DescriptorKey key = element.getAnnotation(DescriptorKey.class); 377 if (key != null) { 378 String name = key.value(); 379 Object value; 380 try { 381 value = element.invoke(a); 382 } catch (RuntimeException e) { 383 // we don't expect this - except for possibly 384 // security exceptions? 385 // RuntimeExceptions shouldn't be "UndeclaredThrowable". 386 // anyway... 387 // 388 throw e; 389 } catch (Exception e) { 390 // we don't expect this 391 throw new UndeclaredThrowableException(e); 392 } 393 value = annotationToField(value); 394 Object oldValue = descriptorMap.put(name, value); 395 if (oldValue != null && !equals(oldValue, value)) { 396 final String msg = 397 "Inconsistent values for descriptor field " + name + 398 " from annotations: " + value + " :: " + oldValue; 399 throw new IllegalArgumentException(msg); 400 } 401 } 402 } 403 } 404 405 if (descriptorMap.isEmpty()) 406 return ImmutableDescriptor.EMPTY_DESCRIPTOR; 407 else 408 return new ImmutableDescriptor(descriptorMap); 409 } 410 411 /** 412 * Throws a NotCompliantMBeanException or a SecurityException. 413 * @param notCompliant the class which was under examination 414 * @param cause the raeson why NotCompliantMBeanException should 415 * be thrown. 416 * @return nothing - this method always throw an exception. 417 * The return type makes it possible to write 418 * <pre> throw throwException(clazz,cause); </pre> 419 * @throws SecurityException - if cause is a SecurityException 420 * @throws NotCompliantMBeanException otherwise. 421 **/ 422 static NotCompliantMBeanException throwException(Class<?> notCompliant, 423 Throwable cause) 424 throws NotCompliantMBeanException, SecurityException { 425 if (cause instanceof SecurityException) 426 throw (SecurityException) cause; 427 if (cause instanceof NotCompliantMBeanException) 428 throw (NotCompliantMBeanException)cause; 429 final String classname = 430 (notCompliant==null)?"null class":notCompliant.getName(); 431 final String reason = 432 (cause==null)?"Not compliant":cause.getMessage(); 433 final NotCompliantMBeanException res = 434 new NotCompliantMBeanException(classname+": "+reason); 435 res.initCause(cause); 436 throw res; 437 } 438 439 // Convert a value from an annotation element to a descriptor field value 440 // E.g. with @interface Foo {class value()} an annotation @Foo(String.class) 441 // will produce a Descriptor field value "java.lang.String" 442 private static Object annotationToField(Object x) { 443 // An annotation element cannot have a null value but never mind 444 if (x == null) 445 return null; 446 if (x instanceof Number || x instanceof String || 447 x instanceof Character || x instanceof Boolean || 448 x instanceof String[]) 449 return x; 450 // Remaining possibilities: array of primitive (e.g. int[]), 451 // enum, class, array of enum or class. 452 Class<?> c = x.getClass(); 453 if (c.isArray()) { 454 if (c.getComponentType().isPrimitive()) 455 return x; 456 Object[] xx = (Object[]) x; 457 String[] ss = new String[xx.length]; 458 for (int i = 0; i < xx.length; i++) 459 ss[i] = (String) annotationToField(xx[i]); 460 return ss; 461 } 462 if (x instanceof Class<?>) 463 return ((Class<?>) x).getName(); 464 if (x instanceof Enum<?>) 465 return ((Enum<?>) x).name(); 466 // The only other possibility is that the value is another 467 // annotation, or that the language has evolved since this code 468 // was written. We don't allow for either of those currently. 469 // If it is indeed another annotation, then x will be a proxy 470 // with an unhelpful name like $Proxy2. So we extract the 471 // proxy's interface to use that in the exception message. 472 if (Proxy.isProxyClass(c)) 473 c = c.getInterfaces()[0]; // array "can't be empty" 474 throw new IllegalArgumentException("Illegal type for annotation " + 475 "element using @DescriptorKey: " + c.getName()); 476 } 477 478 // This must be consistent with the check for duplicate field values in 479 // ImmutableDescriptor.union. But we don't expect to be called very 480 // often so this inefficient check should be enough. 481 private static boolean equals(Object x, Object y) { 482 return Arrays.deepEquals(new Object[] {x}, new Object[] {y}); 483 } 484 485 /** 486 * Returns the XXMBean interface or null if no such interface exists 487 * 488 * @param c The interface to be tested 489 * @param clName The name of the class implementing this interface 490 */ 491 private static <T> Class<? super T> implementsMBean(Class<T> c, String clName) { 492 String clMBeanName = clName + "MBean"; 493 if (c.getName().equals(clMBeanName)) { 494 return c; 495 } 496 Class<?>[] interfaces = c.getInterfaces(); 497 for (int i = 0;i < interfaces.length; i++) { 498 if (interfaces[i].getName().equals(clMBeanName)) 499 return Util.cast(interfaces[i]); 500 } 501 502 return null; 503 } 504 505 public static Object elementFromComplex(Object complex, String element) 506 throws AttributeNotFoundException { 507 try { 508 if (complex.getClass().isArray() && element.equals("length")) { 509 return Array.getLength(complex); 510 } else if (complex instanceof CompositeData) { 511 return ((CompositeData) complex).get(element); 512 } else { 513 // Java Beans introspection 514 // 515 Class<?> clazz = complex.getClass(); 516 Method readMethod = null; 517 if (BeansHelper.isAvailable()) { 518 Object bi = BeansHelper.getBeanInfo(clazz); 519 Object[] pds = BeansHelper.getPropertyDescriptors(bi); 520 for (Object pd: pds) { 521 if (BeansHelper.getPropertyName(pd).equals(element)) { 522 readMethod = BeansHelper.getReadMethod(pd); 523 break; 524 } 525 } 526 } else { 527 // Java Beans not available so use simple introspection 528 // to locate method 529 readMethod = SimpleIntrospector.getReadMethod(clazz, element); 530 } 531 if (readMethod != null) 532 return readMethod.invoke(complex); 533 534 throw new AttributeNotFoundException( 535 "Could not find the getter method for the property " + 536 element + " using the Java Beans introspector"); 537 } 538 } catch (InvocationTargetException e) { 539 throw new IllegalArgumentException(e); 540 } catch (AttributeNotFoundException e) { 541 throw e; 542 } catch (Exception e) { 543 throw EnvHelp.initCause( 544 new AttributeNotFoundException(e.getMessage()), e); 545 } 546 } 547 548 /** 549 * A simple introspector that uses reflection to analyze a class and 550 * identify its "getter" methods. This class is intended for use only when 551 * Java Beans is not present (which implies that there isn't explicit 552 * information about the bean available). 553 */ 554 private static class SimpleIntrospector { 555 private SimpleIntrospector() { } 556 557 private static final String GET_METHOD_PREFIX = "get"; 558 private static final String IS_METHOD_PREFIX = "is"; 559 560 // cache to avoid repeated lookups 561 private static final Map<Class<?>,SoftReference<List<Method>>> cache = 562 Collections.synchronizedMap( 563 new WeakHashMap<Class<?>,SoftReference<List<Method>>> ()); 564 565 /** 566 * Returns the list of methods cached for the given class, or {@code null} 567 * if not cached. 568 */ 569 private static List<Method> getCachedMethods(Class<?> clazz) { 570 // return cached methods if possible 571 SoftReference<List<Method>> ref = cache.get(clazz); 572 if (ref != null) { 573 List<Method> cached = ref.get(); 574 if (cached != null) 575 return cached; 576 } 577 return null; 578 } 579 580 /** 581 * Returns {@code true} if the given method is a "getter" method (where 582 * "getter" method is a public method of the form getXXX or "boolean 583 * isXXX") 584 */ 585 static boolean isReadMethod(Method method) { 586 // ignore static methods 587 int modifiers = method.getModifiers(); 588 if (Modifier.isStatic(modifiers)) 589 return false; 590 591 String name = method.getName(); 592 Class<?>[] paramTypes = method.getParameterTypes(); 593 int paramCount = paramTypes.length; 594 595 if (paramCount == 0 && name.length() > 2) { 596 // boolean isXXX() 597 if (name.startsWith(IS_METHOD_PREFIX)) 598 return (method.getReturnType() == boolean.class); 599 // getXXX() 600 if (name.length() > 3 && name.startsWith(GET_METHOD_PREFIX)) 601 return (method.getReturnType() != void.class); 602 } 603 return false; 604 } 605 606 /** 607 * Returns the list of "getter" methods for the given class. The list 608 * is ordered so that isXXX methods appear before getXXX methods - this 609 * is for compatability with the JavaBeans Introspector. 610 */ 611 static List<Method> getReadMethods(Class<?> clazz) { 612 // return cached result if available 613 List<Method> cachedResult = getCachedMethods(clazz); 614 if (cachedResult != null) 615 return cachedResult; 616 617 // get list of public methods, filtering out methods that have 618 // been overridden to return a more specific type. 619 List<Method> methods = 620 StandardMBeanIntrospector.getInstance().getMethods(clazz); 621 methods = MBeanAnalyzer.eliminateCovariantMethods(methods); 622 623 // filter out the non-getter methods 624 List<Method> result = new LinkedList<Method>(); 625 for (Method m: methods) { 626 if (isReadMethod(m)) { 627 // favor isXXX over getXXX 628 if (m.getName().startsWith(IS_METHOD_PREFIX)) { 629 result.add(0, m); 630 } else { 631 result.add(m); 632 } 633 } 634 } 635 636 // add result to cache 637 cache.put(clazz, new SoftReference<List<Method>>(result)); 638 639 return result; 640 } 641 642 /** 643 * Returns the "getter" to read the given property from the given class or 644 * {@code null} if no method is found. 645 */ 646 static Method getReadMethod(Class<?> clazz, String property) { 647 // first character in uppercase (compatability with JavaBeans) 648 property = property.substring(0, 1).toUpperCase(Locale.ENGLISH) + 649 property.substring(1); 650 String getMethod = GET_METHOD_PREFIX + property; 651 String isMethod = IS_METHOD_PREFIX + property; 652 for (Method m: getReadMethods(clazz)) { 653 String name = m.getName(); 654 if (name.equals(isMethod) || name.equals(getMethod)) { 655 return m; 656 } 657 } 658 return null; 659 } 660 } 661 662 /** 663 * A class that provides access to the JavaBeans Introspector and 664 * PropertyDescriptors without creating a static dependency on java.beans. 665 */ 666 private static class BeansHelper { 667 private static final Class<?> introspectorClass = 668 getClass("java.beans.Introspector"); 669 private static final Class<?> beanInfoClass = 670 (introspectorClass == null) ? null : getClass("java.beans.BeanInfo"); 671 private static final Class<?> getPropertyDescriptorClass = 672 (beanInfoClass == null) ? null : getClass("java.beans.PropertyDescriptor"); 673 674 private static final Method getBeanInfo = 675 getMethod(introspectorClass, "getBeanInfo", Class.class); 676 private static final Method getPropertyDescriptors = 677 getMethod(beanInfoClass, "getPropertyDescriptors"); 678 private static final Method getPropertyName = 679 getMethod(getPropertyDescriptorClass, "getName"); 680 private static final Method getReadMethod = 681 getMethod(getPropertyDescriptorClass, "getReadMethod"); 682 683 private static Class<?> getClass(String name) { 684 try { 685 return Class.forName(name, true, null); 686 } catch (ClassNotFoundException e) { 687 return null; 688 } 689 } 690 private static Method getMethod(Class<?> clazz, 691 String name, 692 Class<?>... paramTypes) 693 { 694 if (clazz != null) { 695 try { 696 return clazz.getMethod(name, paramTypes); 697 } catch (NoSuchMethodException e) { 698 throw new AssertionError(e); 699 } 700 } else { 701 return null; 702 } 703 } 704 705 private BeansHelper() { } 706 707 /** 708 * Returns {@code true} if java.beans is available. 709 */ 710 static boolean isAvailable() { 711 return introspectorClass != null; 712 } 713 714 /** 715 * Invokes java.beans.Introspector.getBeanInfo(Class) 716 */ 717 static Object getBeanInfo(Class<?> clazz) throws Exception { 718 try { 719 return getBeanInfo.invoke(null, clazz); 720 } catch (InvocationTargetException e) { 721 Throwable cause = e.getCause(); 722 if (cause instanceof Exception) 723 throw (Exception)cause; 724 throw new AssertionError(e); 725 } catch (IllegalAccessException iae) { 726 throw new AssertionError(iae); 727 } 728 } 729 730 /** 731 * Invokes java.beans.BeanInfo.getPropertyDescriptors() 732 */ 733 static Object[] getPropertyDescriptors(Object bi) { 734 try { 735 return (Object[])getPropertyDescriptors.invoke(bi); 736 } catch (InvocationTargetException e) { 737 Throwable cause = e.getCause(); 738 if (cause instanceof RuntimeException) 739 throw (RuntimeException)cause; 740 throw new AssertionError(e); 741 } catch (IllegalAccessException iae) { 742 throw new AssertionError(iae); 743 } 744 } 745 746 /** 747 * Invokes java.beans.PropertyDescriptor.getName() 748 */ 749 static String getPropertyName(Object pd) { 750 try { 751 return (String)getPropertyName.invoke(pd); 752 } catch (InvocationTargetException e) { 753 Throwable cause = e.getCause(); 754 if (cause instanceof RuntimeException) 755 throw (RuntimeException)cause; 756 throw new AssertionError(e); 757 } catch (IllegalAccessException iae) { 758 throw new AssertionError(iae); 759 } 760 } 761 762 /** 763 * Invokes java.beans.PropertyDescriptor.getReadMethod() 764 */ 765 static Method getReadMethod(Object pd) { 766 try { 767 return (Method)getReadMethod.invoke(pd); 768 } catch (InvocationTargetException e) { 769 Throwable cause = e.getCause(); 770 if (cause instanceof RuntimeException) 771 throw (RuntimeException)cause; 772 throw new AssertionError(e); 773 } catch (IllegalAccessException iae) { 774 throw new AssertionError(iae); 775 } 776 } 777 } 778 }