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