diff --git a/.gitignore b/.gitignore
index b9e09d6..1167426 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,5 @@
/target
/test-output
/.settings
+/.idea
+*.iml
diff --git a/pom.xml b/pom.xml
index c5dcd13..1c2c47c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -75,15 +75,18 @@
-
- elibom
- http://repository.elibom.net/nexus/content/repositories/releases
-
-
-
- elibom
- http://repository.elibom.net/nexus/content/repositories/snapshots
-
+
+ releases
+ Internal Releases
+ http://maven.polarislabs.com/content/repositories/releases/
+ default
+
+
+ snapshots
+ Internal Snapshots
+ http://maven.polarislabs.com/content/repositories/snapshots/
+ default
+
-
\ No newline at end of file
+
diff --git a/src/main/java/net/gescobar/jmx/annotation/DescriptorFields.java b/src/main/java/net/gescobar/jmx/annotation/DescriptorFields.java
new file mode 100644
index 0000000..22860a5
--- /dev/null
+++ b/src/main/java/net/gescobar/jmx/annotation/DescriptorFields.java
@@ -0,0 +1,19 @@
+package net.gescobar.jmx.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE})
+public @interface DescriptorFields {
+ /**
+ * Key-value pairs
+ *
+ * @return
+ */
+ String[] value();
+}
diff --git a/src/main/java/net/gescobar/jmx/annotation/Impact.java b/src/main/java/net/gescobar/jmx/annotation/Impact.java
index c1fb78a..ddd8810 100644
--- a/src/main/java/net/gescobar/jmx/annotation/Impact.java
+++ b/src/main/java/net/gescobar/jmx/annotation/Impact.java
@@ -3,24 +3,23 @@
import javax.management.MBeanOperationInfo;
/**
- * An enum that maps to impact options of the MBeanOperationInfo.
- *
+ * An enum that maps to
+ * impact options of the
+ * MBeanOperationInfo.
+ *
* @author German Escobar
*/
public enum Impact {
- ACTION(MBeanOperationInfo.ACTION),
-
+ ACTION(MBeanOperationInfo.ACTION),
INFO(MBeanOperationInfo.INFO),
-
ACTION_INFO(MBeanOperationInfo.ACTION_INFO),
-
UNKNOWN(MBeanOperationInfo.UNKNOWN);
-
+
private int code;
-
+
private Impact(int code) {
- this.code = code;
+ this.code = code;
}
public int getCode() {
diff --git a/src/main/java/net/gescobar/jmx/impl/AbstractInstanceResolver.java b/src/main/java/net/gescobar/jmx/impl/AbstractInstanceResolver.java
new file mode 100644
index 0000000..e5d6479
--- /dev/null
+++ b/src/main/java/net/gescobar/jmx/impl/AbstractInstanceResolver.java
@@ -0,0 +1,16 @@
+package net.gescobar.jmx.impl;
+
+/**
+ * Abstraction to allow for lazy-resolution of proxy targets.
+ *
+ * @author bvarner
+ */
+public abstract class AbstractInstanceResolver {
+
+ /**
+ * Return an object to have methods or attributes invoked upon it.
+ *
+ * @return
+ */
+ public abstract Object resolve();
+}
diff --git a/src/main/java/net/gescobar/jmx/impl/InstanceResolver.java b/src/main/java/net/gescobar/jmx/impl/InstanceResolver.java
new file mode 100644
index 0000000..0307fbc
--- /dev/null
+++ b/src/main/java/net/gescobar/jmx/impl/InstanceResolver.java
@@ -0,0 +1,28 @@
+package net.gescobar.jmx.impl;
+
+/**
+ * A generic AbstractInstanceResolver which resolve the same object every time.
+ *
+ * @author bvarner
+ */
+public class InstanceResolver extends AbstractInstanceResolver {
+
+ private Object target;
+
+ /**
+ * Creates a new InstanceResolver which returns the given object.
+ */
+ public InstanceResolver(Object obj) {
+ this.target = obj;
+ }
+
+ /**
+ * Returns the object this InstanceResolver was constructed to wrap
+ *
+ * @return
+ */
+ @Override
+ public Object resolve() {
+ return target;
+ }
+}
diff --git a/src/main/java/net/gescobar/jmx/impl/MBeanFactory.java b/src/main/java/net/gescobar/jmx/impl/MBeanFactory.java
index e0f68b2..acfb1e5 100644
--- a/src/main/java/net/gescobar/jmx/impl/MBeanFactory.java
+++ b/src/main/java/net/gescobar/jmx/impl/MBeanFactory.java
@@ -1,12 +1,13 @@
package net.gescobar.jmx.impl;
+import net.gescobar.jmx.annotation.DescriptorFields;
import static net.gescobar.jmx.util.StringUtils.capitalize;
import static net.gescobar.jmx.util.StringUtils.decapitalize;
-import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
import javax.management.DynamicMBean;
import javax.management.MBeanAttributeInfo;
@@ -24,362 +25,440 @@
import net.gescobar.jmx.annotation.ManagedOperation;
/**
- *
Factory of DynamicMBeans. Users can use this object directly to create DynamicMBeans and then registering them
- * with any MBeanServer. However, the preferred approach is to use the {@link Management} class (which internally uses
- * this class).
- *
+ *
+ * Factory of DynamicMBeans. Users can use this object directly to create
+ * DynamicMBeans and then registering them with any MBeanServer. However, the
+ * preferred approach is to use the {@link Management} class (which internally
+ * uses this class).
+ *
* @author German Escobar
*/
-public final class MBeanFactory {
-
- /**
- * Hide public constructor.
- */
- private MBeanFactory() {}
-
- /**
- * Creates a DynamicMBean from an object annotated with {@link ManagedBean} exposing all methods and attributes
- * annotated with {@link ManagedOperation} and {@link ManagedAttribute} respectively.
- *
- * @param object the object from which we are creating the DynamicMBean.
- *
- * @return a constructed DynamicMBean object that can be registered with any MBeanServer.
- */
- public static DynamicMBean createMBean(Object object) {
-
- if (object == null) {
- throw new IllegalArgumentException("No object specified.");
- }
-
- Class> objectType = object.getClass();
-
- // retrieve description
- String description = "";
- if (objectType.isAnnotationPresent(Description.class)) {
- description = objectType.getAnnotation(Description.class).value();
- }
-
- // build attributes and operations
- Method[] methods = objectType.getMethods();
- MethodHandler methodHandler = new MBeanFactory().new MethodHandler( objectType );
- for (Method method : methods) {
- methodHandler.handleMethod(method);
- }
-
- // build the MBeanInfo
- MBeanInfo mBeanInfo = new MBeanInfo(objectType.getName(), description, methodHandler.getMBeanAttributes(),
- new MBeanConstructorInfo[0], methodHandler.getMBeanOperations(), new MBeanNotificationInfo[0]);
-
- // create the MBean
- return new MBeanImpl(object, mBeanInfo);
-
- }
+public class MBeanFactory {
+
+ /**
+ * Hide public constructor.
+ */
+ private MBeanFactory() {
+ }
/**
- * This class is used internally to handle the methods of the object that the
- * {@link MBeanFactory#createMBean(Object)} receives as an argument. It creates a collection of MBeanAttributeInfo
- * and MBeanOperationInfo from the information of the methods that it handles.
- *
+ * Creates a DynamicMBean from an object annotated with {@link ManagedBean}
+ * exposing all methods and attributes annotated with
+ * {@link ManagedOperation} and {@link ManagedAttribute} respectively.
+ *
+ * @param object the class representing the object type resolved by TargetObjectResolver.
+ *
+ * @return a constructed DynamicMBean object that can be registered with any
+ * MBeanServer.
+ */
+ public static DynamicMBean createMBean(Object object) {
+ if (object == null) {
+ throw new IllegalArgumentException("No object specified.");
+ }
+ return createMBean(object.getClass(), new InstanceResolver(object));
+ }
+
+ /**
+ * Creates a DynamicMBean from an object annotated with {@link ManagedBean}
+ * exposing all methods and attributes annotated with
+ * {@link ManagedOperation} and {@link ManagedAttribute} respectively.
+ *
+ * @param objectType the class representing the object type resolved by TargetObjectResolver.
+ * @param targetResolver A resolver capable of resolving an instance of the given type.
+ *
+ * @return a constructed DynamicMBean object that can be registered with any
+ * MBeanServer.
+ */
+ public static DynamicMBean createMBean(Class> objectType, AbstractInstanceResolver targetResolver) {
+ if (objectType == null) {
+ throw new IllegalArgumentException("No object class specified.");
+ }
+
+ // retrieve description
+ String description = "";
+ if (objectType.isAnnotationPresent(Description.class)) {
+ description = objectType.getAnnotation(Description.class).value();
+ }
+
+ // build attributes and operations
+ Method[] methods = objectType.getMethods();
+ MethodHandler methodHandler = new MBeanFactory().new MethodHandler(objectType);
+ for (Method method : methods) {
+ methodHandler.handleMethod(method);
+ }
+
+ // build the MBeanInfo
+ MBeanInfo mBeanInfo = new MBeanInfo(objectType.getName(), description, methodHandler.getMBeanAttributes(),
+ new MBeanConstructorInfo[0], methodHandler.getMBeanOperations(), new MBeanNotificationInfo[0]);
+
+ // create the MBean
+ return new MBeanImpl(targetResolver, mBeanInfo);
+
+ }
+
+ /**
+ * This class is used internally to handle the methods of the object that
+ * the {@link MBeanFactory#createMBean(Object)} receives as an argument. It
+ * creates a collection of MBeanAttributeInfo and MBeanOperationInfo from
+ * the information of the methods that it handles.
+ *
* @author German Escobar
*/
private class MethodHandler {
-
- /**
- * The class of the object.
- */
- private Class> objectType;
-
- /**
- * Holds the MBeanAttributeInfo objects that are created from the methods annotated with
- * {@link MBeanAttributeInfo}. Notice that the relation is not 1-1. If the attribute is not readable or
- * writable, it will not get added.
- */
- private Collection mBeanAttributes = new ArrayList();
-
- /**
- * Holds the MBeanOperationInfo objects that are created from the methods annotated with
- * {@link MBeanOperationInfo}. The relation is 1-1.
- */
- private Collection mBeanOperations = new ArrayList();
-
- /**
- * Constructor. Initializes the object with the specified class.
- *
- * @param objectType the class of the object that the MBeanFactory is handling.
- */
- public MethodHandler(Class> objectType) {
- this.objectType = objectType;
- }
-
- /**
- * Called once for each method of the object that the {@link MBeanFactory#createMBean(Object)} receives as an
- * argument. If the method is annotated with {@link ManagedAttribute} it will try to create a
- * MBeanAttributeInfo. If the method is annotated with {@link ManagedOperation} it will create a
- * MBeanOperationInfo. Otherwise, it will do nothing with the method.
- *
- * @param method the method we are handling.
- *
- * @throws ManagementException wraps anyting that could go wrong.
- */
- public void handleMethod(Method method) throws ManagementException {
-
- boolean hasManagedAttribute = method.isAnnotationPresent(ManagedAttribute.class);
- boolean hasManagedOperation = method.isAnnotationPresent(ManagedOperation.class);
-
- if (hasManagedAttribute && hasManagedOperation) {
- throw new ManagementException("Method " + method.getName() + " cannot have both ManagedAttribute and " +
- "ManagedOperation annotations.");
- }
-
- if (hasManagedAttribute) {
- handleManagedAttribute(method);
- }
-
- if (hasManagedOperation) {
- handleManagedOperation(method);
- }
-
- }
-
- /**
- * Called after the {@link #handleMethod(Method)} is called for all the methods of the objectType.
- * Retrieves the exposed attributes.
- *
- * @return an array of initialized MBeanAttributeInfo objects. It will never return null.
- */
- public MBeanAttributeInfo[] getMBeanAttributes() {
- return mBeanAttributes.toArray( new MBeanAttributeInfo[0] );
- }
-
- /**
- * Called after the {@link #handleMethod(Method)} is called for all the methods of the objectType.
- * Retrieves the exposed operations.
- *
- * @return an array of initialized MBeanOperationInfo objects. It will never return null.
- */
- public MBeanOperationInfo[] getMBeanOperations() {
- return mBeanOperations.toArray( new MBeanOperationInfo[0] );
- }
-
- /**
- * Helper method. Handles a method that has a {@link ManagedAttribute} annotation. Notice that the mehtod is
- * not necessarily a valid getter/setter. We actually need to find out.
- *
- * @param method the method that is annotated with {@link ManagedAttribute}
- */
- private void handleManagedAttribute(Method method) {
-
- // validate if the method is a getter or setter
- Method getterMethod = isGetterMethod(method) ? method : null;
- Method setterMethod = isSetterMethod(method) ? method : null;
-
- if (getterMethod == null && setterMethod == null) {
- // not a getter or setter
- throw new ManagementException("Method " + method.getName() + " is annotated as ManagedAttribute " +
- "but doesn't looks like a valid getter or setter.");
- }
-
- // retrieve the attribute name from the method name
- String attributeName = method.getName().startsWith("is") ?
- decapitalize( method.getName().substring(2) ) : decapitalize( method.getName().substring(3) );
-
- // retrieve the attribute type from the setter argument type or the getter return type
- Class> attributeType = setterMethod != null ?
- method.getParameterTypes()[0] : method.getReturnType();
-
- // find the missing method
- getterMethod = getterMethod == null ? findGetterMethod(objectType, attributeName) : getterMethod;
- setterMethod = setterMethod == null ? findSetterMethod(objectType, attributeName, attributeType) : setterMethod;
-
- boolean existsAttribute = existsAttribute(mBeanAttributes, attributeName, attributeType);
- if ( !existsAttribute ) {
-
- // add the MBeanAttribute to the collection
- MBeanAttributeInfo mBeanAttribute = buildMBeanAttribute(attributeName, attributeType, getterMethod,
- setterMethod, method);
- if (mBeanAttribute != null) { // it can be null if it is neither readable or writable
- mBeanAttributes.add( mBeanAttribute );
- }
-
- } else {
- // both getter and setter are annotated ... throw exception
- throw new ManagementException("Both getter and setter are annotated for attribute " +
- attributeName + ". Please remove one of the annotations.");
- }
-
- }
-
- /**
- * Helper method. Tells if the method is a getter or not. It checks if the method name starts with "get" or
- * "is", that the method has no parameters and returns something different than void.
- *
- * @param method the method that we are testing to see if it is a getter.
- *
- * @return true if the method is a getter, false otherwise.
- */
- private boolean isGetterMethod(Method method) {
- return (method.getName().startsWith("get") || method.getName().startsWith("is"))
- && (!method.getReturnType().equals(Void.TYPE) && method.getParameterTypes().length == 0);
- }
-
- /**
- * Helper method. Tells if the method is a setter or not. It checks if the method name starts with "set",
- * that the return type is void and that it has exactly one parameter.
- *
- * @param method the method that we are testing to see if it is a setter.
- *
- * @return true if the method is a setter, false otherwise.
- */
- private boolean isSetterMethod(Method method) {
- return method.getName().startsWith("set") && method.getReturnType().equals(Void.TYPE)
- && method.getParameterTypes().length == 1;
- }
-
- /**
- * Helper method. Tries to find a getter method for the specified objectType and
- * attributeName.
- *
- * @param objectType the class from which we are going to find the getter method.
- * @param attributeName the name of the attribute we are looking for.
- *
- * @return a java.lang.reflect.Method object representing the getter or null if not found.
- */
- private Method findGetterMethod(Class> objectType, String attributeName) {
-
- try {
- return objectType.getMethod( "get" + capitalize(attributeName) );
- } catch (NoSuchMethodException e) {}
-
- try {
- return objectType.getMethod( "is" + capitalize(attributeName) );
- } catch (NoSuchMethodException e) {}
-
- return null;
+
+ /**
+ * The class of the object.
+ */
+ private Class> objectType;
+
+ /**
+ * Holds the MBeanAttributeInfo objects that are created from the
+ * methods annotated with {@link MBeanAttributeInfo}. Notice that the
+ * relation is not 1-1. If the attribute is not readable or writable, it
+ * will not get added.
+ */
+ private Collection mBeanAttributes = new ArrayList();
+
+ /**
+ * Holds the MBeanOperationInfo objects that are created from the
+ * methods annotated with {@link MBeanOperationInfo}. The relation is
+ * 1-1.
+ */
+ private Collection mBeanOperations = new ArrayList();
+
+ /**
+ * Constructor. Initializes the object with the specified class.
+ *
+ * @param objectType the class of the object that the MBeanFactory is
+ * handling.
+ */
+ public MethodHandler(Class> objectType) {
+ this.objectType = objectType;
+ }
+
+ /**
+ * Called once for each method of the object that the
+ * {@link MBeanFactory#createMBean(Object)} receives as an argument. If
+ * the method is annotated with {@link ManagedAttribute} it will try to
+ * create a MBeanAttributeInfo. If the method is annotated with
+ * {@link ManagedOperation} it will create a MBeanOperationInfo.
+ * Otherwise, it will do nothing with the method.
+ *
+ * @param method the method we are handling.
+ *
+ * @throws ManagementException wraps anyting that could go wrong.
+ */
+ public void handleMethod(Method method) throws ManagementException {
+
+ boolean hasManagedAttribute = method.isAnnotationPresent(ManagedAttribute.class);
+ boolean hasManagedOperation = method.isAnnotationPresent(ManagedOperation.class);
+
+ if (hasManagedAttribute && hasManagedOperation) {
+ throw new ManagementException("Method " + method.getName() + " cannot have both ManagedAttribute and "
+ + "ManagedOperation annotations.");
+ }
+
+ if (hasManagedAttribute) {
+ handleManagedAttribute(method);
+ }
+
+ if (hasManagedOperation) {
+ handleManagedOperation(method);
+ }
+
+ }
+
+ /**
+ * Called after the {@link #handleMethod(Method)} is called for all the
+ * methods of the objectType. Retrieves the exposed
+ * attributes.
+ *
+ * @return an array of initialized MBeanAttributeInfo objects. It will
+ * never return null.
+ */
+ public MBeanAttributeInfo[] getMBeanAttributes() {
+ return mBeanAttributes.toArray(new MBeanAttributeInfo[0]);
+ }
+
+ /**
+ * Called after the {@link #handleMethod(Method)} is called for all the
+ * methods of the objectType. Retrieves the exposed
+ * operations.
+ *
+ * @return an array of initialized MBeanOperationInfo objects. It will
+ * never return null.
+ */
+ public MBeanOperationInfo[] getMBeanOperations() {
+ return mBeanOperations.toArray(new MBeanOperationInfo[0]);
+ }
+
+ /**
+ * Helper method. Handles a method that has a {@link ManagedAttribute}
+ * annotation. Notice that the mehtod is not necessarily a valid
+ * getter/setter. We actually need to find out.
+ *
+ * @param method the method that is annotated with
+ * {@link ManagedAttribute}
+ */
+ private void handleManagedAttribute(Method method) {
+
+ // validate if the method is a getter or setter
+ Method getterMethod = isGetterMethod(method) ? method : null;
+ Method setterMethod = isSetterMethod(method) ? method : null;
+
+ if (getterMethod == null && setterMethod == null) {
+ // not a getter or setter
+ throw new ManagementException("Method " + method.getName() + " is annotated as ManagedAttribute "
+ + "but doesn't looks like a valid getter or setter.");
+ }
+
+ // retrieve the attribute name from the method name
+ String attributeName = method.getName().startsWith("is")
+ ? decapitalize(method.getName().substring(2)) : decapitalize(method.getName().substring(3));
+
+ // retrieve the attribute type from the setter argument type or the getter return type
+ Class> attributeType = setterMethod != null
+ ? method.getParameterTypes()[0] : method.getReturnType();
+
+ // find the missing method
+ getterMethod = getterMethod == null ? findGetterMethod(objectType, attributeName) : getterMethod;
+ setterMethod = setterMethod == null ? findSetterMethod(objectType, attributeName, attributeType) : setterMethod;
+
+ boolean existsAttribute = existsAttribute(mBeanAttributes, attributeName, attributeType);
+ if (!existsAttribute) {
+
+ // add the MBeanAttribute to the collection
+ MBeanAttributeInfo mBeanAttribute = buildMBeanAttribute(attributeName, attributeType, getterMethod,
+ setterMethod, method);
+ if (mBeanAttribute != null) { // it can be null if it is neither readable or writable
+ mBeanAttributes.add(mBeanAttribute);
+ }
+
+ } else {
+ // both getter and setter are annotated ... throw exception
+ throw new ManagementException("Both getter and setter are annotated for attribute "
+ + attributeName + ". Please remove one of the annotations.");
+ }
+
}
- /**
- * Helper method. Tries to find a setter method for the specified objectType,
- * attributeName and attributeType
- *
- * @param objectType the class from which we are going to find the setter method.
- * @param attributeName the name of the attribute we are looking for.
- * @param attributeType the type of the attribute we are looking for.
- *
- * @return a java.lang.reflect.Method object representing the setter or null if not found.
- */
+ /**
+ * Helper method. Tells if the method is a getter or not. It checks if
+ * the method name starts with "get" or "is", that the method has no
+ * parameters and returns something different than void.
+ *
+ * @param method the method that we are testing to see if it is a
+ * getter.
+ *
+ * @return true if the method is a getter, false otherwise.
+ */
+ private boolean isGetterMethod(Method method) {
+ return (method.getName().startsWith("get") || method.getName().startsWith("is"))
+ && (!method.getReturnType().equals(Void.TYPE) && method.getParameterTypes().length == 0);
+ }
+
+ /**
+ * Helper method. Tells if the method is a setter or not. It checks if
+ * the method name starts with "set", that the return type is
+ * void and that it has exactly one parameter.
+ *
+ * @param method the method that we are testing to see if it is a
+ * setter.
+ *
+ * @return true if the method is a setter, false otherwise.
+ */
+ private boolean isSetterMethod(Method method) {
+ return method.getName().startsWith("set") && method.getReturnType().equals(Void.TYPE)
+ && method.getParameterTypes().length == 1;
+ }
+
+ /**
+ * Helper method. Tries to find a getter method for the specified
+ * objectType and attributeName.
+ *
+ * @param objectType the class from which we are going to find the
+ * getter method.
+ * @param attributeName the name of the attribute we are looking for.
+ *
+ * @return a java.lang.reflect.Method object representing the getter or
+ * null if not found.
+ */
+ private Method findGetterMethod(Class> objectType, String attributeName) {
+
+ try {
+ return objectType.getMethod("get" + capitalize(attributeName));
+ } catch (NoSuchMethodException e) {
+ }
+
+ try {
+ return objectType.getMethod("is" + capitalize(attributeName));
+ } catch (NoSuchMethodException e) {
+ }
+
+ return null;
+ }
+
+ /**
+ * Helper method. Tries to find a setter method for the specified
+ * objectType, attributeName and
+ * attributeType
+ *
+ * @param objectType the class from which we are going to find the
+ * setter method.
+ * @param attributeName the name of the attribute we are looking for.
+ * @param attributeType the type of the attribute we are looking for.
+ *
+ * @return a java.lang.reflect.Method object representing the setter or
+ * null if not found.
+ */
private Method findSetterMethod(Class> objectType, String name, Class> attributeType) {
-
- try {
- return objectType.getMethod( "set" + capitalize(name), attributeType );
- } catch (NoSuchMethodException e) {
- return null;
- }
-
+
+ try {
+ return objectType.getMethod("set" + capitalize(name), attributeType);
+ } catch (NoSuchMethodException e) {
+ return null;
+ }
+
}
-
- /**
- * Helper method. Tells if the collection of MBeanAttributeInfo holds an attribute with the specified name
- * and type.
- *
- * @param mBeanAttributes the collection of MBeanAttributeInfo in which we are searching the attribute.
- * @param attributeName the name of the attribute we are searching for.
- * @param attributeType the type of the attribute we are searching for.
- *
- * @return true if the collections holds the attribute, false otherwise.
- */
- private boolean existsAttribute(Collection mBeanAttributes, String attributeName, Class> attributeType) {
-
- for (MBeanAttributeInfo mBeanAttribute : mBeanAttributes) {
- if (mBeanAttribute.getName().equals(attributeName)
- && mBeanAttribute.getType().equals(attributeType.getName())) {
- return true;
- }
- }
-
- return false;
-
- }
-
- /**
- * Helper method. Builds an MBeanAttributeInfo. As a precondition we know that there is public getter or
- * setter method for the attribute that it's annotated with {@link ManagedAttribute}.
- *
- * @param attributeName the name of the attribute for which we are trying to build the MBeanAttributeInfo.
- * @param attributeType the class of the attribute for which we are trying to build the MBeanAttributeInfo.
- * @param getterMethod the getter method of the attribute ... can be null.
- * @param setterMethod the setter method of the attribute ... can be null.
- * @param annotatedMethod the method that is annotated with {@link ManagedAttribute} ... can't be null.
- *
- * @return a constructed MBeanAttributeInfo object or null if the attribute is neither readable or writable.
- */
- private MBeanAttributeInfo buildMBeanAttribute(String attributeName,
- Class> attributeType, Method getterMethod, Method setterMethod, Method annotatedMethod) {
-
- ManagedAttribute managedAttribute = annotatedMethod.getAnnotation(ManagedAttribute.class);
-
- // it's readable if the annotation has readable=true (which is the default) and the getter method exists
- boolean readable = managedAttribute.readable() && getterMethod != null;
-
- // it's writable if the annotation has writable=true (which is the default) and the setter method exists.
- boolean writable = managedAttribute.writable() && setterMethod != null;
-
- // it's IS if the getter method exists and starts with "is".
- boolean isIs = getterMethod != null && getterMethod.getName().startsWith("is");
-
- // only add the attribute if it is readable and writable
- if (readable || writable) {
- return new MBeanAttributeInfo(attributeName, attributeType.getName(), managedAttribute.description(),
- readable, writable, isIs);
- }
-
- return null;
-
+
+ /**
+ * Helper method. Tells if the collection of MBeanAttributeInfo holds an
+ * attribute with the specified name and type.
+ *
+ * @param mBeanAttributes the collection of MBeanAttributeInfo in which
+ * we are searching the attribute.
+ * @param attributeName the name of the attribute we are searching for.
+ * @param attributeType the type of the attribute we are searching for.
+ *
+ * @return true if the collections holds the attribute, false otherwise.
+ */
+ private boolean existsAttribute(Collection mBeanAttributes, String attributeName, Class> attributeType) {
+
+ for (MBeanAttributeInfo mBeanAttribute : mBeanAttributes) {
+ if (mBeanAttribute.getName().equals(attributeName)
+ && mBeanAttribute.getType().equals(attributeType.getName())) {
+ return true;
+ }
+ }
+
+ return false;
+
+ }
+
+ /**
+ * Helper method. Builds an MBeanAttributeInfo. As a precondition we
+ * know that there is public getter or setter method for the attribute
+ * that it's annotated with {@link ManagedAttribute}.
+ *
+ * @param attributeName the name of the attribute for which we are
+ * trying to build the MBeanAttributeInfo.
+ * @param attributeType the class of the attribute for which we are
+ * trying to build the MBeanAttributeInfo.
+ * @param getterMethod the getter method of the attribute ... can be
+ * null.
+ * @param setterMethod the setter method of the attribute ... can be
+ * null.
+ * @param annotatedMethod the method that is annotated with
+ * {@link ManagedAttribute} ... can't be null.
+ *
+ * @return a constructed MBeanAttributeInfo object or null if the
+ * attribute is neither readable or writable.
+ */
+ private MBeanAttributeInfo buildMBeanAttribute(String attributeName,
+ Class> attributeType, Method getterMethod, Method setterMethod, Method annotatedMethod) {
+
+ ManagedAttribute managedAttribute = annotatedMethod.getAnnotation(ManagedAttribute.class);
+
+ // it's readable if the annotation has readable=true (which is the default) and the getter method exists
+ boolean readable = managedAttribute.readable() && getterMethod != null;
+
+ // it's writable if the annotation has writable=true (which is the default) and the setter method exists.
+ boolean writable = managedAttribute.writable() && setterMethod != null;
+
+ // it's IS if the getter method exists and starts with "is".
+ boolean isIs = getterMethod != null && getterMethod.getName().startsWith("is");
+
+ // only add the attribute if it is readable and writable
+ if (readable || writable) {
+ return new MBeanAttributeInfo(attributeName, attributeType.getName(), managedAttribute.description(),
+ readable, writable, isIs);
+ }
+
+ return null;
+
+ }
+
+ /**
+ * Helper method. Handles a method that has a {@link ManagedOperation}
+ * annotation. It creates an MBeanOperationInfo from the method.
+ *
+ * @param method the method that is annotated with
+ * {@link ManagedOperation}
+ */
+ private void handleManagedOperation(Method method) {
+
+ // build the MBeanParameterInfo array from the parameters of the method
+ MBeanParameterInfo[] mBeanParameters = buildMBeanParameters(method);
+
+ ManagedOperation managedOperation = method.getAnnotation(ManagedOperation.class);
+ Impact impact = managedOperation.impact();
+
+ mBeanOperations.add(new MBeanOperationInfo(method.getName(), managedOperation.description(),
+ mBeanParameters, method.getReturnType().getName(), impact.getCode()));
+
}
-
+
/**
- * Helper method. Handles a method that has a {@link ManagedOperation} annotation. It creates an
- * MBeanOperationInfo from the method.
- *
- * @param method the method that is annotated with {@link ManagedOperation}
- */
- private void handleManagedOperation(Method method) {
-
- // build the MBeanParameterInfo array from the parameters of the method
- MBeanParameterInfo[] mBeanParameters = buildMBeanParameters( method.getParameterTypes(),
- method.getParameterAnnotations() );
-
- ManagedOperation managedOperation = method.getAnnotation(ManagedOperation.class);
- Impact impact = managedOperation.impact();
-
- mBeanOperations.add( new MBeanOperationInfo(method.getName(), managedOperation.description(),
- mBeanParameters, method.getReturnType().getName(), impact.getCode()) );
-
- }
-
- /**
- * Helper method. Builds an array of MBeanParameterInfo objects that represent the parameters.
- *
- * @param paramsTypes the type of the parameters from which we are building the MBeanParameterInfo array.
- * @param paramsAnnotations the annotations on the parameters .. we are not using this info yet but we will
- * need it to retrieve the description of the parameters.
- *
- * @return an array of MBeanParameterInfo objects of the same size of the paramsTypes argument.
- */
- private MBeanParameterInfo[] buildMBeanParameters(Class>[] paramsTypes, Annotation[][] paramsAnnotations) {
-
- MBeanParameterInfo[] mBeanParameters = new MBeanParameterInfo[paramsTypes.length];
-
- for (int i=0; i < paramsTypes.length; i++) {
-
- MBeanParameterInfo parameterInfo = new MBeanParameterInfo("param" + i, paramsTypes[i].getName(), "");
- mBeanParameters[i] = parameterInfo;
-
- }
-
- return mBeanParameters;
-
+ * Helper method. Builds an array of MBeanParameterInfo objects that
+ * represent the parameters, using the DescriptorFields annotation style from the JMX 2.0 specification.
+ *
+ * @param method the method having parameter info build for it.
+ *
+ * @return an array of MBeanParameterInfo objects describing the Method.
+ */
+ private MBeanParameterInfo[] buildMBeanParameters(Method method) {
+ Class[] paramsTypes = method.getParameterTypes();
+
+ HashMap paramNames = new HashMap();
+ HashMap paramDesc = new HashMap();
+
+ boolean hasDescriptorFields = method.isAnnotationPresent(DescriptorFields.class);
+ if (hasDescriptorFields) {
+ String[] pairs = method.getAnnotation(DescriptorFields.class).value();
+ for (int i = 0; i < pairs.length; i++) {
+ String[] keydesc = pairs[i].split(";");
+ String[] keyval = keydesc[0].split("=");
+
+ String desc = "";
+ if (keydesc.length > 1) {
+ desc = keydesc[1];
+ }
+
+ paramNames.put(keyval[0], keyval[1]);
+ paramDesc.put(keyval[0], desc);
+ }
+ }
+
+ MBeanParameterInfo[] mBeanParameters = new MBeanParameterInfo[paramsTypes.length];
+
+ for (int i = 0; i < paramsTypes.length; i++) {
+ String name = "p" + i;
+ String description = "";
+
+ if (paramDesc.containsKey(name)) {
+ description = paramDesc.get(name);
+ }
+
+ if (paramNames.containsKey(name)) {
+ name = paramNames.get(name);
+ }
+
+
+ MBeanParameterInfo parameterInfo = new MBeanParameterInfo(name, paramsTypes[i].getName(), description);
+ mBeanParameters[i] = parameterInfo;
+ }
+
+ return mBeanParameters;
}
-
}
-
-}
\ No newline at end of file
+}
diff --git a/src/main/java/net/gescobar/jmx/impl/MBeanImpl.java b/src/main/java/net/gescobar/jmx/impl/MBeanImpl.java
index c557514..5f317a0 100644
--- a/src/main/java/net/gescobar/jmx/impl/MBeanImpl.java
+++ b/src/main/java/net/gescobar/jmx/impl/MBeanImpl.java
@@ -20,447 +20,450 @@
/**
* This is the DynamicMBean implementation that is returned from the {@link MBeanFactory#createMBean(Object)} method.
- *
+ *
* @author German Escobar
*/
public class MBeanImpl implements DynamicMBean {
- /**
- * The object that is being instrumented.
- */
- private Object object;
-
- /**
- * Describes the exposed information of the object.
- */
+ /**
+ * The object that is being instrumented.
+ */
+ private AbstractInstanceResolver resolver;
+
+ /**
+ * Describes the exposed information of the object.
+ */
private MBeanInfo mBeanInfo;
-
+
/**
- * Constructor. Creates an instance using the Object instance that is going to be instrumented and the MBeanInfo
- * that describes the exposed information from the object.
- *
+ * Constructor. Creates an instance using the Object instance that is going to be instrumented and the MBeanInfo that describes
+ * the exposed information from the object.
+ *
* @param object the object that is going to be instrumented.
* @param mBeanInfo describes the exposed information of the object.
*/
- public MBeanImpl(Object object, MBeanInfo mBeanInfo) {
- this.object = object;
- this.mBeanInfo = mBeanInfo;
+ public MBeanImpl(AbstractInstanceResolver resolver, MBeanInfo mBeanInfo) {
+ this.resolver = resolver;
+ this.mBeanInfo = mBeanInfo;
}
@Override
- public Object getAttribute(String attributeName) throws AttributeNotFoundException, MBeanException,
- ReflectionException {
-
- // check attribute_name is not null to avoid NullPointerException later on
- if (attributeName == null) {
- throw new RuntimeOperationsException(new IllegalArgumentException("Attribute name cannot be null"),
- "Cannot invoke a getter of " + mBeanInfo.getClassName() + " with null attribute name");
- }
-
- MBeanAttributeInfo mBeanAttribute = findMBeanAttribute(attributeName);
- if (mBeanAttribute == null) {
- throw new AttributeNotFoundException("Cannot find " + attributeName + " attribute in " +
- mBeanInfo.getClassName());
- }
-
- Method getterMethod = findGetterMethod( mBeanAttribute );
- if (getterMethod == null) {
- throw new AttributeNotFoundException("Cannot find " + attributeName + " attribute or equivalent getter in " +
- mBeanInfo.getClassName());
- }
-
- try {
- return getterMethod.invoke(object);
- } catch (Exception e) {
- throw new MBeanException(e);
- }
+ public Object getAttribute(String attributeName) throws AttributeNotFoundException, MBeanException,
+ ReflectionException {
+
+ // check attribute_name is not null to avoid NullPointerException later on
+ if (attributeName == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Attribute name cannot be null"),
+ "Cannot invoke a getter of " + mBeanInfo.getClassName() + " with null attribute name");
+ }
+
+ MBeanAttributeInfo mBeanAttribute = findMBeanAttribute(attributeName);
+ if (mBeanAttribute == null) {
+ throw new AttributeNotFoundException("Cannot find " + attributeName + " attribute in "
+ + mBeanInfo.getClassName());
+ }
+
+ Method getterMethod = findGetterMethod(mBeanAttribute);
+ if (getterMethod == null) {
+ throw new AttributeNotFoundException("Cannot find " + attributeName + " attribute or equivalent getter in "
+ + mBeanInfo.getClassName());
+ }
+
+ try {
+ return getterMethod.invoke(resolver.resolve());
+ } catch (Exception e) {
+ throw new MBeanException(e);
+ }
}
-
+
@Override
public AttributeList getAttributes(String[] attributesNames) {
-
- // check attributeNames is not null to avoid NullPointerException later on
- if (attributesNames == null) {
- throw new RuntimeOperationsException(new IllegalArgumentException("attributeNames[] cannot be null"),
- "Cannot invoke a getter of " + mBeanInfo.getClassName());
- }
-
- AttributeList resultList = new AttributeList();
-
- // if attributeNames is empty, return an empty result list
- if (attributesNames.length == 0) {
- return resultList;
- }
-
- // build the result attribute list
- for (int i=0 ; i < attributesNames.length ; i++){
- try {
- Object value = getAttribute((String) attributesNames[i]);
- resultList.add(new Attribute(attributesNames[i], value));
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- return resultList;
-
+
+ // check attributeNames is not null to avoid NullPointerException later on
+ if (attributesNames == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException("attributeNames[] cannot be null"),
+ "Cannot invoke a getter of " + mBeanInfo.getClassName());
+ }
+
+ AttributeList resultList = new AttributeList();
+
+ // if attributeNames is empty, return an empty result list
+ if (attributesNames.length == 0) {
+ return resultList;
+ }
+
+ // build the result attribute list
+ for (int i = 0; i < attributesNames.length; i++) {
+ try {
+ Object value = getAttribute(attributesNames[i]);
+ resultList.add(new Attribute(attributesNames[i], value));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ return resultList;
+
}
@Override
public void setAttribute(Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException,
- MBeanException, ReflectionException {
-
- // check attribute is not null to avoid NullPointerException later on
- if (attribute == null) {
- throw new RuntimeOperationsException(new IllegalArgumentException("Attribute cannot be null"),
- "Cannot invoke a setter of " + mBeanInfo.getClassName() + " with null attribute");
- }
-
- String attributeName = attribute.getName();
- Object value = attribute.getValue();
-
- if (attributeName == null) {
- throw new RuntimeOperationsException(new IllegalArgumentException("Attribute name cannot be null"),
- "Cannot invoke the setter of " + mBeanInfo.getClassName() + " with null attribute name");
- }
-
- if (value == null) {
- throw(new InvalidAttributeValueException("Cannot set attribute " + attributeName + " to null"));
- }
-
- // try to set from the setter method
- MBeanAttributeInfo mBeanAttribute = findMBeanAttribute(attributeName);
- if (mBeanAttribute == null) {
- throw new AttributeNotFoundException("Cannot find " + attributeName + " attribute in " +
- mBeanInfo.getClassName());
- }
-
- Class> type = null;
- try {
- type = findClass(mBeanAttribute.getType());
- if ( !isAssignable(type, value.getClass()) ) {
- throw new InvalidAttributeValueException("Cannot set attribute "+ attributeName +" to a " +
- value.getClass().getName() + " object, " + type.getName() + " expected");
- }
- } catch (ClassNotFoundException e) {
- throw new RuntimeException(e);
- }
-
- Method setterMethod = findSetterMethod( mBeanAttribute, type );
- if (setterMethod == null) {
- throw new RuntimeException("No setter method for attribute " + attributeName);
- }
-
- try {
- setterMethod.invoke(object, value);
- } catch (Exception e) {
- throw new MBeanException(e);
- }
-
+ MBeanException, ReflectionException {
+
+ // check attribute is not null to avoid NullPointerException later on
+ if (attribute == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Attribute cannot be null"),
+ "Cannot invoke a setter of " + mBeanInfo.getClassName() + " with null attribute");
+ }
+
+ String attributeName = attribute.getName();
+ Object value = attribute.getValue();
+
+ if (attributeName == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException("Attribute name cannot be null"),
+ "Cannot invoke the setter of " + mBeanInfo.getClassName() + " with null attribute name");
+ }
+
+ if (value == null) {
+ throw (new InvalidAttributeValueException("Cannot set attribute " + attributeName + " to null"));
+ }
+
+ // try to set from the setter method
+ MBeanAttributeInfo mBeanAttribute = findMBeanAttribute(attributeName);
+ if (mBeanAttribute == null) {
+ throw new AttributeNotFoundException("Cannot find " + attributeName + " attribute in "
+ + mBeanInfo.getClassName());
+ }
+
+ Class> type = null;
+ try {
+ type = findClass(mBeanAttribute.getType());
+ if (!isAssignable(type, value.getClass())) {
+ throw new InvalidAttributeValueException("Cannot set attribute " + attributeName + " to a "
+ + value.getClass().getName() + " object, " + type.getName() + " expected");
+ }
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+
+ Method setterMethod = findSetterMethod(mBeanAttribute, type);
+ if (setterMethod == null) {
+ throw new RuntimeException("No setter method for attribute " + attributeName);
+ }
+
+ try {
+ setterMethod.invoke(resolver.resolve(), value);
+ } catch (Exception e) {
+ throw new MBeanException(e);
+ }
+
}
-
+
@Override
public AttributeList setAttributes(AttributeList attributes) {
- // check attributes is not null to avoid NullPointerException later on
- if (attributes == null) {
- throw new RuntimeOperationsException(new IllegalArgumentException("AttributeList attributes cannot be null"),
- "Cannot invoke a setter of " + mBeanInfo.getClassName());
- }
-
- AttributeList resultList = new AttributeList();
-
- // if attributeNames is empty, nothing more to do
- if (attributes.isEmpty()) {
- return resultList;
- }
-
- // for each attribute, try to set it and add to the result list if successful
- for (Iterator