MBeanInfoBuilder.java
package com.ziesemer.utils.jmx.server;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanParameterInfo;
import javax.management.NotCompliantMBeanException;
import javax.management.OperationsException;
import com.ziesemer.utils.jmx.beanInfo.MBeanDescription;
import com.ziesemer.utils.jmx.beanInfo.MBeanOperation;
import com.ziesemer.utils.jmx.beanInfo.MBeanParameter;
/**
* <p>This class currently provides only limited customization, currently
* focusing on performance. Additionally, it does not yet handle
* constructors ({@link javax.management.MBeanConstructorInfo}),
* notifications ({@link javax.management.MBeanNotificationInfo})
* or descriptors ({@link javax.management.Descriptor}),
* so the non-public methods are subject to (and probably will) change.</p>
* @author Mark A. Ziesemer
* <a href="http://www.ziesemer.com."><www.ziesemer.com></a>
*/
public class MBeanInfoBuilder{
public MBeanInfo build(final Class<?> c) throws OperationsException{
final String className = mapName(c);
final String description = mapDescription(c);
// Attributes & Operations
final List<MBeanOperationInfo> operations = new ArrayList<MBeanOperationInfo>();
final AttributeSetBuilder asb = new AttributeSetBuilder();
for(final Method method : c.getMethods()){
// This section of code based on
// com.sun.jmx.mbeanserver.Introspector.introspect(Class baseClass, Class beanClass)
// in JDK 1.5.
final String name = method.getName();
final Class<?>[] paramTypes = method.getParameterTypes();
final Class<?> returnType = method.getReturnType();
final int paramCount = paramTypes.length;
if(name.startsWith("get") && !"get".equals(name)
&& paramCount == 0 && !void.class.equals(returnType)){
asb.setGetter(name.substring(3), method);
}else if(name.startsWith("set") && !"set".equals(name)
&& paramCount == 1 && void.class.equals(returnType)){
asb.get(name.substring(3)).setter = method;
}else if(name.startsWith("is") && !"is".equals(name)
&& paramCount == 0 && boolean.class.equals(returnType)){
asb.setGetter(name.substring(2), method);
}else{
operations.add(mapOperation(method, name, paramTypes));
}
}
final MBeanAttributeInfo[] attributes = mapAttributes(asb);
return new MBeanInfo(className, description, attributes, null,
operations.toArray(new MBeanOperationInfo[operations.size()]),
null);
}
protected String mapName(final Class<?> c){
return c.getName();
}
protected String mapDescription(final Class<?> c){
String description = mapName(c);
final MBeanDescription descriptionA = c.getAnnotation(MBeanDescription.class);
if(descriptionA != null){
description = descriptionA.value();
}
return description;
}
protected MBeanAttributeInfo[] mapAttributes(final AttributeSetBuilder asb)
throws OperationsException{
final MBeanAttributeInfo[] attributes = new MBeanAttributeInfo[asb.attributeSets.size()];
int attributeIdx = 0;
for(final Map.Entry<String, AttributeSet> ase : asb.attributeSets.entrySet()){
final AttributeSet as = ase.getValue();
MBeanDescription descAtt = null;
if(as.getter != null){
descAtt = as.getter.getAnnotation(MBeanDescription.class);
}
if(as.setter != null){
final MBeanDescription setDescAtt = as.setter.getAnnotation(MBeanDescription.class);
if(setDescAtt != null){
if(descAtt != null){
throw new NotCompliantMBeanException(MBeanDescription.class.getName()
+ " must be set on getter when both getter and setter exist.");
}
descAtt = setDescAtt;
}
}
final String description = descAtt == null ? ase.getKey() : descAtt.value();
// This MBeanAttributeInfo constructor does a bit of additional rule checking.
attributes[attributeIdx++] = new MBeanAttributeInfo(ase.getKey(), description, as.getter, as.setter);
}
return attributes;
}
protected MBeanOperationInfo mapOperation(final Method m, final String name, final Class<?>[] paramTypes)
throws OperationsException{
String description = name;
final MBeanParameterInfo[] signature = new MBeanParameterInfo[paramTypes.length];
int impact = MBeanOperationInfo.UNKNOWN;
final MBeanDescription descAtt = m.getAnnotation(MBeanDescription.class);
if(descAtt != null){
description = descAtt.value();
}
final MBeanOperation operAtt = m.getAnnotation(MBeanOperation.class);
boolean signaturePopulated = false;
if(operAtt != null){
impact = operAtt.impact();
final MBeanParameter[] paramAtts = operAtt.value();
if(paramAtts.length > 0){
if(paramAtts.length != signature.length){
throw new NotCompliantMBeanException(MBeanParameter.class.getName()
+ " length of " + paramAtts.length + " does not match signature: " + m + ".");
}
for(int i=0; i < paramTypes.length; i++){
final MBeanParameter paramAtt = paramAtts[i];
String paramDesc = paramAtt.description();
if(paramDesc == null || paramDesc.trim().length() == 0){
paramDesc = paramAtt.value();
}
signature[i] = new MBeanParameterInfo(
paramAtt.value(), paramTypes[i].getName(), paramDesc);
}
signaturePopulated = true;
}
}
if(!signaturePopulated){
for(int i=0; i < paramTypes.length; i++){
final String paramName = "p" + (i + 1);
signature[i] = new MBeanParameterInfo(
paramName, paramTypes[i].getName(), paramName);
}
}
return new MBeanOperationInfo(
name, description,
signature, m.getReturnType().getName(), impact);
}
protected static class AttributeSet{
public Method getter;
public Method setter;
}
protected static class AttributeSetBuilder{
public final Map<String, AttributeSet> attributeSets = new HashMap<String, AttributeSet>();
public AttributeSet get(final String name){
AttributeSet s = attributeSets.get(name);
if(s == null){
s = new AttributeSet();
attributeSets.put(name, s);
}
return s;
}
public void setGetter(final String name, final Method getter) throws OperationsException{
final AttributeSet s = get(name);
if(s.getter != null){
throw new NotCompliantMBeanException("Conflicting getters for attribute " + name);
}
s.getter = getter;
}
}
}