001    package patterntesting.runtime.jmx;
002    
003    import java.lang.reflect.Method;
004    import java.util.*;
005    
006    import javax.management.*;
007    
008    /**
009     * The Class AnnotatedStandardMBean.
010     *
011     * @author ninthbit
012     *
013     * StandardMBeans have meaningless default values with can be overcome
014     * subclassing and annotations. This classed is based on the article
015     * http://weblogs.java.net/blog/emcmanus/archive/2005/07/adding_informat.html
016     */
017    public class AnnotatedStandardMBean extends StandardMBean {
018    
019            /**
020             * Instance where the MBean interface is implemented by another object.
021             *
022             * @param <T> not necessary but occasionally avoids compiler warnings about generics
023             * @param impl Class which implements an MBean interface
024             * @param mbeanInterface MBean interface
025             * @throws NotCompliantMBeanException thrown if MBean is not JMX compliant
026             */
027            public <T> AnnotatedStandardMBean(final T impl, final Class<T> mbeanInterface)
028                            throws NotCompliantMBeanException {
029                    super(impl, mbeanInterface);
030            }
031    
032            /** Instance where the MBean interface is implemented by this object.
033             * @param mbeanInterface MBean interface
034             * @throws NotCompliantMBeanException thrown if MBean is not JMX compliant*/
035            protected AnnotatedStandardMBean(final Class<?> mbeanInterface)
036                            throws NotCompliantMBeanException {
037                    super(mbeanInterface);
038            }
039    
040            /**
041             * Overrides the default description with the content
042             * a @Description annotation.
043             *
044             * @param op the op
045             * @return the description
046             * @see patterntesting.runtime.jmx.Description Description
047             */
048            @Override
049            protected String getDescription(final MBeanOperationInfo op) {
050            String descr = op.getDescription();
051            Method m = methodFor(getMBeanInterface(), op);
052            return getDescriptionForMethod(descr, m);
053            }
054    
055            /**
056             * Gets the description.
057             *
058             * @param info the info
059             * @return the description
060             * @see javax.management.StandardMBean#getDescription(javax.management.MBeanAttributeInfo)
061             */
062            @Override
063            protected String getDescription(final MBeanAttributeInfo info) {
064                    String description = info.getDescription();
065                    Method m = getMethodForAttribute(info);
066                    return getDescriptionForMethod(description, m);
067            }
068    
069            private String getDescriptionForMethod(String descr, final Method m) {
070                    if (m != null) {
071                Description d = m.getAnnotation(Description.class);
072                if (d != null) {
073                    descr = d.value();
074                }
075            }
076            return descr;
077            }
078    
079    
080            private Method getMethodForAttribute(final MBeanAttributeInfo info) {
081                    Method m = null;
082                    Iterator<String> methodNames = constructPossibleMethodNames(info.getName()).iterator();
083                    boolean found = false;
084                    while (!found && methodNames.hasNext()) {
085                            try {
086                                    m = getMBeanInterface().getMethod(methodNames.next(), new Class[0]);
087                                    found = true;
088                            } catch (NoSuchMethodException e) {     found = false; }
089                    }
090                    return m;
091            }
092    
093            private List<String> constructPossibleMethodNames(final String attributeName) {
094                    List<String> names = new ArrayList<String>();
095            String capitalizedVarName = attributeName.substring(0, 1).toUpperCase()
096                    + attributeName.substring(1);
097                    names.add("get" + capitalizedVarName);
098                    names.add("is" + capitalizedVarName);
099                    names.add("has" + capitalizedVarName);
100                    names.add("are" + capitalizedVarName);
101                    return names;
102            }
103    
104            private static Method methodFor(final Class<?> mbeanInterface,
105                            final MBeanOperationInfo op) {
106                    final MBeanParameterInfo[] params = op.getSignature();
107                    final String[] paramTypes = new String[params.length];
108                    for (int i = 0; i < params.length; i++) {
109                paramTypes[i] = params[i].getType();
110            }
111    
112                    return findMethod(mbeanInterface, op.getName(), paramTypes);
113            }
114    
115            private static Method findMethod(final Class<?> mbeanInterface, final String name,
116                            final String... paramTypes) {
117                    try {
118                            final ClassLoader loader = mbeanInterface.getClassLoader();
119                            final Class<?>[] paramClasses = new Class<?>[paramTypes.length];
120                            for (int i = 0; i < paramTypes.length; i++) {
121                    paramClasses[i] = classForName(paramTypes[i], loader);
122                }
123                            return mbeanInterface.getMethod(name, paramClasses);
124                    } catch (RuntimeException e) {
125                            // avoid accidentally catching unexpected runtime exceptions
126                            throw e;
127                    } catch (Exception e) {
128                            return null;
129                    }
130            }
131    
132            /**
133             * Class for name.
134             *
135             * @param name the name
136             * @param loader the loader
137             * @return the class
138             * @throws ClassNotFoundException the class not found exception
139             */
140            static Class<?> classForName(final String name, final ClassLoader loader)
141                            throws ClassNotFoundException {
142                    Class<?> c = primitiveClasses.get(name);
143                    if (c == null) {
144                c = Class.forName(name, false, loader);
145            }
146                    return c;
147            }
148    
149            private static final Map<String, Class<?>> primitiveClasses = new HashMap<String, Class<?>>();
150            static {
151                    Class<?>[] prims = { byte.class, short.class, int.class, long.class,
152                                    float.class, double.class, char.class, boolean.class, };
153                    for (Class<?> c : prims) {
154                primitiveClasses.put(c.getName(), c);
155            }
156            }
157    
158    }