/* * Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.jmx.mbeanserver; import static com.sun.jmx.mbeanserver.Util.*;import static com.sun.jmx.mbeanserver.MXBeanIntrospector.typeName; import static javax.management.openmbean.SimpleType.*; import com.sun.jmx.remote.util.EnvHelp; import java.beans.ConstructorProperties;import java.io.InvalidObjectException;import java.lang.annotation.ElementType;import java.lang.ref.WeakReference;import java.lang.reflect.Array;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.GenericArrayType;import java.lang.reflect.Method;import java.lang.reflect.Modifier;import java.lang.reflect.ParameterizedType;import java.lang.reflect.Proxy;import java.lang.reflect.Type;import java.util.ArrayList;import java.util.Arrays;import java.util.BitSet;import java.util.Collection;import java.util.Comparator;import java.util.HashSet;import java.util.List;import java.util.Map;import java.util.Set;import java.util.SortedMap;import java.util.SortedSet;import java.util.TreeSet;import java.util.WeakHashMap; import javax.management.JMX;import javax.management.ObjectName;import javax.management.openmbean.ArrayType;import javax.management.openmbean.CompositeData;import javax.management.openmbean.CompositeDataInvocationHandler;import javax.management.openmbean.CompositeDataSupport;import javax.management.openmbean.CompositeDataView;import javax.management.openmbean.CompositeType;import javax.management.openmbean.OpenDataException;import javax.management.openmbean.OpenType;import javax.management.openmbean.SimpleType;import javax.management.openmbean.TabularData;import javax.management.openmbean.TabularDataSupport;import javax.management.openmbean.TabularType; /** * <p>A converter between Java types and the limited set of classes * defined by Open MBeans.</p> * * <p>A Java type is an instance of java.lang.reflect.Type. For our * purposes, it is either a Class, such as String.class or int.class; * or a ParameterizedType, such as List<String> or Map<Integer, * String[]>. On J2SE 1.4 and earlier, it can only be a Class.</p> * * <p>Each Type is associated with an DefaultMXBeanMappingFactory. The * DefaultMXBeanMappingFactory defines an OpenType corresponding to the Type, plus a * Java class corresponding to the OpenType. For example:</p> * * <pre> * Type Open class OpenType * ---- ---------- -------- * Integer Integer SimpleType.INTEGER * int int SimpleType.INTEGER * Integer[] Integer[] ArrayType(1, SimpleType.INTEGER) * int[] Integer[] ArrayType(SimpleType.INTEGER, true) * String[][] String[][] ArrayType(2, SimpleType.STRING) * List<String> String[] ArrayType(1, SimpleType.STRING) * ThreadState (an Enum) String SimpleType.STRING * Map<Integer, String[]> TabularData TabularType( * CompositeType( * {"key", SimpleType.INTEGER}, * {"value", * ArrayType(1, * SimpleType.STRING)}), * indexNames={"key"}) * </pre> * * <p>Apart from simple types, arrays, and collections, Java types are * converted through introspection into CompositeType. The Java type * must have at least one getter (method such as "int getSize()" or * "boolean isBig()"), and we must be able to deduce how to * reconstruct an instance of the Java class from the values of the * getters using one of various heuristics.</p> * * @since 1.6 */public class DefaultMXBeanMappingFactory extends MXBeanMappingFactory { static abstract class NonNullMXBeanMapping extends MXBeanMapping { NonNullMXBeanMapping(Type javaType, OpenType<?> openType) { super(javaType, openType); } @Override public final Object fromOpenValue(Object openValue) throws InvalidObjectException { if (openValue == null) return null; else return fromNonNullOpenValue(openValue); } @Override public final Object toOpenValue(Object javaValue) throws OpenDataException { if (javaValue == null) return null; else return toNonNullOpenValue(javaValue); } abstract Object fromNonNullOpenValue(Object openValue) throws InvalidObjectException; abstract Object toNonNullOpenValue(Object javaValue) throws OpenDataException; /** * <p>True if and only if this MXBeanMapping's toOpenValue and * fromOpenValue methods are the identity function.</p> */ boolean isIdentity() { return false; } } static boolean isIdentity(MXBeanMapping mapping) { return (mapping instanceof NonNullMXBeanMapping && ((NonNullMXBeanMapping) mapping).isIdentity()); } private static final class Mappings extends WeakHashMap<Type, WeakReference<MXBeanMapping>> {} private static final Mappings mappings = new Mappings(); /** Following List simply serves to keep a reference to predefined MXBeanMappings so they don't get garbage collected. */ private static final List<MXBeanMapping> permanentMappings = newList(); private static synchronized MXBeanMapping getMapping(Type type) { WeakReference<MXBeanMapping> wr = mappings.get(type); return (wr == null) ? null : wr.get(); } private static synchronized void putMapping(Type type, MXBeanMapping mapping) { WeakReference<MXBeanMapping> wr = new WeakReference<MXBeanMapping>(mapping); mappings.put(type, wr); } private static synchronized void putPermanentMapping( Type type, MXBeanMapping mapping) { putMapping(type, mapping); permanentMappings.add(mapping); } static { /* Set up the mappings for Java types that map to SimpleType. */ final OpenType<?>[] simpleTypes = { BIGDECIMAL, BIGINTEGER, BOOLEAN, BYTE, CHARACTER, DATE, DOUBLE, FLOAT, INTEGER, LONG, OBJECTNAME, SHORT, STRING, VOID, }; for (int i = 0; i < simpleTypes.length; i++) { final OpenType<?> t = simpleTypes[i]; Class<?> c; try { c = Class.forName(t.getClassName(), false, ObjectName.class.getClassLoader()); } catch (ClassNotFoundException e) { // the classes that these predefined types declare must exist! throw new Error(e); } final MXBeanMapping mapping = new IdentityMapping(c, t); putPermanentMapping(c, mapping); if (c.getName().startsWith("java.lang.")) { try { final Field typeField = c.getField("TYPE"); final Class<?> primitiveType = (Class<?>) typeField.get(null); final MXBeanMapping primitiveMapping = new IdentityMapping(primitiveType, t); putPermanentMapping(primitiveType, primitiveMapping); if (primitiveType != void.class) { final Class<?> primitiveArrayType = Array.newInstance(primitiveType, 0).getClass(); final OpenType<?> primitiveArrayOpenType = ArrayType.getPrimitiveArrayType(primitiveArrayType); final MXBeanMapping primitiveArrayMapping = new IdentityMapping(primitiveArrayType, primitiveArrayOpenType); putPermanentMapping(primitiveArrayType, primitiveArrayMapping); } } catch (NoSuchFieldException e) { // OK: must not be a primitive wrapper } catch (IllegalAccessException e) { // Should not reach here assert(false); } } } } /** Get the converter for the given Java type, creating it if necessary. */ @Override public synchronized MXBeanMapping mappingForType(Type objType, MXBeanMappingFactory factory) throws OpenDataException { if (inProgress.containsKey(objType)) { throw new OpenDataException( "Recursive data structure, including " + typeName(objType)); } MXBeanMapping mapping; mapping = getMapping(objType); if (mapping != null) return mapping; inProgress.put(objType, objType); try { mapping = makeMapping(objType, factory); } catch (OpenDataException e) { throw openDataException("Cannot convert type: " + typeName(objType), e); } finally { inProgress.remove(objType); } putMapping(objType, mapping); return mapping; } private MXBeanMapping makeMapping(Type objType, MXBeanMappingFactory factory) throws OpenDataException { /* It's not yet worth formalizing these tests by having for example an array of factory classes, each of which says whether it recognizes the Type (Chain of Responsibility pattern). */ if (objType instanceof GenericArrayType) { Type componentType = ((GenericArrayType) objType).getGenericComponentType(); return makeArrayOrCollectionMapping(objType, componentType, factory); } else if (objType instanceof Class<?>) { Class<?> objClass = (Class<?>) objType; if (objClass.isEnum()) { // Huge hack to avoid compiler warnings here. The ElementType // parameter is ignored but allows us to obtain a type variable // T that matches <T extends Enum<T>>. return makeEnumMapping((Class<?>) objClass, ElementType.class); } else if (objClass.isArray()) { Type componentType = objClass.getComponentType(); return makeArrayOrCollectionMapping(objClass, componentType, factory); } else if (JMX.isMXBeanInterface(objClass)) { return makeMXBeanRefMapping(objClass); } else { return makeCompositeMapping(objClass, factory); } } else if (objType instanceof ParameterizedType) { return makeParameterizedTypeMapping((ParameterizedType) objType, factory); } else throw new OpenDataException("Cannot map type: " + objType); } private static <T extends Enum<T>> MXBeanMapping makeEnumMapping(Class<?> enumClass, Class<T> fake) { return new EnumMapping<T>(Util.<Class<T>>cast(enumClass)); } /* Make the converter for an array type, or a collection such as * List<String> or Set<Integer>. We never see one-dimensional * primitive arrays (e.g. int[]) here because they use the identity * converter and are registered as such in the static initializer. */ private MXBeanMapping makeArrayOrCollectionMapping(Type collectionType, Type elementType, MXBeanMappingFactory factory) throws OpenDataException { final MXBeanMapping elementMapping = factory.mappingForType(elementType, factory); final OpenType<?> elementOpenType = elementMapping.getOpenType(); final ArrayType<?> openType = ArrayType.getArrayType(elementOpenType); final Class<?> elementOpenClass = elementMapping.getOpenClass(); final Class<?> openArrayClass; final String openArrayClassName; if (elementOpenClass.isArray()) openArrayClassName = "[" + elementOpenClass.getName(); else openArrayClassName = "[L" + elementOpenClass.getName() + ";"; try { openArrayClass = Class.forName(openArrayClassName); } catch (ClassNotFoundException e) { throw openDataException("Cannot obtain array class", e); } if (collectionType instanceof ParameterizedType) { return new CollectionMapping(collectionType, openType, openArrayClass, elementMapping); } else { if (isIdentity(elementMapping)) { return new IdentityMapping(collectionType, openType); } else { return new ArrayMapping(collectionType, openType, openArrayClass, elementMapping); } } } private static final String[] keyArray = {"key"}; private static final String[] keyValueArray = {"key", "value"}; private MXBeanMapping makeTabularMapping(Type objType, boolean sortedMap, Type keyType, Type valueType, MXBeanMappingFactory factory) throws OpenDataException { final String objTypeName = typeName(objType); final MXBeanMapping keyMapping = factory.mappingForType(keyType, factory); final MXBeanMapping valueMapping = factory.mappingForType(valueType, factory); final OpenType<?> keyOpenType = keyMapping.getOpenType(); final OpenType<?> valueOpenType = valueMapping.getOpenType(); final CompositeType rowType = new CompositeType(objTypeName, objTypeName, keyValueArray, keyValueArray, new OpenType<?>[] {keyOpenType, valueOpenType}); final TabularType tabularType = new TabularType(objTypeName, objTypeName, rowType, keyArray); return new TabularMapping(objType, sortedMap, tabularType, keyMapping, valueMapping); } /* We know how to translate List<E>, Set<E>, SortedSet<E>, Map<K,V>, SortedMap<K,V>, and that's it. We don't accept subtypes of those because we wouldn't know how to deserialize them. We don't accept Queue<E> because it is unlikely people would use that as a parameter or return type in an MBean. */ private MXBeanMapping makeParameterizedTypeMapping(ParameterizedType objType, MXBeanMappingFactory factory) throws OpenDataException { final Type rawType = objType.getRawType(); if (rawType instanceof Class<?>) { Class<?> c = (Class<?>) rawType; if (c == List.class || c == Set.class || c == SortedSet.class) { Type[] actuals = objType.getActualTypeArguments(); assert(actuals.length == 1); if (c == SortedSet.class) mustBeComparable(c, actuals[0]); return makeArrayOrCollectionMapping(objType, actuals[0], factory); } else { boolean sortedMap = (c == SortedMap.class); if (c == Map.class || sortedMap) { Type[] actuals = objType.getActualTypeArguments(); assert(actuals.length == 2); if (sortedMap) mustBeComparable(c, actuals[0]); return makeTabularMapping(objType, sortedMap, actuals[0], actuals[1], factory); } } } throw new OpenDataException("Cannot convert type: " + objType); } private static MXBeanMapping makeMXBeanRefMapping(Type t) throws OpenDataException { return new MXBeanRefMapping(t); } private MXBeanMapping makeCompositeMapping(Class<?> c, MXBeanMappingFactory factory) throws OpenDataException { // For historical reasons GcInfo implements CompositeData but we // shouldn't count its CompositeData.getCompositeType() field as // an item in the computed CompositeType. final boolean gcInfoHack = (c.getName().equals("com.sun.management.GcInfo") && c.getClassLoader() == null); final List<Method> methods = MBeanAnalyzer.eliminateCovariantMethods(Arrays.asList(c.getMethods())); final SortedMap<String,Method> getterMap = newSortedMap(); /* Select public methods that look like "T getX()" or "boolean isX()", where T is not void and X is not the empty string. Exclude "Class getClass()" inherited from Object. */ for (Method method : methods) { final String propertyName = propertyName(method); if (propertyName == null) continue; if (gcInfoHack && propertyName.equals("CompositeType")) continue; Method old = getterMap.put(decapitalize(propertyName), method); if (old != null) { final String msg = "Class " + c.getName() + " has method name clash: " + old.getName() + ", " + method.getName(); throw new OpenDataException(msg); } } final int nitems = getterMap.size(); if (nitems == 0) { throw new OpenDataException("Can't map " + c.getName() + " to an open data type"); } final Method[] getters = new Method[nitems]; final String[] itemNames = new String[nitems]; final OpenType<?>[] openTypes = new OpenType<?>[nitems]; int i = 0; for (Map.Entry<String,Method> entry : getterMap.entrySet()) { itemNames[i] = entry.getKey(); final Method getter = entry.getValue(); getters[i] = getter; final Type retType = getter.getGenericReturnType(); openTypes[i] = factory.mappingForType(retType, factory).getOpenType(); i++; } CompositeType compositeType = new CompositeType(c.getName(), c.getName(), itemNames, // field names itemNames, // field descriptions openTypes); return new CompositeMapping(c, compositeType, itemNames, getters, factory); } /* Converter for classes where the open data is identical to the original data. This is true for any of the SimpleType types, and for an any-dimension array of those. It is also true for primitive types as of JMX 1.3, since an int[] can be directly represented by an ArrayType, and an int needs no mapping because reflection takes care of it. */ private static final class IdentityMapping extends NonNullMXBeanMapping { IdentityMapping(Type targetType, OpenType<?> openType) { super(targetType, openType); } boolean isIdentity() { return true; } @OverrideLines 1–500 of 1492. Load more
/* * Copyright (c) 2005, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.jmx.mbeanserver; import static com.sun.jmx.mbeanserver.Util.*; import static com.sun.jmx.mbeanserver.MXBeanIntrospector.typeName; import static javax.management.openmbean.SimpleType.*; import com.sun.jmx.remote.util.EnvHelp; import java.beans.ConstructorProperties; import java.io.InvalidObjectException; import java.lang.annotation.ElementType; import java.lang.ref.WeakReference; import java.lang.reflect.Array; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.GenericArrayType; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Proxy; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.Collection; import java.util.Comparator; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeSet; import java.util.WeakHashMap; import javax.management.JMX; import javax.management.ObjectName; import javax.management.openmbean.ArrayType; import javax.management.openmbean.CompositeData; import javax.management.openmbean.CompositeDataInvocationHandler; import javax.management.openmbean.CompositeDataSupport; import javax.management.openmbean.CompositeDataView; import javax.management.openmbean.CompositeType; import javax.management.openmbean.OpenDataException; import javax.management.openmbean.OpenType; import javax.management.openmbean.SimpleType; import javax.management.openmbean.TabularData; import javax.management.openmbean.TabularDataSupport; import javax.management.openmbean.TabularType; /** * <p>A converter between Java types and the limited set of classes * defined by Open MBeans.</p> * * <p>A Java type is an instance of java.lang.reflect.Type. For our * purposes, it is either a Class, such as String.class or int.class; * or a ParameterizedType, such as List<String> or Map<Integer, * String[]>. On J2SE 1.4 and earlier, it can only be a Class.</p> * * <p>Each Type is associated with an DefaultMXBeanMappingFactory. The * DefaultMXBeanMappingFactory defines an OpenType corresponding to the Type, plus a * Java class corresponding to the OpenType. For example:</p> * * <pre> * Type Open class OpenType * ---- ---------- -------- * Integer Integer SimpleType.INTEGER * int int SimpleType.INTEGER * Integer[] Integer[] ArrayType(1, SimpleType.INTEGER) * int[] Integer[] ArrayType(SimpleType.INTEGER, true) * String[][] String[][] ArrayType(2, SimpleType.STRING) * List<String> String[] ArrayType(1, SimpleType.STRING) * ThreadState (an Enum) String SimpleType.STRING * Map<Integer, String[]> TabularData TabularType( * CompositeType( * {"key", SimpleType.INTEGER}, * {"value", * ArrayType(1, * SimpleType.STRING)}), * indexNames={"key"}) * </pre> * * <p>Apart from simple types, arrays, and collections, Java types are * converted through introspection into CompositeType. The Java type * must have at least one getter (method such as "int getSize()" or * "boolean isBig()"), and we must be able to deduce how to * reconstruct an instance of the Java class from the values of the * getters using one of various heuristics.</p> * * @since 1.6 */ public class DefaultMXBeanMappingFactory extends MXBeanMappingFactory { static abstract class NonNullMXBeanMapping extends MXBeanMapping { NonNullMXBeanMapping(Type javaType, OpenType<?> openType) { super(javaType, openType); } @Override public final Object fromOpenValue(Object openValue) throws InvalidObjectException { if (openValue == null) return null; else return fromNonNullOpenValue(openValue); } @Override public final Object toOpenValue(Object javaValue) throws OpenDataException { if (javaValue == null) return null; else return toNonNullOpenValue(javaValue); } abstract Object fromNonNullOpenValue(Object openValue) throws InvalidObjectException; abstract Object toNonNullOpenValue(Object javaValue) throws OpenDataException; /** * <p>True if and only if this MXBeanMapping's toOpenValue and * fromOpenValue methods are the identity function.</p> */ boolean isIdentity() { return false; } } static boolean isIdentity(MXBeanMapping mapping) { return (mapping instanceof NonNullMXBeanMapping && ((NonNullMXBeanMapping) mapping).isIdentity()); } private static final class Mappings extends WeakHashMap<Type, WeakReference<MXBeanMapping>> {} private static final Mappings mappings = new Mappings(); /** Following List simply serves to keep a reference to predefined MXBeanMappings so they don't get garbage collected. */ private static final List<MXBeanMapping> permanentMappings = newList(); private static synchronized MXBeanMapping getMapping(Type type) { WeakReference<MXBeanMapping> wr = mappings.get(type); return (wr == null) ? null : wr.get(); } private static synchronized void putMapping(Type type, MXBeanMapping mapping) { WeakReference<MXBeanMapping> wr = new WeakReference<MXBeanMapping>(mapping); mappings.put(type, wr); } private static synchronized void putPermanentMapping( Type type, MXBeanMapping mapping) { putMapping(type, mapping); permanentMappings.add(mapping); } static { /* Set up the mappings for Java types that map to SimpleType. */ final OpenType<?>[] simpleTypes = { BIGDECIMAL, BIGINTEGER, BOOLEAN, BYTE, CHARACTER, DATE, DOUBLE, FLOAT, INTEGER, LONG, OBJECTNAME, SHORT, STRING, VOID, }; for (int i = 0; i < simpleTypes.length; i++) { final OpenType<?> t = simpleTypes[i]; Class<?> c; try { c = Class.forName(t.getClassName(), false, ObjectName.class.getClassLoader()); } catch (ClassNotFoundException e) { // the classes that these predefined types declare must exist! throw new Error(e); } final MXBeanMapping mapping = new IdentityMapping(c, t); putPermanentMapping(c, mapping); if (c.getName().startsWith("java.lang.")) { try { final Field typeField = c.getField("TYPE"); final Class<?> primitiveType = (Class<?>) typeField.get(null); final MXBeanMapping primitiveMapping = new IdentityMapping(primitiveType, t); putPermanentMapping(primitiveType, primitiveMapping); if (primitiveType != void.class) { final Class<?> primitiveArrayType = Array.newInstance(primitiveType, 0).getClass(); final OpenType<?> primitiveArrayOpenType = ArrayType.getPrimitiveArrayType(primitiveArrayType); final MXBeanMapping primitiveArrayMapping = new IdentityMapping(primitiveArrayType, primitiveArrayOpenType); putPermanentMapping(primitiveArrayType, primitiveArrayMapping); } } catch (NoSuchFieldException e) { // OK: must not be a primitive wrapper } catch (IllegalAccessException e) { // Should not reach here assert(false); } } } } /** Get the converter for the given Java type, creating it if necessary. */ @Override public synchronized MXBeanMapping mappingForType(Type objType, MXBeanMappingFactory factory) throws OpenDataException { if (inProgress.containsKey(objType)) { throw new OpenDataException( "Recursive data structure, including " + typeName(objType)); } MXBeanMapping mapping; mapping = getMapping(objType); if (mapping != null) return mapping; inProgress.put(objType, objType); try { mapping = makeMapping(objType, factory); } catch (OpenDataException e) { throw openDataException("Cannot convert type: " + typeName(objType), e); } finally { inProgress.remove(objType); } putMapping(objType, mapping); return mapping; } private MXBeanMapping makeMapping(Type objType, MXBeanMappingFactory factory) throws OpenDataException { /* It's not yet worth formalizing these tests by having for example an array of factory classes, each of which says whether it recognizes the Type (Chain of Responsibility pattern). */ if (objType instanceof GenericArrayType) { Type componentType = ((GenericArrayType) objType).getGenericComponentType(); return makeArrayOrCollectionMapping(objType, componentType, factory); } else if (objType instanceof Class<?>) { Class<?> objClass = (Class<?>) objType; if (objClass.isEnum()) { // Huge hack to avoid compiler warnings here. The ElementType // parameter is ignored but allows us to obtain a type variable // T that matches <T extends Enum<T>>. return makeEnumMapping((Class<?>) objClass, ElementType.class); } else if (objClass.isArray()) { Type componentType = objClass.getComponentType(); return makeArrayOrCollectionMapping(objClass, componentType, factory); } else if (JMX.isMXBeanInterface(objClass)) { return makeMXBeanRefMapping(objClass); } else { return makeCompositeMapping(objClass, factory); } } else if (objType instanceof ParameterizedType) { return makeParameterizedTypeMapping((ParameterizedType) objType, factory); } else throw new OpenDataException("Cannot map type: " + objType); } private static <T extends Enum<T>> MXBeanMapping makeEnumMapping(Class<?> enumClass, Class<T> fake) { return new EnumMapping<T>(Util.<Class<T>>cast(enumClass)); } /* Make the converter for an array type, or a collection such as * List<String> or Set<Integer>. We never see one-dimensional * primitive arrays (e.g. int[]) here because they use the identity * converter and are registered as such in the static initializer. */ private MXBeanMapping makeArrayOrCollectionMapping(Type collectionType, Type elementType, MXBeanMappingFactory factory) throws OpenDataException { final MXBeanMapping elementMapping = factory.mappingForType(elementType, factory); final OpenType<?> elementOpenType = elementMapping.getOpenType(); final ArrayType<?> openType = ArrayType.getArrayType(elementOpenType); final Class<?> elementOpenClass = elementMapping.getOpenClass(); final Class<?> openArrayClass; final String openArrayClassName; if (elementOpenClass.isArray()) openArrayClassName = "[" + elementOpenClass.getName(); else openArrayClassName = "[L" + elementOpenClass.getName() + ";"; try { openArrayClass = Class.forName(openArrayClassName); } catch (ClassNotFoundException e) { throw openDataException("Cannot obtain array class", e); } if (collectionType instanceof ParameterizedType) { return new CollectionMapping(collectionType, openType, openArrayClass, elementMapping); } else { if (isIdentity(elementMapping)) { return new IdentityMapping(collectionType, openType); } else { return new ArrayMapping(collectionType, openType, openArrayClass, elementMapping); } } } private static final String[] keyArray = {"key"}; private static final String[] keyValueArray = {"key", "value"}; private MXBeanMapping makeTabularMapping(Type objType, boolean sortedMap, Type keyType, Type valueType, MXBeanMappingFactory factory) throws OpenDataException { final String objTypeName = typeName(objType); final MXBeanMapping keyMapping = factory.mappingForType(keyType, factory); final MXBeanMapping valueMapping = factory.mappingForType(valueType, factory); final OpenType<?> keyOpenType = keyMapping.getOpenType(); final OpenType<?> valueOpenType = valueMapping.getOpenType(); final CompositeType rowType = new CompositeType(objTypeName, objTypeName, keyValueArray, keyValueArray, new OpenType<?>[] {keyOpenType, valueOpenType}); final TabularType tabularType = new TabularType(objTypeName, objTypeName, rowType, keyArray); return new TabularMapping(objType, sortedMap, tabularType, keyMapping, valueMapping); } /* We know how to translate List<E>, Set<E>, SortedSet<E>, Map<K,V>, SortedMap<K,V>, and that's it. We don't accept subtypes of those because we wouldn't know how to deserialize them. We don't accept Queue<E> because it is unlikely people would use that as a parameter or return type in an MBean. */ private MXBeanMapping makeParameterizedTypeMapping(ParameterizedType objType, MXBeanMappingFactory factory) throws OpenDataException { final Type rawType = objType.getRawType(); if (rawType instanceof Class<?>) { Class<?> c = (Class<?>) rawType; if (c == List.class || c == Set.class || c == SortedSet.class) { Type[] actuals = objType.getActualTypeArguments(); assert(actuals.length == 1); if (c == SortedSet.class) mustBeComparable(c, actuals[0]); return makeArrayOrCollectionMapping(objType, actuals[0], factory); } else { boolean sortedMap = (c == SortedMap.class); if (c == Map.class || sortedMap) { Type[] actuals = objType.getActualTypeArguments(); assert(actuals.length == 2); if (sortedMap) mustBeComparable(c, actuals[0]); return makeTabularMapping(objType, sortedMap, actuals[0], actuals[1], factory); } } } throw new OpenDataException("Cannot convert type: " + objType); } private static MXBeanMapping makeMXBeanRefMapping(Type t) throws OpenDataException { return new MXBeanRefMapping(t); } private MXBeanMapping makeCompositeMapping(Class<?> c, MXBeanMappingFactory factory) throws OpenDataException { // For historical reasons GcInfo implements CompositeData but we // shouldn't count its CompositeData.getCompositeType() field as // an item in the computed CompositeType. final boolean gcInfoHack = (c.getName().equals("com.sun.management.GcInfo") && c.getClassLoader() == null); final List<Method> methods = MBeanAnalyzer.eliminateCovariantMethods(Arrays.asList(c.getMethods())); final SortedMap<String,Method> getterMap = newSortedMap(); /* Select public methods that look like "T getX()" or "boolean isX()", where T is not void and X is not the empty string. Exclude "Class getClass()" inherited from Object. */ for (Method method : methods) { final String propertyName = propertyName(method); if (propertyName == null) continue; if (gcInfoHack && propertyName.equals("CompositeType")) continue; Method old = getterMap.put(decapitalize(propertyName), method); if (old != null) { final String msg = "Class " + c.getName() + " has method name clash: " + old.getName() + ", " + method.getName(); throw new OpenDataException(msg); } } final int nitems = getterMap.size(); if (nitems == 0) { throw new OpenDataException("Can't map " + c.getName() + " to an open data type"); } final Method[] getters = new Method[nitems]; final String[] itemNames = new String[nitems]; final OpenType<?>[] openTypes = new OpenType<?>[nitems]; int i = 0; for (Map.Entry<String,Method> entry : getterMap.entrySet()) { itemNames[i] = entry.getKey(); final Method getter = entry.getValue(); getters[i] = getter; final Type retType = getter.getGenericReturnType(); openTypes[i] = factory.mappingForType(retType, factory).getOpenType(); i++; } CompositeType compositeType = new CompositeType(c.getName(), c.getName(), itemNames, // field names itemNames, // field descriptions openTypes); return new CompositeMapping(c, compositeType, itemNames, getters, factory); } /* Converter for classes where the open data is identical to the original data. This is true for any of the SimpleType types, and for an any-dimension array of those. It is also true for primitive types as of JMX 1.3, since an int[] can be directly represented by an ArrayType, and an int needs no mapping because reflection takes care of it. */ private static final class IdentityMapping extends NonNullMXBeanMapping { IdentityMapping(Type targetType, OpenType<?> openType) { super(targetType, openType); } boolean isIdentity() { return true; } @Override