/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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.
*/
/**
* <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
*/
}
throws InvalidObjectException {
return null;
else
return fromNonNullOpenValue(openValue);
}
return null;
else
return toNonNullOpenValue(javaValue);
}
throws InvalidObjectException;
throws OpenDataException;
/**
* <p>True if and only if this MXBeanMapping's toOpenValue and
* fromOpenValue methods are the identity function.</p>
*/
boolean isIdentity() {
return false;
}
}
return (mapping instanceof NonNullMXBeanMapping &&
}
private static final class Mappings
/** Following List simply serves to keep a reference to predefined
MXBeanMappings so they don't get garbage collected. */
}
}
private static synchronized void putPermanentMapping(
}
static {
/* Set up the mappings for Java types that map to SimpleType. */
final OpenType<?>[] simpleTypes = {
VOID,
};
final OpenType<?> t = simpleTypes[i];
Class<?> c;
try {
ObjectName.class.getClassLoader());
} catch (ClassNotFoundException e) {
// the classes that these predefined types declare must exist!
throw new Error(e);
}
try {
final MXBeanMapping primitiveMapping =
new IdentityMapping(primitiveType, t);
if (primitiveType != void.class) {
final Class<?> primitiveArrayType =
final OpenType<?> primitiveArrayOpenType =
final MXBeanMapping 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. */
throws OpenDataException {
throw new OpenDataException(
}
return mapping;
try {
} catch (OpenDataException e) {
} finally {
}
return mapping;
}
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) {
// 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>>.
factory);
return makeMXBeanRefMapping(objClass);
} else {
}
} else if (objType instanceof ParameterizedType) {
factory);
} else
}
private static <T extends Enum<T>> MXBeanMapping
}
/* 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
throws OpenDataException {
final Class<?> openArrayClass;
final String openArrayClassName;
if (elementOpenClass.isArray())
else
try {
} catch (ClassNotFoundException e) {
throw openDataException("Cannot obtain array class", e);
}
if (collectionType instanceof ParameterizedType) {
return new CollectionMapping(collectionType,
} else {
if (isIdentity(elementMapping)) {
return new IdentityMapping(collectionType,
openType);
} else {
return new ArrayMapping(collectionType,
}
}
}
private MXBeanMapping
throws OpenDataException {
final CompositeType rowType =
new CompositeType(objTypeName,
final TabularType tabularType =
}
/* 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
throws OpenDataException {
if (c == SortedSet.class)
} else {
if (sortedMap)
}
}
}
}
throws OpenDataException {
return new MXBeanRefMapping(t);
}
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.getClassLoader() == null);
/* 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. */
if (propertyName == null)
continue;
continue;
method);
throw new OpenDataException(msg);
}
}
if (nitems == 0) {
" to an open data type");
}
int i = 0;
i++;
}
new CompositeType(c.getName(),
c.getName(),
itemNames, // field names
itemNames, // field descriptions
return new CompositeMapping(c,
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. */
super(targetType, openType);
}
boolean isIdentity() {
return true;
}
throws InvalidObjectException {
return openValue;
}
return javaValue;
}
}
extends NonNullMXBeanMapping {
}
}
throws InvalidObjectException {
try {
} catch (Exception e) {
throw invalidObjectException("Cannot convert to enum: " +
value, e);
}
}
}
super(targetType, openArrayType);
this.elementMapping = elementMapping;
}
throws OpenDataException {
for (int i = 0; i < len; i++)
return openArray;
}
throws InvalidObjectException {
final Object[] valueArray;
final Type componentType;
if (javaType instanceof GenericArrayType) {
} else {
throw new IllegalArgumentException("Not an array: " +
javaType);
}
return valueArray;
}
}
/**
* DefaultMXBeanMappingFactory for the elements of this array. If this is an
* array of arrays, the converter converts the second-level arrays,
* not the deepest elements.
*/
}
Class<?> openArrayClass,
super(targetType, openArrayType);
this.elementMapping = elementMapping;
/* Determine the concrete class to be used when converting
back to this Java type. We convert all Lists to ArrayList
and all Sets to TreeSet. (TreeSet because it is a SortedSet,
so works for both Set and SortedSet.) */
if (c == List.class)
else if (c == Set.class)
else if (c == SortedSet.class)
else { // can't happen
assert(false);
}
}
throws OpenDataException {
if (valueCollection instanceof SortedSet<?>) {
Comparator<?> comparator =
if (comparator != null) {
"Cannot convert SortedSet with non-null comparator: " +
}
}
valueCollection.size());
int i = 0;
for (Object o : valueCollection)
return openArray;
}
throws InvalidObjectException {
try {
} catch (Exception e) {
throw invalidObjectException("Cannot create collection", e);
}
"Could not add " + o + " to " +
" (duplicate set element?)";
throw new InvalidObjectException(msg);
}
}
return valueCollection;
}
}
}
}
throws OpenDataException {
return name;
}
throws InvalidObjectException {
"No MXBean for name: " + name;
throw new InvalidObjectException(msg);
}
return mxbean;
}
private <T extends Exception> MXBeanLookup
throws T {
"Cannot convert MXBean interface in this context";
T exc;
try {
} catch (Exception e) {
throw new RuntimeException(e);
}
throw exc;
}
return lookup;
}
}
boolean sortedMap,
super(targetType, tabularType);
this.keyMapping = keyConverter;
this.valueMapping = valueConverter;
}
if (comparator != null) {
"Cannot convert SortedMap with non-null comparator: " +
}
}
final CompositeData row;
row =
openValue});
}
return table;
}
throws InvalidObjectException {
"Duplicate entry in TabularData: key=" + key;
throw new InvalidObjectException(msg);
}
}
return valueMap;
}
}
private final boolean sortedMap;
}
super(targetClass, compositeType);
}
}
throws OpenDataException {
if (value instanceof CompositeDataView)
return null;
try {
} catch (Exception e) {
throw openDataException("Error calling getter for " +
itemNames[i] + ": " + e, e);
}
}
}
/** Determine how to convert back from the CompositeData into
the original Java type. For a type that is not reconstructible,
this method will fail every time, and will throw the right
exception. */
private synchronized void makeCompositeBuilder()
throws InvalidObjectException {
if (compositeBuilder != null)
return;
/* In this 2D array, each subarray is a set of builders where
there is no point in consulting the ones after the first if
the first refuses. */
CompositeBuilder[][] builders = {
{
},
{
},
{
},
};
/* We try to make a meaningful exception message by
concatenating each Builder's explanation of why it
isn't applicable. */
find:
break find;
}
if (i == 0)
break; // skip other builders in this group
}
}
}
if (foundBuilder == null) {
" from a CompositeData: " + whyNots;
if (possibleCause != null)
msg += ". Remaining exceptions show a POSSIBLE cause.";
}
}
}
throws InvalidObjectException {
}
}
/** Converts from a CompositeData to an instance of the targetClass. */
private static abstract class CompositeBuilder {
this.targetClass = targetClass;
}
return targetClass;
}
return itemNames;
}
/** 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. If the
subclass should be appropriate but there is a problem,
then the method throws InvalidObjectException. */
throws InvalidObjectException;
/** If the subclass returns an explanation of why it is not applicable,
it can additionally indicate an exception with details. This is
potentially confusing, because the real problem could be that one
of the other subclasses is supposed to be applicable but isn't.
But the advantage of less information loss probably outweighs the
disadvantage of possible confusion. */
return null;
}
throws InvalidObjectException;
}
/** Builder for when the target class has a method "public static
from(CompositeData)". */
private static final class CompositeBuilderViaFrom
extends CompositeBuilder {
super(targetClass, itemNames);
}
// See if it has a method "T from(CompositeData)"
// as is conventional for a CompositeDataView
try {
"Method from(CompositeData) is not static";
throw new InvalidObjectException(msg);
}
"Method from(CompositeData) returns " +
throw new InvalidObjectException(msg);
}
this.fromMethod = fromMethod;
return null; // success!
} catch (InvalidObjectException e) {
throw e;
} catch (Exception e) {
// OK: it doesn't have the method
return "no method from(CompositeData)";
}
}
throws InvalidObjectException {
try {
} catch (Exception e) {
throw invalidObjectException(msg, e);
}
}
}
/** 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. */
super(targetClass, itemNames);
this.getterConverters = getterConverters;
}
try {
} catch (InvalidObjectException e) {
possibleCause = e;
"that cannot be mapped back from OpenData";
}
}
return "";
}
return possibleCause;
}
MXBeanMapping[] converters) {
throw new Error();
}
}
/** Builder for when the target class has a setter for every getter. */
super(targetClass, itemNames);
}
try {
} catch (Exception e) {
return "does not have a public no-arg constructor";
}
try {
if (setter.getReturnType() != void.class)
throw new Exception();
} catch (Exception e) {
return "not all getters have corresponding setters " +
}
}
return null;
}
throws InvalidObjectException {
Object o;
try {
o = targetClass.newInstance();
}
}
} catch (Exception e) {
throw invalidObjectException(e);
}
return o;
}
}
/** Builder for when the target class has a constructor that is
annotated with @ConstructorProperties so we can see the correspondence
to getters. */
private static final class CompositeBuilderViaConstructor
extends CompositeBuilder {
super(targetClass, itemNames);
}
// Applicable if and only if there are any annotated constructors
}
if (annotatedConstrList.isEmpty())
return "no constructor has @ConstructorProperties annotation";
// Now check that all the annotated constructors are valid
// and throw an exception if not.
// First link the itemNames to their getter indexes.
// Run through the constructors making the checks in the spec.
// For each constructor, remember the correspondence between its
// parameters and the items. The int[] for a constructor says
// what parameter index should get what item. For example,
// if element 0 is 2 then that means that item 0 in the
// CompositeData goes to parameter 2 of the constructor. If an
// element is -1, that item isn't given to the constructor.
// Also remember the set of properties in that constructor
// so we can test unambiguity.
String[] propertyNames =
"Number of constructor params does not match " +
"@ConstructorProperties annotation: " + constr;
throw new InvalidObjectException(msg);
}
paramIndexes[i] = -1;
"@ConstructorProperties includes name " + propertyName +
" which does not correspond to a property";
msg += " (differs only in case from property " +
getterName + ")";
}
}
throw new InvalidObjectException(msg);
}
paramIndexes[getterIndex] = i;
"@ConstructorProperties contains property " +
throw new InvalidObjectException(msg);
}
"@ConstructorProperties gives property " + propertyName +
throw new InvalidObjectException(msg);
}
}
"More than one constructor has a @ConstructorProperties " +
"annotation with this set of names: " +
throw new InvalidObjectException(msg);
}
}
/* Check that no possible set of items could lead to an ambiguous
* choice of constructor (spec requires this check). For any
* pair of constructors, their union would be the minimal
* ambiguous set. If this set itself corresponds to a constructor,
* there is no ambiguity for that pair. In the usual case, one
* of the constructors is a superset of the other so the union is
* just the bigger constuctor.
*
* The algorithm here is quadratic in the number of constructors
* with a @ConstructorProperties annotation. Typically this corresponds
* to the number of versions of the class there have been. Ten
* would already be a large number, so although it's probably
* possible to have an O(n lg n) algorithm it wouldn't be
* worth the complexity.
*/
for (BitSet a : getterIndexSets) {
boolean seen = false;
for (BitSet b : getterIndexSets) {
if (a == b)
seen = true;
else if (seen) {
if (!getterIndexSets.contains(u)) {
i = u.nextSetBit(i+1))
"Constructors with @ConstructorProperties annotation " +
" would be ambiguous for these items: " +
throw new InvalidObjectException(msg);
}
}
}
}
return null; // success!
}
throws InvalidObjectException {
// The CompositeData might come from an earlier version where
// not all the items were present. We look for a constructor
// that accepts just the items that are present. Because of
// the ambiguity check in applicable(), we know there must be
// at most one maximally applicable constructor.
}
}
"No constructor has a @ConstructorProperties for this set of " +
throw new InvalidObjectException(msg);
}
continue;
if (index >= 0)
}
try {
} catch (Exception e) {
throw invalidObjectException(msg, e);
}
}
}
private static class Constr {
final int[] paramIndexes;
this.constructor = constructor;
this.paramIndexes = paramIndexes;
this.presentParams = presentParams;
}
}
}
/** Builder for when 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. */
private static final class CompositeBuilderViaProxy
extends CompositeBuilder {
super(targetClass, itemNames);
}
if (!targetClass.isInterface())
return "not an interface";
/* If the interface has any methods left over, they better be
* public methods that are already present in java.lang.Object.
*/
try {
} catch (NoSuchMethodException e) {
}
/* We don't catch SecurityException since it shouldn't
* happen for a method in Object and if it does we would
* like to know about it rather than mysteriously complaining.
*/
}
return null; // success!
}
MXBeanMapping[] converters) {
return
new Class<?>[] {targetClass},
new CompositeDataInvocationHandler(cd));
}
}
}
}
}
}
throws OpenDataException {
Comparable.class.getName();
throw new OpenDataException(msg);
}
}
/**
* Utility method to take a string and convert it to normal Java variable
* name capitalization. This normally means converting the first
* character from upper case to lower case, but in the (unusual) special
* case when there is more than one character and both the first and
* second characters are upper case, we leave it alone.
* <p>
* Thus "FooBah" becomes "fooBah" and "X" becomes "x", but "URL" stays
* as "URL".
*
* @param name The string to be decapitalized.
* @return The decapitalized version of the string.
*/
return name;
}
// Should be name.offsetByCodePoints but 6242664 makes this fail
return name;
}
/**
* Reverse operation for java.beans.Introspector.decapitalize. For any s,
* capitalize(decapitalize(s)).equals(s). The reverse is not true:
* e.g. capitalize("uRL") produces "URL" which is unchanged by
* decapitalize.
*/
return name;
}
|| m.getReturnType() == void.class
return null;
return rest;
}
// really an IdentityHashSet but that doesn't exist
}