24 */ |
24 */ |
25 |
25 |
26 package com.sun.jmx.mbeanserver; |
26 package com.sun.jmx.mbeanserver; |
27 |
27 |
28 import java.lang.annotation.Annotation; |
28 import java.lang.annotation.Annotation; |
|
29 import java.lang.ref.SoftReference; |
29 import java.lang.reflect.AnnotatedElement; |
30 import java.lang.reflect.AnnotatedElement; |
30 import java.lang.reflect.Constructor; |
31 import java.lang.reflect.Constructor; |
31 import java.lang.reflect.Method; |
32 import java.lang.reflect.Method; |
32 import java.lang.reflect.Modifier; |
33 import java.lang.reflect.Modifier; |
33 import java.lang.reflect.Proxy; |
34 import java.lang.reflect.Proxy; |
34 import java.lang.reflect.UndeclaredThrowableException; |
35 import java.lang.reflect.UndeclaredThrowableException; |
35 import java.util.Arrays; |
36 import java.util.Arrays; |
|
37 import java.util.Collections; |
36 import java.util.HashMap; |
38 import java.util.HashMap; |
|
39 import java.util.List; |
|
40 import java.util.LinkedList; |
|
41 import java.util.Locale; |
37 import java.util.Map; |
42 import java.util.Map; |
|
43 import java.util.WeakHashMap; |
38 |
44 |
39 import javax.management.Descriptor; |
45 import javax.management.Descriptor; |
40 import javax.management.DescriptorKey; |
46 import javax.management.DescriptorKey; |
41 import javax.management.DynamicMBean; |
47 import javax.management.DynamicMBean; |
42 import javax.management.ImmutableDescriptor; |
48 import javax.management.ImmutableDescriptor; |
504 } else if (complex instanceof CompositeData) { |
510 } else if (complex instanceof CompositeData) { |
505 return ((CompositeData) complex).get(element); |
511 return ((CompositeData) complex).get(element); |
506 } else { |
512 } else { |
507 // Java Beans introspection |
513 // Java Beans introspection |
508 // |
514 // |
509 BeanInfo bi = java.beans.Introspector.getBeanInfo(complex.getClass()); |
515 Class<?> clazz = complex.getClass(); |
510 PropertyDescriptor[] pds = bi.getPropertyDescriptors(); |
516 Method readMethod = null; |
511 for (PropertyDescriptor pd : pds) |
517 if (BeansHelper.isAvailable()) { |
512 if (pd.getName().equals(element)) |
518 Object bi = BeansHelper.getBeanInfo(clazz); |
513 return pd.getReadMethod().invoke(complex); |
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 |
514 throw new AttributeNotFoundException( |
534 throw new AttributeNotFoundException( |
515 "Could not find the getter method for the property " + |
535 "Could not find the getter method for the property " + |
516 element + " using the Java Beans introspector"); |
536 element + " using the Java Beans introspector"); |
517 } |
537 } |
518 } catch (InvocationTargetException e) { |
538 } catch (InvocationTargetException e) { |
522 } catch (Exception e) { |
542 } catch (Exception e) { |
523 throw EnvHelp.initCause( |
543 throw EnvHelp.initCause( |
524 new AttributeNotFoundException(e.getMessage()), e); |
544 new AttributeNotFoundException(e.getMessage()), e); |
525 } |
545 } |
526 } |
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 } |
527 } |
778 } |