OpenTypeBuilder.java
package com.ziesemer.utils.jmx.server.beta;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import javax.management.JMException;
import javax.management.ObjectName;
import javax.management.openmbean.ArrayType;
import javax.management.openmbean.CompositeType;
import javax.management.openmbean.OpenDataException;
import javax.management.openmbean.OpenType;
import javax.management.openmbean.SimpleType;
import javax.management.openmbean.TabularType;
import com.ziesemer.utils.jmx.beanInfo.MBeanDescription;
/**
* <p>An <b>incomplete</b> replacement implementation for converting types to be
* used for OpenMBeans / MXBeans.
* Originally designed to provide some level of backwards-compatible support
* for MXBeans under Java 1.5/5.0.</p>
* <p>Notably, does not handle "Mappings for MXBean interfaces".
* Also, despite a strong initial set of JUnit tests, there are lots of rules,
* exceptions, and other inconsistencies in the specification, and I am not
* confident that I have covered everything.</p>
* <p>This class only provides methods for assisting with the one-way
* mapping from classes to {@link OpenType}s, with no methods currently
* available to help convert back to the original class types.
* Part of the difficulty in this is that Java 1.5/5.0 does not provide
* the <code>java.beans.ConstructorProperties</code> annotation, or
* <code>javax.management.openmbean.CompositeDataInvocationHandler</code>.
* It also does nothing to create or read from
* {@link javax.management.openmbean.CompositeData} instances.</p>
* @author Mark A. Ziesemer
* <a href="http://www.ziesemer.com."><www.ziesemer.com></a>
*/
public class OpenTypeBuilder{
public static final Map<Class<?>, SimpleType> SIMPLE_TYPES_MAP;
static{
final Map<Class<?>, SimpleType> simpleTypesMap = new HashMap<Class<?>, SimpleType>(32);
simpleTypesMap.put(boolean.class, SimpleType.BOOLEAN);
simpleTypesMap.put(Boolean.class, SimpleType.BOOLEAN);
simpleTypesMap.put(byte.class, SimpleType.BYTE);
simpleTypesMap.put(Byte.class, SimpleType.BYTE);
simpleTypesMap.put(char.class, SimpleType.CHARACTER);
simpleTypesMap.put(Character.class, SimpleType.CHARACTER);
simpleTypesMap.put(short.class, SimpleType.SHORT);
simpleTypesMap.put(Short.class, SimpleType.SHORT);
simpleTypesMap.put(int.class, SimpleType.INTEGER);
simpleTypesMap.put(Integer.class, SimpleType.INTEGER);
simpleTypesMap.put(long.class, SimpleType.LONG);
simpleTypesMap.put(Long.class, SimpleType.LONG);
simpleTypesMap.put(float.class, SimpleType.FLOAT);
simpleTypesMap.put(Float.class, SimpleType.FLOAT);
simpleTypesMap.put(double.class, SimpleType.DOUBLE);
simpleTypesMap.put(Double.class, SimpleType.DOUBLE);
simpleTypesMap.put(void.class, SimpleType.VOID);
simpleTypesMap.put(Void.class, SimpleType.VOID);
simpleTypesMap.put(BigDecimal.class, SimpleType.BIGDECIMAL);
simpleTypesMap.put(BigInteger.class, SimpleType.BIGINTEGER);
simpleTypesMap.put(Date.class, SimpleType.DATE);
simpleTypesMap.put(String.class, SimpleType.STRING);
simpleTypesMap.put(ObjectName.class, SimpleType.OBJECTNAME);
SIMPLE_TYPES_MAP = Collections.unmodifiableMap(simpleTypesMap);
}
public static CompositeType buildCompositeType(final Class<?> c) throws JMException{
return buildCompositeType(c, new LinkedHashSet<Class<?>>());
}
protected static CompositeType buildCompositeType(
final Class<?> c, final Set<Class<?>> typeChain) throws OpenDataException{
final List<String> itemNames = new LinkedList<String>();
final List<String> itemDescriptions = new LinkedList<String>();
final List<OpenType> itemTypes = new LinkedList<OpenType>();
if(!typeChain.add(c)){
final List<Class<?>> errorTypeChain = new ArrayList<Class<?>>(typeChain.size() + 1);
errorTypeChain.addAll(typeChain);
errorTypeChain.add(c);
throw new OpenDataException("Recursive data structure: " + errorTypeChain);
}
for(final Method m : c.getMethods()){
if(Modifier.isStatic(m.getModifiers())
|| !Modifier.isPublic(m.getModifiers())){
continue;
}
final String mName = m.getName();
final Class<?>[] paramTypes = m.getParameterTypes();
final int paramCount = paramTypes.length;
final Class<?> rt = m.getReturnType();
String aName;
if(mName.startsWith("get") && !"get".equals(mName)
&& paramCount == 0 && !void.class.equals(rt)){
aName = mName.substring(3);
if("Class".equals(aName) && paramCount == 0){
continue;
}
}else if(mName.startsWith("is") && !"is".equals(mName)
&& paramCount == 0 && boolean.class.equals(rt)){
aName = mName.substring(2);
}else{
continue;
}
if(aName.length() > 1){
aName = Character.toLowerCase(aName.charAt(0))
+ aName.substring(1);
}
String aDescription = aName;
final MBeanDescription descriptionA = m.getAnnotation(MBeanDescription.class);
if(descriptionA != null){
aDescription = descriptionA.value();
}
OpenType ot = SIMPLE_TYPES_MAP.get(rt);
if(ot != null){
// "Mappings for primitive types"
// Nothing special to do.
}else if(rt.isArray()){
Class<?> ct = rt;
int dimension = 0;
while(ct.isArray()){
ct = ct.getComponentType();
dimension++;
}
ot = new ArrayType(dimension, getOpenType(ct, typeChain));
}else if(List.class.isAssignableFrom(rt) || Set.class.isAssignableFrom(rt)){
// "Mappings for collections"
final Class<?> grt = findGenericType(m.getGenericReturnType());
if(grt != null){
if(SortedSet.class.isAssignableFrom(rt) && !Comparable.class.isAssignableFrom(grt)){
throw new OpenDataException("Parameter " + grt + " of " + rt.getName()
+ " does not implement " + Comparable.class.getName() + ".");
}
ot = new ArrayType(1, getOpenType(grt, typeChain));
}else{
throw new OpenDataException("Cannot map type: " + m.getGenericReturnType());
}
}else if(Map.class.isAssignableFrom(rt)){
// "Mappings for maps"
final Type grt = m.getGenericReturnType();
if(grt instanceof ParameterizedType){
final String typeName = m.getGenericReturnType().toString();
final String[] keyValue = new String[]{"key", "value"};
final Type[] ats = ((ParameterizedType)grt).getActualTypeArguments();
if(SortedMap.class.isAssignableFrom(rt)
&& !Comparable.class.isAssignableFrom((Class<?>)ats[0])){
throw new OpenDataException("Parameter " + grt + " of " + rt.getName()
+ " does not implement " + Comparable.class.getName() + ".");
}
final OpenType[] openTypes = new OpenType[ats.length]; // Should always be 2.
for(int i=0; i < ats.length; i++){
openTypes[i] = getOpenType((Class<?>)ats[i], typeChain);
}
final CompositeType rowType = new CompositeType(typeName, typeName, keyValue, keyValue, openTypes);
ot = new TabularType(typeName, typeName, rowType, new String[]{"key"});
}else{
throw new OpenDataException("Cannot map type: " + grt);
}
}else if(rt.isEnum()){
ot = SimpleType.STRING;
}else{
// "Mappings for other types"
ot = buildCompositeType(rt, typeChain);
}
itemNames.add(aName);
itemDescriptions.add(aDescription);
itemTypes.add(ot);
}
typeChain.remove(c);
String cDescription = c.getName();
final MBeanDescription cDescriptionA = c.getAnnotation(MBeanDescription.class);
if(cDescriptionA != null){
cDescription = cDescriptionA.value();
}
final CompositeType ct = new CompositeType(
c.getName(),
cDescription,
itemNames.toArray(new String[itemNames.size()]),
itemDescriptions.toArray(new String[itemDescriptions.size()]),
itemTypes.toArray(new OpenType[itemTypes.size()]));
return ct;
}
public static OpenType getOpenType(final Class<?> c) throws OpenDataException{
return getOpenType(c, new LinkedHashSet<Class<?>>());
}
protected static OpenType getOpenType(final Class<?> c, final Set<Class<?>> typeChain) throws OpenDataException{
OpenType ot = SIMPLE_TYPES_MAP.get(c);
if(ot == null){
ot = buildCompositeType(c, typeChain);
}
return ot;
}
protected static Class<?> findGenericType(final Type t){
if(t instanceof ParameterizedType){
final ParameterizedType pt = (ParameterizedType)t;
final Type[] ats = pt.getActualTypeArguments();
if(ats.length == 1){
final Type at = ats[0];
if(at instanceof Class<?>){
return (Class<?>)at;
}
}
}
return null;
}
}