1492 lines · 61.7 KB · Java
RawDownload
/* * 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

Lines 1–500 of 1492.