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 }