/*
* Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package javax.management.openmbean;
import com.sun.jmx.mbeanserver.
GetPropertyAction;
import java.io.
IOException;
import java.io.
InvalidObjectException;
import java.io.
ObjectInputStream;
import java.io.
Serializable;
import java.security.
AccessController;
import java.security.
PrivilegedAction;
import java.util.
Arrays;
import java.util.
Collections;
import java.util.
List;
import javax.management.
Descriptor;
import javax.management.
ImmutableDescriptor;
/**
* The <code>OpenType</code> class is the parent abstract class of all classes which describe the actual <i>open type</i>
* of open data values.
* <p>
* An <i>open type</i> is defined by:
* <ul>
* <li>the fully qualified Java class name of the open data values this type describes;
* note that only a limited set of Java classes is allowed for open data values
* (see {@link #ALLOWED_CLASSNAMES_LIST ALLOWED_CLASSNAMES_LIST}),</li>
* <li>its name,</li>
* <li>its description.</li>
* </ul>
*
* @param <T> the Java type that instances described by this type must
* have. For example, {@link SimpleType#INTEGER} is a {@code
* SimpleType<Integer>} which is a subclass of {@code OpenType<Integer>},
* meaning that an attribute, parameter, or return value that is described
* as a {@code SimpleType.INTEGER} must have Java type
* {@link Integer}.
*
* @since 1.5
*/
public abstract class
OpenType<T> implements
Serializable {
/* Serial version */
static final long
serialVersionUID = -9195195325186646468L;
/**
* List of the fully qualified names of the Java classes allowed for open
* data values. A multidimensional array of any one of these classes or
* their corresponding primitive types is also an allowed class for open
* data values.
*
<pre>ALLOWED_CLASSNAMES_LIST = {
"java.lang.Void",
"java.lang.Boolean",
"java.lang.Character",
"java.lang.Byte",
"java.lang.Short",
"java.lang.Integer",
"java.lang.Long",
"java.lang.Float",
"java.lang.Double",
"java.lang.String",
"java.math.BigDecimal",
"java.math.BigInteger",
"java.util.Date",
"javax.management.ObjectName",
CompositeData.class.getName(),
TabularData.class.getName() } ;
</pre>
*
*/
public static final
List<
String>
ALLOWED_CLASSNAMES_LIST =
Collections.
unmodifiableList(
Arrays.
asList(
"java.lang.Void",
"java.lang.Boolean",
"java.lang.Character",
"java.lang.Byte",
"java.lang.Short",
"java.lang.Integer",
"java.lang.Long",
"java.lang.Float",
"java.lang.Double",
"java.lang.String",
"java.math.BigDecimal",
"java.math.BigInteger",
"java.util.Date",
"javax.management.ObjectName",
CompositeData.class.
getName(), // better refer to these two class names like this, rather than hardcoding a string,
TabularData.class.
getName()) ); // in case the package of these classes should change (who knows...)
/**
* @deprecated Use {@link #ALLOWED_CLASSNAMES_LIST ALLOWED_CLASSNAMES_LIST} instead.
*/
@
Deprecated
public static final
String[]
ALLOWED_CLASSNAMES =
ALLOWED_CLASSNAMES_LIST.
toArray(new
String[0]);
/**
* @serial The fully qualified Java class name of open data values this
* type describes.
*/
private
String className;
/**
* @serial The type description (should not be null or empty).
*/
private
String description;
/**
* @serial The name given to this type (should not be null or empty).
*/
private
String typeName;
/**
* Tells if this type describes an array (checked in constructor).
*/
private transient boolean
isArray = false;
/**
* Cached Descriptor for this OpenType, constructed on demand.
*/
private transient
Descriptor descriptor;
/* *** Constructor *** */
/**
* Constructs an <code>OpenType</code> instance (actually a subclass instance as <code>OpenType</code> is abstract),
* checking for the validity of the given parameters.
* The validity constraints are described below for each parameter.
* <br>
* @param className The fully qualified Java class name of the open data values this open type describes.
* The valid Java class names allowed for open data values are listed in
* {@link #ALLOWED_CLASSNAMES_LIST ALLOWED_CLASSNAMES_LIST}.
* A multidimensional array of any one of these classes
* or their corresponding primitive types is also an allowed class,
* in which case the class name follows the rules defined by the method
* {@link Class#getName() getName()} of <code>java.lang.Class</code>.
* For example, a 3-dimensional array of Strings has for class name
* "<code>[[[Ljava.lang.String;</code>" (without the quotes).
* <br>
* @param typeName The name given to the open type this instance represents; cannot be a null or empty string.
* <br>
* @param description The human readable description of the open type this instance represents;
* cannot be a null or empty string.
* <br>
* @throws IllegalArgumentException if <var>className</var>, <var>typeName</var> or <var>description</var>
* is a null or empty string
* <br>
* @throws OpenDataException if <var>className</var> is not one of the allowed Java class names for open data
*/
protected
OpenType(
String className,
String typeName,
String description) throws
OpenDataException {
checkClassNameOverride();
this.
typeName =
valid("typeName",
typeName);
this.
description =
valid("description",
description);
this.
className =
validClassName(
className);
this.
isArray = (this.
className != null && this.
className.
startsWith("["));
}
/* Package-private constructor for callers we trust to get it right. */
OpenType(
String className,
String typeName,
String description,
boolean
isArray) {
this.
className =
valid("className",
className);
this.
typeName =
valid("typeName",
typeName);
this.
description =
valid("description",
description);
this.
isArray =
isArray;
}
private void
checkClassNameOverride() throws
SecurityException {
if (this.
getClass().
getClassLoader() == null)
return; // We trust bootstrap classes.
if (
overridesGetClassName(this.
getClass())) {
final
GetPropertyAction getExtendOpenTypes =
new
GetPropertyAction("jmx.extend.open.types");
if (
AccessController.
doPrivileged(
getExtendOpenTypes) == null) {
throw new
SecurityException("Cannot override getClassName() " +
"unless -Djmx.extend.open.types");
}
}
}
private static boolean
overridesGetClassName(final
Class<?>
c) {
return
AccessController.
doPrivileged(new
PrivilegedAction<
Boolean>() {
public
Boolean run() {
try {
return (
c.
getMethod("getClassName").
getDeclaringClass() !=
OpenType.class);
} catch (
Exception e) {
return true; // fail safe
}
}
});
}
private static
String validClassName(
String className) throws
OpenDataException {
className =
valid("className",
className);
// Check if className describes an array class, and determines its elements' class name.
// (eg: a 3-dimensional array of Strings has for class name: "[[[Ljava.lang.String;")
//
int
n = 0;
while (
className.
startsWith("[",
n)) {
n++;
}
String eltClassName; // class name of array elements
boolean
isPrimitiveArray = false;
if (
n > 0) {
if (
className.
startsWith("L",
n) &&
className.
endsWith(";")) {
// removes the n leading '[' + the 'L' characters
// and the last ';' character
eltClassName =
className.
substring(
n+1,
className.
length()-1);
} else if (
n ==
className.
length() - 1) {
// removes the n leading '[' characters
eltClassName =
className.
substring(
n,
className.
length());
isPrimitiveArray = true;
} else {
throw new
OpenDataException("Argument className=\"" +
className +
"\" is not a valid class name");
}
} else {
// not an array
eltClassName =
className;
}
// Check that eltClassName's value is one of the allowed basic data types for open data
//
boolean
ok = false;
if (
isPrimitiveArray) {
ok =
ArrayType.
isPrimitiveContentType(
eltClassName);
} else {
ok =
ALLOWED_CLASSNAMES_LIST.
contains(
eltClassName);
}
if ( !
ok ) {
throw new
OpenDataException("Argument className=\""+
className +
"\" is not one of the allowed Java class names for open data.");
}
return
className;
}
/* Return argValue.trim() provided argValue is neither null nor empty;
otherwise throw IllegalArgumentException. */
private static
String valid(
String argName,
String argValue) {
if (
argValue == null || (
argValue =
argValue.
trim()).
equals(""))
throw new
IllegalArgumentException("Argument " +
argName +
" cannot be null or empty");
return
argValue;
}
/* Package-private access to a Descriptor containing this OpenType. */
synchronized
Descriptor getDescriptor() {
if (
descriptor == null) {
descriptor = new
ImmutableDescriptor(new
String[] {"openType"},
new
Object[] {this});
}
return
descriptor;
}
/* *** Open type information methods *** */
/**
* Returns the fully qualified Java class name of the open data values
* this open type describes.
* The only possible Java class names for open data values are listed in
* {@link #ALLOWED_CLASSNAMES_LIST ALLOWED_CLASSNAMES_LIST}.
* A multidimensional array of any one of these classes or their
* corresponding primitive types is also an allowed class,
* in which case the class name follows the rules defined by the method
* {@link Class#getName() getName()} of <code>java.lang.Class</code>.
* For example, a 3-dimensional array of Strings has for class name
* "<code>[[[Ljava.lang.String;</code>" (without the quotes),
* a 3-dimensional array of Integers has for class name
* "<code>[[[Ljava.lang.Integer;</code>" (without the quotes),
* and a 3-dimensional array of int has for class name
* "<code>[[[I</code>" (without the quotes)
*
* @return the class name.
*/
public
String getClassName() {
return
className;
}
// A version of getClassName() that can only be called from within this
// package and that cannot be overridden.
String safeGetClassName() {
return
className;
}
/**
* Returns the name of this <code>OpenType</code> instance.
*
* @return the type name.
*/
public
String getTypeName() {
return
typeName;
}
/**
* Returns the text description of this <code>OpenType</code> instance.
*
* @return the description.
*/
public
String getDescription() {
return
description;
}
/**
* Returns <code>true</code> if the open data values this open
* type describes are arrays, <code>false</code> otherwise.
*
* @return true if this is an array type.
*/
public boolean
isArray() {
return
isArray;
}
/**
* Tests whether <var>obj</var> is a value for this open type.
*
* @param obj the object to be tested for validity.
*
* @return <code>true</code> if <var>obj</var> is a value for this
* open type, <code>false</code> otherwise.
*/
public abstract boolean
isValue(
Object obj) ;
/**
* Tests whether values of the given type can be assigned to this open type.
* The default implementation of this method returns true only if the
* types are equal.
*
* @param ot the type to be tested.
*
* @return true if {@code ot} is assignable to this open type.
*/
boolean
isAssignableFrom(
OpenType<?>
ot) {
return this.
equals(
ot);
}
/* *** Methods overriden from class Object *** */
/**
* Compares the specified <code>obj</code> parameter with this
* open type instance for equality.
*
* @param obj the object to compare to.
*
* @return true if this object and <code>obj</code> are equal.
*/
public abstract boolean
equals(
Object obj) ;
public abstract int
hashCode() ;
/**
* Returns a string representation of this open type instance.
*
* @return the string representation.
*/
public abstract
String toString() ;
/**
* Deserializes an {@link OpenType} from an {@link java.io.ObjectInputStream}.
*/
private void
readObject(
ObjectInputStream in)
throws
IOException,
ClassNotFoundException {
checkClassNameOverride();
ObjectInputStream.
GetField fields =
in.
readFields();
final
String classNameField;
final
String descriptionField;
final
String typeNameField;
try {
classNameField =
validClassName((
String)
fields.
get("className", null));
descriptionField =
valid("description", (
String)
fields.
get("description", null));
typeNameField =
valid("typeName", (
String)
fields.
get("typeName", null));
} catch (
Exception e) {
IOException e2 = new
InvalidObjectException(
e.
getMessage());
e2.
initCause(
e);
throw
e2;
}
className =
classNameField;
description =
descriptionField;
typeName =
typeNameField;
isArray = (
className.
startsWith("["));
}
}