diff --git a/src/main/java/org/weakref/jmx/AnnotationUtils.java b/src/main/java/org/weakref/jmx/AnnotationUtils.java old mode 100644 new mode 100755 index 6225cf9..408f5e7 --- a/src/main/java/org/weakref/jmx/AnnotationUtils.java +++ b/src/main/java/org/weakref/jmx/AnnotationUtils.java @@ -15,6 +15,13 @@ */ package org.weakref.jmx; +import com.google.common.base.Objects; +import com.google.common.collect.ImmutableList; +import org.weakref.jmx.JmxException.Reason; + +import javax.management.Descriptor; +import javax.management.DescriptorKey; +import javax.management.ImmutableDescriptor; import java.lang.annotation.Annotation; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; @@ -27,12 +34,6 @@ import java.util.Set; import java.util.TreeMap; -import javax.management.Descriptor; -import javax.management.DescriptorKey; -import javax.management.ImmutableDescriptor; - -import org.weakref.jmx.JmxException.Reason; - import static java.util.Arrays.asList; final class AnnotationUtils @@ -230,23 +231,45 @@ public static String getDescription(Annotation... annotations) public static Map findManagedMethods(Class clazz) { Map result = new HashMap(); + Set foundMethods = new HashSet(); + findManagedMethods(clazz, result, foundMethods); - // gather all publicly available methods + return result; + } + + private static void findManagedMethods(Class clazz, Map result, Set foundMethods) + { + // gather all available methods // this returns everything, even if it's declared in a parent - for (Method method : clazz.getMethods()) { + for (Method method : clazz.getDeclaredMethods()) { // skip methods that are used internally by the vm for implementing covariance, etc if (method.isSynthetic() || method.isBridge()) { continue; } + Signature methodSignature = new Signature(method); + if (foundMethods.contains(methodSignature)) { + continue; + } + foundMethods.add(methodSignature); + // look for annotations recursively in superclasses or interfaces Method managedMethod = findManagedMethod(clazz, method.getName(), method.getParameterTypes()); if (managedMethod != null) { + method.setAccessible(true); + managedMethod.setAccessible(true); result.put(method, managedMethod); } } - return result; + Class superclass = clazz.getSuperclass(); + if (superclass != null) { + findManagedMethods(superclass, result, foundMethods); + } + + for (Class iface : clazz.getInterfaces()) { + findManagedMethods(iface, result, foundMethods); + } } public static Method findManagedMethod(Method method) diff --git a/src/main/java/org/weakref/jmx/ReflectionUtils.java b/src/main/java/org/weakref/jmx/ReflectionUtils.java old mode 100644 new mode 100755 index d1a1170..fe09f6b --- a/src/main/java/org/weakref/jmx/ReflectionUtils.java +++ b/src/main/java/org/weakref/jmx/ReflectionUtils.java @@ -21,11 +21,11 @@ import javax.management.RuntimeOperationsException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.Map; -import java.util.LinkedHashMap; -import java.util.Collections; final class ReflectionUtils { @@ -53,8 +53,8 @@ public static Object invoke(Object target, Method method, Object... params) throws MBeanException, ReflectionException { assertNotNull(target, "target"); - assertNotNull(target, "method"); - assertNotNull(target, "params"); + assertNotNull(method, "method"); + assertNotNull(params, "params"); try { Object result = method.invoke(target, params); @@ -166,6 +166,46 @@ public static boolean isAssignable(Object value, Class type) } } + public static Method getMethod(Class target, String name, Class... parameterTypes) + throws NoSuchMethodException + { + Method method = getMethod0(target, name, parameterTypes); + if (method != null) { + return method; + } + + // Throw a NoSuchMethodException + return target.getMethod(name, parameterTypes); + } + + private static Method getMethod0(Class target, String name, Class[] parameterTypes) + { + try { + Method method = target.getDeclaredMethod(name, parameterTypes); + method.setAccessible(true); + return method; + } + catch (NoSuchMethodException ignored) { + } + + Class superclass = target.getSuperclass(); + if (superclass != null) { + Method method = getMethod0(superclass, name, parameterTypes); + if (method != null) { + return method; + } + } + + for (Class iface : target.getInterfaces()) { + Method method = getMethod0(iface, name, parameterTypes); + if (method != null) { + return method; + } + } + + return null; + } + private static void assertNotNull(Object param, String name) { if (param == null) { diff --git a/src/test/java/org/weakref/jmx/AbstractMbeanTest.java b/src/test/java/org/weakref/jmx/AbstractMbeanTest.java old mode 100644 new mode 100755 index 74e407a..63cf50e --- a/src/test/java/org/weakref/jmx/AbstractMbeanTest.java +++ b/src/test/java/org/weakref/jmx/AbstractMbeanTest.java @@ -17,6 +17,7 @@ import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; +import static org.weakref.jmx.ReflectionUtils.getMethod; public abstract class AbstractMbeanTest { @@ -43,8 +44,8 @@ public void testGetterAttributeInfo(String attribute, boolean isIs, Object[] val String methodName = "set" + attribute; for (T t : objects) { String attributeName = toFeatureName(attribute, t); - SimpleObject simpleObject = toSimpleObject(t); - Method setter = simpleObject.getClass().getMethod(methodName, clazz); + SimpleInterface simpleInterface = toSimpleInterface(t); + Method setter = getMethod(simpleInterface.getClass(), methodName, clazz); MBeanInfo info = getMBeanInfo(t); MBeanAttributeInfo attributeInfo = getAttributeInfo(info, attributeName); @@ -64,8 +65,8 @@ public void testSetterAttributeInfo(String attribute, boolean isIs, Object[] val for (T t : objects) { String attributeName = toFeatureName(attribute, t); - SimpleObject simpleObject = toSimpleObject(t); - Method getter = simpleObject.getClass().getMethod(methodName); + SimpleInterface simpleInterface = toSimpleInterface(t); + Method getter = getMethod(simpleInterface.getClass(), methodName); MBeanInfo info = getMBeanInfo(t); MBeanAttributeInfo attributeInfo = getAttributeInfo(info, attributeName); @@ -165,11 +166,11 @@ public void testGet(String attribute, boolean isIs, Object[] values, Class cl String methodName = "set" + attribute; for (T t : objects) { String attributeName = toFeatureName(attribute, t); - SimpleObject simpleObject = toSimpleObject(t); - Method setter = simpleObject.getClass().getMethod(methodName, clazz); + SimpleInterface simpleInterface = toSimpleInterface(t); + Method setter = getMethod(simpleInterface.getClass(), methodName, clazz); for (Object value : values) { - setter.invoke(simpleObject, value); + setter.invoke(simpleInterface, value); assertEquals(getAttribute(t, attributeName), value); } @@ -184,13 +185,13 @@ public void testSet(String attribute, boolean isIs, Object[] values, Class cl for (T t : objects) { String attributeName = toFeatureName(attribute, t); - SimpleObject simpleObject = toSimpleObject(t); - Method getter = simpleObject.getClass().getMethod(methodName); + SimpleInterface simpleInterface = toSimpleInterface(t); + Method getter = getMethod(simpleInterface.getClass(), methodName); for (Object value : values) { setAttribute(t, attributeName, value); - assertEquals(getter.invoke(simpleObject), value); + assertEquals(getter.invoke(simpleInterface), value); } } } @@ -200,9 +201,9 @@ public void testSetFailsOnNotManaged() throws Exception { for (T t : objects) { - SimpleObject simpleObject = toSimpleObject(t); + SimpleInterface simpleInterface = toSimpleInterface(t); - simpleObject.setNotManaged(1); + simpleInterface.setNotManaged(1); try { setAttribute(t, "NotManaged", 2); fail("Should not allow setting unmanaged attribute"); @@ -211,7 +212,7 @@ public void testSetFailsOnNotManaged() // ignore } - assertEquals(simpleObject.getNotManaged(), 1); + assertEquals(simpleInterface.getNotManaged(), 1); } } @@ -251,8 +252,8 @@ public void testSetFailsOnReadOnly() throws Exception { for (T t : objects) { - SimpleObject simpleObject = toSimpleObject(t); - simpleObject.setReadOnly(1); + SimpleInterface simpleInterface = toSimpleInterface(t); + simpleInterface.setReadOnly(1); try { setAttribute(t, "ReadOnly", 2); fail("Should not allow setting read-only attribute"); @@ -261,7 +262,7 @@ public void testSetFailsOnReadOnly() // ignore } - assertEquals(simpleObject.getReadOnly(), 1); + assertEquals(simpleInterface.getReadOnly(), 1); } } @@ -339,7 +340,10 @@ Object[][] getFixtures() new Object[] { "StringValue", false, new Object[] { null, "hello there" }, String.class }, - new Object[] { "ObjectValue", false, new Object[] { "random object", 1, true }, Object.class } + new Object[] { "ObjectValue", false, new Object[] { "random object", 1, true }, Object.class }, + + new Object[] { "PrivateValue", false, new Object[] { Integer.MAX_VALUE, Integer.MIN_VALUE, 0 }, + Integer.TYPE }, }; } @@ -356,21 +360,21 @@ protected String toFeatureName(String attribute, T t) return attributeName; } - protected SimpleObject toSimpleObject(T t) + protected SimpleInterface toSimpleInterface(T t) { - SimpleObject simpleObject; - if (getObject(t) instanceof SimpleObject) { - simpleObject = (SimpleObject) getObject(t); + SimpleInterface simpleInterface; + if (getObject(t) instanceof SimpleInterface) { + simpleInterface = (SimpleInterface) getObject(t); } else if (getObject(t) instanceof FlattenObject) { - simpleObject = ((FlattenObject) getObject(t)).getSimpleObject(); + simpleInterface = ((FlattenObject) getObject(t)).getSimpleObject(); } else if (getObject(t) instanceof NestedObject) { - simpleObject = ((NestedObject) getObject(t)).getSimpleObject(); + simpleInterface = ((NestedObject) getObject(t)).getSimpleObject(); } else { - throw new IllegalArgumentException("Expected objects of type SimpleObject or FlattenObject but got " + getObject(t).getClass().getName()); + throw new IllegalArgumentException("Expected objects implementing SimpleInterface or FlattenObject but got " + getObject(t).getClass().getName()); } - return simpleObject; + return simpleInterface; } } diff --git a/src/test/java/org/weakref/jmx/CustomAnnotationObject.java b/src/test/java/org/weakref/jmx/CustomAnnotationObject.java index e8f4cc6..31a5e2a 100644 --- a/src/test/java/org/weakref/jmx/CustomAnnotationObject.java +++ b/src/test/java/org/weakref/jmx/CustomAnnotationObject.java @@ -6,8 +6,29 @@ import java.lang.annotation.Target; public class CustomAnnotationObject - extends SimpleObject + implements SimpleInterface { + private boolean booleanValue; + private Boolean booleanBoxedValue; + private byte byteValue; + private Byte byteBoxedValue; + private short shortValue; + private Short shortBoxedValue; + private int integerValue; + private Integer integerBoxedValue; + private long longValue; + private Long longBoxedValue; + private float floatValue; + private Float floatBoxedValue; + private double doubleValue; + private Double doubleBoxedValue; + private String stringValue; + private Object objectValue; + private int privateValue; + + private int notManaged; + private int writeOnly; + private int readOnly; @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD }) @@ -26,280 +47,251 @@ public class CustomAnnotationObject } - @Override @Managed1 public boolean isBooleanValue() { - return super.isBooleanValue(); + return booleanValue; } - @Override @Managed2 public void setBooleanValue(boolean booleanValue) { - super.setBooleanValue(booleanValue); + this.booleanValue = booleanValue; } - @Override @Managed2 public Boolean isBooleanBoxedValue() { - return super.isBooleanBoxedValue(); + return booleanBoxedValue; } - @Override @Managed1 public void setBooleanBoxedValue(Boolean booleanBoxedValue) { - super.setBooleanBoxedValue(booleanBoxedValue); + this.booleanBoxedValue = booleanBoxedValue; } - @Override @Managed1 public byte getByteValue() { - return super.getByteValue(); + return byteValue; } - @Override @Managed1 public void setByteValue(byte byteValue) { - super.setByteValue(byteValue); + this.byteValue = byteValue; } - @Override @Managed2 public Byte getByteBoxedValue() { - return super.getByteBoxedValue(); + return byteBoxedValue; } - @Override @Managed2 public void setByteBoxedValue(Byte byteBoxedValue) { - super.setByteBoxedValue(byteBoxedValue); + this.byteBoxedValue = byteBoxedValue; } - @Override @Managed2 public short getShortValue() { - return super.getShortValue(); + return shortValue; } - @Override @Managed2 public void setShortValue(short shortValue) { - super.setShortValue(shortValue); + this.shortValue = shortValue; } - @Override @Managed1 public Short getShortBoxedValue() { - return super.getShortBoxedValue(); + return shortBoxedValue; } - @Override @Managed1 public void setShortBoxedValue(Short shortBoxedValue) { - super.setShortBoxedValue(shortBoxedValue); + this.shortBoxedValue = shortBoxedValue; } - @Override @Managed1 public int getIntegerValue() { - return super.getIntegerValue(); + return integerValue; } - @Override @Managed2 public void setIntegerValue(int integerValue) { - super.setIntegerValue(integerValue); + this.integerValue = integerValue; } - @Override @Managed2 public Integer getIntegerBoxedValue() { - return super.getIntegerBoxedValue(); + return integerBoxedValue; } - @Override @Managed1 public void setIntegerBoxedValue(Integer integerBoxedValue) { - super.setIntegerBoxedValue(integerBoxedValue); + this.integerBoxedValue = integerBoxedValue; } - @Override @Managed1 public long getLongValue() { - return super.getLongValue(); + return longValue; } - @Override @Managed2 public void setLongValue(long longValue) { - super.setLongValue(longValue); + this.longValue = longValue; } - @Override @Managed1 public Long getLongBoxedValue() { - return super.getLongBoxedValue(); + return longBoxedValue; } - @Override @Managed1 public void setLongBoxedValue(Long longBoxedValue) { - super.setLongBoxedValue(longBoxedValue); + this.longBoxedValue = longBoxedValue; } - @Override @Managed2 public float getFloatValue() { - return super.getFloatValue(); + return floatValue; } - @Override @Managed2 public void setFloatValue(float floatValue) { - super.setFloatValue(floatValue); + this.floatValue = floatValue; } - @Override @Managed1 public Float getFloatBoxedValue() { - return super.getFloatBoxedValue(); + return floatBoxedValue; } - @Override @Managed2 public void setFloatBoxedValue(Float floatBoxedValue) { - super.setFloatBoxedValue(floatBoxedValue); + this.floatBoxedValue = floatBoxedValue; } - @Override @Managed1 public double getDoubleValue() { - return super.getDoubleValue(); + return this.doubleValue; } - @Override @Managed2 public void setDoubleValue(double doubleValue) { - super.setDoubleValue(doubleValue); + this.doubleValue = doubleValue; } - @Override @Managed1 public Double getDoubleBoxedValue() { - return super.getDoubleBoxedValue(); + return doubleBoxedValue; } - @Override @Managed1 public void setDoubleBoxedValue(Double doubleBoxedValue) { - super.setDoubleBoxedValue(doubleBoxedValue); + this.doubleBoxedValue = doubleBoxedValue; } - @Override @Managed2 public String getStringValue() { - return super.getStringValue(); + return stringValue; } - @Override @Managed2 public void setStringValue(String stringValue) { - super.setStringValue(stringValue); + this.stringValue = stringValue; } - @Override public void setNotManaged(int value) { - super.setNotManaged(value); + this.notManaged = value; } - @Override public int getNotManaged() { - return super.getNotManaged(); + return notManaged; } - @Override @Managed2 public Object echo(Object value) { - return super.echo(value); + return value; } - @Override @Managed1 public Object getObjectValue() { - return super.getObjectValue(); + return objectValue; } - @Override @Managed2 public void setObjectValue(Object objectValue) { - super.setObjectValue(objectValue); + this.objectValue = objectValue; } - @Override public int getWriteOnly() { - return super.getWriteOnly(); + return writeOnly; } - @Override @Managed1 public void setWriteOnly(int writeOnly) { - super.setWriteOnly(writeOnly); + this.writeOnly = writeOnly; } - @Override @Managed2 public int getReadOnly() { - return super.getReadOnly(); + return readOnly; } - @Override public void setReadOnly(int readOnly) { - super.setReadOnly(readOnly); + this.readOnly = readOnly; + } + + @Managed1 + private int getPrivateValue() + { + return privateValue; + } + + @Managed2 + private void setPrivateValue(int privateValue) + { + this.privateValue = privateValue; } - @Override @Managed1(description = "epic description") public int getDescribedInt() { - return super.getDescribedInt(); + return 1; } - } diff --git a/src/test/java/org/weakref/jmx/SimpleInterface.java b/src/test/java/org/weakref/jmx/SimpleInterface.java new file mode 100644 index 0000000..c79046f --- /dev/null +++ b/src/test/java/org/weakref/jmx/SimpleInterface.java @@ -0,0 +1,120 @@ +package org.weakref.jmx; + +public interface SimpleInterface +{ + @Managed + boolean isBooleanValue(); + + @Managed + void setBooleanValue(boolean booleanValue); + + @Managed + Boolean isBooleanBoxedValue(); + + @Managed + void setBooleanBoxedValue(Boolean booleanBoxedValue); + + @Managed + byte getByteValue(); + + @Managed + void setByteValue(byte byteValue); + + @Managed + Byte getByteBoxedValue(); + + @Managed + void setByteBoxedValue(Byte byteBoxedValue); + + @Managed + short getShortValue(); + + @Managed + void setShortValue(short shortValue); + + @Managed + Short getShortBoxedValue(); + + @Managed + void setShortBoxedValue(Short shortBoxedValue); + + @Managed + int getIntegerValue(); + + @Managed + void setIntegerValue(int integerValue); + + @Managed + Integer getIntegerBoxedValue(); + + @Managed + void setIntegerBoxedValue(Integer integerBoxedValue); + + @Managed + long getLongValue(); + + @Managed + void setLongValue(long longValue); + + @Managed + Long getLongBoxedValue(); + + @Managed + void setLongBoxedValue(Long longBoxedValue); + + @Managed + float getFloatValue(); + + @Managed + void setFloatValue(float floatValue); + + @Managed + Float getFloatBoxedValue(); + + @Managed + void setFloatBoxedValue(Float floatBoxedValue); + + @Managed + double getDoubleValue(); + + @Managed + void setDoubleValue(double doubleValue); + + @Managed + Double getDoubleBoxedValue(); + + @Managed + void setDoubleBoxedValue(Double doubleBoxedValue); + + @Managed + String getStringValue(); + + @Managed + void setStringValue(String stringValue); + + void setNotManaged(int value); + + int getNotManaged(); + + @Managed + Object echo(Object value); + + @Managed + Object getObjectValue(); + + @Managed + void setObjectValue(Object objectValue); + + int getWriteOnly(); + + @Managed + void setWriteOnly(int writeOnly); + + @Managed + int getReadOnly(); + + void setReadOnly(int readOnly); + + @Managed(description = "epic description") + int getDescribedInt(); +} diff --git a/src/test/java/org/weakref/jmx/SimpleObject.java b/src/test/java/org/weakref/jmx/SimpleObject.java index 50f5632..eb3e620 100644 --- a/src/test/java/org/weakref/jmx/SimpleObject.java +++ b/src/test/java/org/weakref/jmx/SimpleObject.java @@ -16,6 +16,7 @@ package org.weakref.jmx; public class SimpleObject + implements SimpleInterface { private boolean booleanValue; private Boolean booleanBoxedValue; @@ -33,6 +34,7 @@ public class SimpleObject private Double doubleBoxedValue; private String stringValue; private Object objectValue; + private int privateValue; private int notManaged; private int writeOnly; @@ -268,6 +270,18 @@ public void setReadOnly(int readOnly) this.readOnly = readOnly; } + @Managed + private int getPrivateValue() + { + return privateValue; + } + + @Managed + private void setPrivateValue(int privateValue) + { + this.privateValue = privateValue; + } + @Managed(description = "epic description") public int getDescribedInt() {