/* * Licensed to the Apache Software Foundation (ASF) under one or more contributor license * agreements. See the NOTICE file distributed with this work for additional information regarding * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. You may obtain a * copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed under the License * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express * or implied. See the License for the specific language governing permissions and limitations under * the License. */ package org.apache.geode.management.internal; import static javax.management.openmbean.SimpleType.BIGDECIMAL; import static javax.management.openmbean.SimpleType.BIGINTEGER; import static javax.management.openmbean.SimpleType.BOOLEAN; import static javax.management.openmbean.SimpleType.BYTE; import static javax.management.openmbean.SimpleType.CHARACTER; import static javax.management.openmbean.SimpleType.DATE; import static javax.management.openmbean.SimpleType.DOUBLE; import static javax.management.openmbean.SimpleType.FLOAT; import static javax.management.openmbean.SimpleType.INTEGER; import static javax.management.openmbean.SimpleType.LONG; import static javax.management.openmbean.SimpleType.OBJECTNAME; import static javax.management.openmbean.SimpleType.SHORT; import static javax.management.openmbean.SimpleType.STRING; import static javax.management.openmbean.SimpleType.VOID; import java.beans.ConstructorProperties; import java.io.InvalidObjectException; 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.Arrays; import java.util.BitSet; 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.CompositeType; import javax.management.openmbean.OpenDataException; import javax.management.openmbean.OpenType; import javax.management.openmbean.TabularType; import org.apache.geode.management.ManagementException; /** * It takes care of converting a Java type to an open types * * A Java type is an instance of java.lang.reflect.Type representing all types in Java. * * Each Type is associated with an OpenTypeConverter. The OpenTypeConverter defines an OpenType * corresponding to the Type, plus a Java class corresponding to the OpenType. For example: * * * Java Type : Integer Open Class :Integer Open Type :SimpleType.INTEGER * * Apart from simple types, arrays, and collections, Java types are converted through introspection * into CompositeType * * */ public abstract class OpenTypeConverter { private final Type targetType; /* * The Java class corresponding to getOpenType(). This is the class named by * getOpenType().getClassName(), except that it may be a primitive type or an array of primitive * type. */ private final OpenType openType; private final Class openClass; private static final class ConverterMap extends WeakHashMap<Type, WeakReference<OpenTypeConverter>> { } private static final ConverterMap converterMap = new ConverterMap(); private final static Map<Type, Type> inProgress = OpenTypeUtil.newIdentityHashMap(); /** * Following List simply serves to keep a reference to predefined OpenConverters so they don't get * garbage collected. */ private static final List<OpenTypeConverter> preDefinedConverters = OpenTypeUtil.newList(); protected OpenTypeConverter(Type targetType, OpenType openType, Class openClass) { this.targetType = targetType; this.openType = openType; this.openClass = openClass; } /** * Convert an instance of openClass into an instance of targetType. * * @param value * @return the java type object * @throws InvalidObjectException */ public final Object fromOpenValue(Object value) throws InvalidObjectException { if (value == null) return null; else return fromNonNullOpenValue(value); } abstract Object fromNonNullOpenValue(Object value) throws InvalidObjectException; /** * Throw an appropriate InvalidObjectException if we will not be able to convert back from the * open data to the original Java object. * * @throws InvalidObjectException */ void checkReconstructible() throws InvalidObjectException { // subclasses can override } /** * Convert an instance of targetType into an instance of openClass. * * @param value * @return open class object * @throws OpenDataException */ final Object toOpenValue(Object value) throws OpenDataException { if (value == null) return null; else return toNonNullOpenValue(value); } abstract Object toNonNullOpenValue(Object value) throws OpenDataException; /** * * @return True if and only if this OpenTypeConverter's toOpenValue and fromOpenValue methods are * the identity function. */ boolean isIdentity() { return false; } final Type getTargetType() { return targetType; } final OpenType getOpenType() { return openType; } final Class getOpenClass() { return openClass; } /** * * @param type * @return a converter corresponding to a type */ private static synchronized OpenTypeConverter getConverter(Type type) { if (type instanceof GenericArrayType) { Type component = ((GenericArrayType) type).getGenericComponentType(); if (component instanceof Class) type = Array.newInstance((Class<?>) component, 0).getClass(); } WeakReference<OpenTypeConverter> wr = converterMap.get(type); return (wr == null) ? null : wr.get(); } /** * Put the converter in the map to avoid future creation * * @param type * @param conv */ private static synchronized void putConverter(Type type, OpenTypeConverter conv) { WeakReference<OpenTypeConverter> wr = new WeakReference<OpenTypeConverter>(conv); converterMap.put(type, wr); } private static synchronized void putPreDefinedConverter(Type type, OpenTypeConverter conv) { putConverter(type, conv); preDefinedConverters.add(conv); } /** * Static block to initialize pre defined convertor */ static { 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) { throw new Error(e); } final OpenTypeConverter conv = new IdentityConverter(c, t, c); putPreDefinedConverter(c, conv); if (c.getName().startsWith("java.lang.")) { try { final Field typeField = c.getField("TYPE"); final Class primitiveType = (Class) typeField.get(null); final OpenTypeConverter primitiveConv = new IdentityConverter(primitiveType, t, primitiveType); putPreDefinedConverter(primitiveType, primitiveConv); if (primitiveType != void.class) { final Class primitiveArrayType = Array.newInstance(primitiveType, 0).getClass(); final OpenType primitiveArrayOpenType = ArrayType.getPrimitiveArrayType(primitiveArrayType); final OpenTypeConverter primitiveArrayConv = new IdentityConverter(primitiveArrayType, primitiveArrayOpenType, primitiveArrayType); putPreDefinedConverter(primitiveArrayType, primitiveArrayConv); } } catch (NoSuchFieldException e) { } catch (IllegalAccessException e) { assert (false); } } } } /** * * @param objType * @return the converter for the given Java type, creating it if necessary * @throws OpenDataException */ public static synchronized OpenTypeConverter toConverter(Type objType) throws OpenDataException { if (inProgress.containsKey(objType)) { throw new OpenDataException("Recursive data structure, including " + typeName(objType)); } OpenTypeConverter conv; conv = getConverter(objType); if (conv != null) return conv; inProgress.put(objType, objType); try { conv = makeConverter(objType); } catch (OpenDataException e) { throw openDataException("Cannot convert type: " + objType, e); } finally { inProgress.remove(objType); } putConverter(objType, conv); return conv; } /** * * @param objType * @return the open type converrter for a given type * @throws OpenDataException */ private static OpenTypeConverter makeConverter(Type objType) throws OpenDataException { if (objType instanceof GenericArrayType) { Type componentType = ((GenericArrayType) objType).getGenericComponentType(); return makeArrayOrCollectionConverter(objType, componentType); } else if (objType instanceof Class) { Class objClass = (Class<?>) objType; if (objClass.isEnum()) { return makeEnumConverter(objClass); } else if (objClass.isArray()) { Type componentType = objClass.getComponentType(); return makeArrayOrCollectionConverter(objClass, componentType); } else if (JMX.isMXBeanInterface(objClass)) { throw openDataException("Cannot obtain array class", new ManagementException(" MXBean as an Return Type is not supported")); } else { return makeCompositeConverter(objClass); } } else if (objType instanceof ParameterizedType) { return makeParameterizedConverter((ParameterizedType) objType); } else throw new OpenDataException("Cannot map type: " + objType); } private static <T extends Enum<T>> OpenTypeConverter makeEnumConverter(Class<T> enumClass) { return new EnumConverter<T>(enumClass); } private static OpenTypeConverter makeArrayOrCollectionConverter(Type collectionType, Type elementType) throws OpenDataException { final OpenTypeConverter elementConverter = toConverter(elementType); final OpenType elementOpenType = elementConverter.getOpenType(); final ArrayType openType = new ArrayType(1, elementOpenType); final Class elementOpenClass = elementConverter.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 CollectionConverter(collectionType, openType, openArrayClass, elementConverter); } else { if (elementConverter.isIdentity()) { return new IdentityConverter(collectionType, openType, openArrayClass); } else { return new ArrayConverter(collectionType, openType, openArrayClass, elementConverter); } } } protected static final String[] keyArray = {"key"}; protected static final String[] keyValueArray = {"key", "value"}; private static OpenTypeConverter makeTabularConverter(Type objType, boolean sortedMap, Type keyType, Type valueType) throws OpenDataException { final String objTypeName = objType.toString(); final OpenTypeConverter keyConverter = toConverter(keyType); final OpenTypeConverter valueConverter = toConverter(valueType); final OpenType keyOpenType = keyConverter.getOpenType(); final OpenType valueOpenType = valueConverter.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 TableConverter(objType, sortedMap, tabularType, keyConverter, valueConverter); } /** * Supprted types are List<E>, Set<E>, SortedSet<E>, Map<K,V>, SortedMap<K,V>. * * Subclasses of the above types wont be supported as deserialize info wont be there. * * Queue<E> won't be supported as Queue is more of a functional data structure rather than a data * holder * * @param objType * @return the open type converrter for a given type * @throws OpenDataException */ private static OpenTypeConverter makeParameterizedConverter(ParameterizedType objType) 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 = ((ParameterizedType) objType).getActualTypeArguments(); assert (actuals.length == 1); if (c == SortedSet.class) mustBeComparable(c, actuals[0]); return makeArrayOrCollectionConverter(objType, actuals[0]); } else { boolean sortedMap = (c == SortedMap.class); if (c == Map.class || sortedMap) { Type[] actuals = ((ParameterizedType) objType).getActualTypeArguments(); assert (actuals.length == 2); if (sortedMap) mustBeComparable(c, actuals[0]); return makeTabularConverter(objType, sortedMap, actuals[0], actuals[1]); } } } throw new OpenDataException("Cannot convert type: " + objType); } /** * * @param c * @return the open type converrter for a given type * @throws OpenDataException */ private static OpenTypeConverter makeCompositeConverter(Class c) throws OpenDataException { final List<Method> methods = Arrays.asList(c.getMethods()); final SortedMap<String, Method> getterMap = OpenTypeUtil.newSortedMap(); for (Method method : methods) { final String propertyName = propertyName(method); if (propertyName == null) continue; Method old = getterMap.put(OpenTypeUtil.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] = toConverter(retType).getOpenType(); i++; } CompositeType compositeType = new CompositeType(c.getName(), c.getName(), itemNames, // field // names itemNames, // field descriptions openTypes); return new CompositeConverter(c, compositeType, itemNames, getters); } /** * Converts from a CompositeData to an instance of the targetClass Various subclasses override its * functionality. * * */ protected static abstract class CompositeBuilder { CompositeBuilder(Class targetClass, String[] itemNames) { this.targetClass = targetClass; this.itemNames = itemNames; } Class getTargetClass() { return targetClass; } String[] getItemNames() { return itemNames; } /** * If the subclass should be appropriate but there is a problem, then the method throws * InvalidObjectException. * * @param getters * @return If the subclass is appropriate for targetClass, then the method returns null. If the * subclass is not appropriate, then the method returns an explanation of why not. * @throws InvalidObjectException */ abstract String applicable(Method[] getters) throws InvalidObjectException; /** * * @return possible cause if target class is not applicable */ Throwable possibleCause() { return null; } /** * * @param cd * @param itemNames * @param converters * @return Actual java types from the composite type * @throws InvalidObjectException */ abstract Object fromCompositeData(CompositeData cd, String[] itemNames, OpenTypeConverter[] converters) throws InvalidObjectException; private final Class targetClass; private final String[] itemNames; } /** * Builder if the target class has a method "public static from(CompositeData)" * * */ protected static final class CompositeBuilderViaFrom extends CompositeBuilder { CompositeBuilderViaFrom(Class targetClass, String[] itemNames) { super(targetClass, itemNames); } String applicable(Method[] getters) throws InvalidObjectException { // See if it has a method "T from(CompositeData)" // as is conventional for a CompositeDataView Class targetClass = getTargetClass(); try { Method fromMethod = targetClass.getMethod("from", new Class[] {CompositeData.class}); if (!Modifier.isStatic(fromMethod.getModifiers())) { final String msg = "Method from(CompositeData) is not static"; throw new InvalidObjectException(msg); } if (fromMethod.getReturnType() != getTargetClass()) { final String msg = "Method from(CompositeData) returns " + typeName(fromMethod.getReturnType()) + " not " + typeName(targetClass); throw new InvalidObjectException(msg); } this.fromMethod = fromMethod; return null; } catch (InvalidObjectException e) { throw e; } catch (Exception e) { return "no method from(CompositeData)"; } } final Object fromCompositeData(CompositeData cd, String[] itemNames, OpenTypeConverter[] converters) throws InvalidObjectException { try { return fromMethod.invoke(null, cd); } catch (Exception e) { final String msg = "Failed to invoke from(CompositeData)"; throw invalidObjectException(msg, e); } } private Method fromMethod; } /** * This builder never actually returns success. It simply serves to check whether the other * builders in the same group have any chance of success. If any getter in the targetClass returns * a type that we don't know how to reconstruct, then we will not be able to make a builder, and * there is no point in repeating the error about the problematic getter as many times as there * are candidate builders. Instead, the "applicable" method will return an explanatory string, and * the other builders will be skipped. If all the getters are OK, then the "applicable" method * will return an empty string and the other builders will be tried. * * */ protected static class CompositeBuilderCheckGetters extends CompositeBuilder { CompositeBuilderCheckGetters(Class targetClass, String[] itemNames, OpenTypeConverter[] getterConverters) { super(targetClass, itemNames); this.getterConverters = getterConverters; } String applicable(Method[] getters) { for (int i = 0; i < getters.length; i++) { try { getterConverters[i].checkReconstructible(); } catch (InvalidObjectException e) { possibleCause = e; return "method " + getters[i].getName() + " returns type " + "that cannot be mapped back from OpenData"; } } return ""; } @Override Throwable possibleCause() { return possibleCause; } final Object fromCompositeData(CompositeData cd, String[] itemNames, OpenTypeConverter[] converters) { throw new Error(); } private final OpenTypeConverter[] getterConverters; private Throwable possibleCause; } /** * Builder if the target class has a setter for every getter * * */ protected static class CompositeBuilderViaSetters extends CompositeBuilder { CompositeBuilderViaSetters(Class targetClass, String[] itemNames) { super(targetClass, itemNames); } String applicable(Method[] getters) { try { Constructor c = getTargetClass().getConstructor((Class[]) null); } catch (Exception e) { return "does not have a public no-arg constructor"; } Method[] setters = new Method[getters.length]; for (int i = 0; i < getters.length; i++) { Method getter = getters[i]; Class returnType = getter.getReturnType(); String name = propertyName(getter); String setterName = "set" + name; Method setter; try { setter = getTargetClass().getMethod(setterName, returnType); if (setter.getReturnType() != void.class) throw new Exception(); } catch (Exception e) { return "not all getters have corresponding setters " + "(" + getter + ")"; } setters[i] = setter; } this.setters = setters; return null; } Object fromCompositeData(CompositeData cd, String[] itemNames, OpenTypeConverter[] converters) throws InvalidObjectException { Object o; try { o = getTargetClass().newInstance(); for (int i = 0; i < itemNames.length; i++) { if (cd.containsKey(itemNames[i])) { Object openItem = cd.get(itemNames[i]); Object javaItem = converters[i].fromOpenValue(openItem); setters[i].invoke(o, javaItem); } } } catch (Exception e) { throw invalidObjectException(e); } return o; } private Method[] setters; } /** * Builder if the target class has a constructor that is annotated with @ConstructorProperties so * we can derive the corresponding getters. * * */ protected static final class CompositeBuilderViaConstructor extends CompositeBuilder { CompositeBuilderViaConstructor(Class targetClass, String[] itemNames) { super(targetClass, itemNames); } String applicable(Method[] getters) throws InvalidObjectException { final Class<ConstructorProperties> propertyNamesClass = ConstructorProperties.class; Class targetClass = getTargetClass(); Constructor[] constrs = targetClass.getConstructors(); List<Constructor> annotatedConstrList = OpenTypeUtil.newList(); for (Constructor constr : constrs) { if (Modifier.isPublic(constr.getModifiers()) && constr.getAnnotation(propertyNamesClass) != null) annotatedConstrList.add(constr); } if (annotatedConstrList.isEmpty()) return "no constructor has @ConstructorProperties annotation"; annotatedConstructors = OpenTypeUtil.newList(); Map<String, Integer> getterMap = OpenTypeUtil.newMap(); String[] itemNames = getItemNames(); for (int i = 0; i < itemNames.length; i++) getterMap.put(itemNames[i], i); Set<BitSet> getterIndexSets = OpenTypeUtil.newSet(); for (Constructor constr : annotatedConstrList) { String[] propertyNames = ((ConstructorProperties) constr.getAnnotation(propertyNamesClass)).value(); Type[] paramTypes = constr.getGenericParameterTypes(); if (paramTypes.length != propertyNames.length) { final String msg = "Number of constructor params does not match " + "@ConstructorProperties annotation: " + constr; throw new InvalidObjectException(msg); } for (int i = 0; i < paramTypes.length; i++) paramTypes[i] = fixType(paramTypes[i]); int[] paramIndexes = new int[getters.length]; for (int i = 0; i < getters.length; i++) paramIndexes[i] = -1; BitSet present = new BitSet(); for (int i = 0; i < propertyNames.length; i++) { String propertyName = propertyNames[i]; if (!getterMap.containsKey(propertyName)) { String msg = "@ConstructorProperties includes name " + propertyName + " which does not correspond to a property"; for (String getterName : getterMap.keySet()) { if (getterName.equalsIgnoreCase(propertyName)) { msg += " (differs only in case from property " + getterName + ")"; } } msg += ": " + constr; throw new InvalidObjectException(msg); } int getterIndex = getterMap.get(propertyName); paramIndexes[getterIndex] = i; if (present.get(getterIndex)) { final String msg = "@ConstructorProperties contains property " + propertyName + " more than once: " + constr; throw new InvalidObjectException(msg); } present.set(getterIndex); Method getter = getters[getterIndex]; Type propertyType = getter.getGenericReturnType(); if (!propertyType.equals(paramTypes[i])) { final String msg = "@ConstructorProperties gives property " + propertyName + " of type " + propertyType + " for parameter " + " of type " + paramTypes[i] + ": " + constr; throw new InvalidObjectException(msg); } } if (!getterIndexSets.add(present)) { final String msg = "More than one constructor has a @ConstructorProperties " + "annotation with this set of names: " + Arrays.toString(propertyNames); throw new InvalidObjectException(msg); } Constr c = new Constr(constr, paramIndexes, present); annotatedConstructors.add(c); } for (BitSet a : getterIndexSets) { boolean seen = false; for (BitSet b : getterIndexSets) { if (a == b) seen = true; else if (seen) { BitSet u = new BitSet(); u.or(a); u.or(b); if (!getterIndexSets.contains(u)) { Set<String> names = new TreeSet<String>(); for (int i = u.nextSetBit(0); i >= 0; i = u.nextSetBit(i + 1)) names.add(itemNames[i]); final String msg = "Constructors with @ConstructorProperties annotation " + " would be ambiguous for these items: " + names; throw new InvalidObjectException(msg); } } } } return null; } Object fromCompositeData(CompositeData cd, String[] itemNames, OpenTypeConverter[] converters) throws InvalidObjectException { CompositeType ct = cd.getCompositeType(); BitSet present = new BitSet(); for (int i = 0; i < itemNames.length; i++) { if (ct.getType(itemNames[i]) != null) present.set(i); } Constr max = null; for (Constr constr : annotatedConstructors) { if (subset(constr.presentParams, present) && (max == null || subset(max.presentParams, constr.presentParams))) max = constr; } if (max == null) { final String msg = "No constructor has a @ConstructorProperties for this set of " + "items: " + ct.keySet(); throw new InvalidObjectException(msg); } Object[] params = new Object[max.presentParams.cardinality()]; for (int i = 0; i < itemNames.length; i++) { if (!max.presentParams.get(i)) continue; Object openItem = cd.get(itemNames[i]); Object javaItem = converters[i].fromOpenValue(openItem); int index = max.paramIndexes[i]; if (index >= 0) params[index] = javaItem; } try { return max.constructor.newInstance(params); } catch (Exception e) { final String msg = "Exception constructing " + getTargetClass().getName(); throw invalidObjectException(msg, e); } } private static boolean subset(BitSet sub, BitSet sup) { BitSet subcopy = (BitSet) sub.clone(); subcopy.andNot(sup); return subcopy.isEmpty(); } private static class Constr { final Constructor constructor; final int[] paramIndexes; final BitSet presentParams; Constr(Constructor constructor, int[] paramIndexes, BitSet presentParams) { this.constructor = constructor; this.paramIndexes = paramIndexes; this.presentParams = presentParams; } } private List<Constr> annotatedConstructors; } /** * Builder if the target class is an interface and contains no methods other than getters. Then we * can make an instance using a dynamic proxy that forwards the getters to the source * CompositeData * * */ protected static final class CompositeBuilderViaProxy extends CompositeBuilder { CompositeBuilderViaProxy(Class targetClass, String[] itemNames) { super(targetClass, itemNames); } String applicable(Method[] getters) { Class targetClass = getTargetClass(); if (!targetClass.isInterface()) return "not an interface"; Set<Method> methods = OpenTypeUtil.newSet(Arrays.asList(targetClass.getMethods())); methods.removeAll(Arrays.asList(getters)); String bad = null; for (Method m : methods) { String mname = m.getName(); Class[] mparams = m.getParameterTypes(); try { Method om = Object.class.getMethod(mname, mparams); if (!Modifier.isPublic(om.getModifiers())) bad = mname; } catch (NoSuchMethodException e) { bad = mname; } } if (bad != null) return "contains methods other than getters (" + bad + ")"; return null; } final Object fromCompositeData(CompositeData cd, String[] itemNames, OpenTypeConverter[] converters) { final Class targetClass = getTargetClass(); return Proxy.newProxyInstance(targetClass.getClassLoader(), new Class[] {targetClass}, new CompositeDataInvocationHandler(cd)); } } static InvalidObjectException invalidObjectException(String msg, Throwable cause) { return new InvalidObjectException(msg); } static InvalidObjectException invalidObjectException(Throwable cause) { return invalidObjectException(cause.getMessage(), cause); } static OpenDataException openDataException(String msg, Throwable cause) { return new OpenDataException(msg); } static OpenDataException openDataException(Throwable cause) { return openDataException(cause.getMessage(), cause); } static void mustBeComparable(Class collection, Type element) throws OpenDataException { if (!(element instanceof Class) || !Comparable.class.isAssignableFrom((Class<?>) element)) { final String msg = "Parameter class " + element + " of " + collection.getName() + " does not implement " + Comparable.class.getName(); throw new OpenDataException(msg); } } public static String propertyName(Method m) { String rest = null; String name = m.getName(); if (name.startsWith("get")) rest = name.substring(3); else if (name.startsWith("is") && m.getReturnType() == boolean.class) rest = name.substring(2); if (rest == null || rest.length() == 0 || m.getParameterTypes().length > 0 || m.getReturnType() == void.class || name.equals("getClass")) return null; return rest; } protected static String typeName(Type t) { if (t instanceof Class<?>) { Class<?> c = (Class<?>) t; if (c.isArray()) return typeName(c.getComponentType()) + "[]"; else return c.getName(); } return t.toString(); } private static Type fixType(Type t) { if (!(t instanceof GenericArrayType)) return t; GenericArrayType gat = (GenericArrayType) t; Type ultimate = ultimateComponentType(gat); if (!(ultimate instanceof Class<?>)) return t; Class<?> component = (Class<?>) fixType(gat.getGenericComponentType()); return Array.newInstance(component, 0).getClass(); } private static Type ultimateComponentType(GenericArrayType gat) { Type component = gat.getGenericComponentType(); if (component instanceof GenericArrayType) return ultimateComponentType((GenericArrayType) component); else return component; } }