1 /* 2 * Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.management.openmbean; 27 28 import com.sun.jmx.mbeanserver.GetPropertyAction; 29 import java.io.IOException; 30 import java.io.InvalidObjectException; 31 import java.io.ObjectInputStream; 32 import java.io.Serializable; 33 import java.security.AccessController; 34 import java.security.PrivilegedAction; 35 import java.util.Arrays; 36 import java.util.Collections; 37 import java.util.List; 38 import javax.management.Descriptor; 39 import javax.management.ImmutableDescriptor; 40 41 /** 42 * The <code>OpenType</code> class is the parent abstract class of all classes which describe the actual <i>open type</i> 43 * of open data values. 44 * <p> 45 * An <i>open type</i> is defined by: 46 * <ul> 47 * <li>the fully qualified Java class name of the open data values this type describes; 48 * note that only a limited set of Java classes is allowed for open data values 49 * (see {@link #ALLOWED_CLASSNAMES_LIST ALLOWED_CLASSNAMES_LIST}),</li> 50 * <li>its name,</li> 51 * <li>its description.</li> 52 * </ul> 53 * 54 * @param <T> the Java type that instances described by this type must 55 * have. For example, {@link SimpleType#INTEGER} is a {@code 56 * SimpleType<Integer>} which is a subclass of {@code OpenType<Integer>}, 57 * meaning that an attribute, parameter, or return value that is described 58 * as a {@code SimpleType.INTEGER} must have Java type 59 * {@link Integer}. 60 * 61 * @since 1.5 62 */ 63 public abstract class OpenType<T> implements Serializable { 64 65 /* Serial version */ 66 static final long serialVersionUID = -9195195325186646468L; 67 68 69 /** 70 * List of the fully qualified names of the Java classes allowed for open 71 * data values. A multidimensional array of any one of these classes or 72 * their corresponding primitive types is also an allowed class for open 73 * data values. 74 * 75 <pre>ALLOWED_CLASSNAMES_LIST = { 76 "java.lang.Void", 77 "java.lang.Boolean", 78 "java.lang.Character", 79 "java.lang.Byte", 80 "java.lang.Short", 81 "java.lang.Integer", 82 "java.lang.Long", 83 "java.lang.Float", 84 "java.lang.Double", 85 "java.lang.String", 86 "java.math.BigDecimal", 87 "java.math.BigInteger", 88 "java.util.Date", 89 "javax.management.ObjectName", 90 CompositeData.class.getName(), 91 TabularData.class.getName() } ; 92 </pre> 93 * 94 */ 95 public static final List<String> ALLOWED_CLASSNAMES_LIST = 96 Collections.unmodifiableList( 97 Arrays.asList( 98 "java.lang.Void", 99 "java.lang.Boolean", 100 "java.lang.Character", 101 "java.lang.Byte", 102 "java.lang.Short", 103 "java.lang.Integer", 104 "java.lang.Long", 105 "java.lang.Float", 106 "java.lang.Double", 107 "java.lang.String", 108 "java.math.BigDecimal", 109 "java.math.BigInteger", 110 "java.util.Date", 111 "javax.management.ObjectName", 112 CompositeData.class.getName(), // better refer to these two class names like this, rather than hardcoding a string, 113 TabularData.class.getName()) ); // in case the package of these classes should change (who knows...) 114 115 116 /** 117 * @deprecated Use {@link #ALLOWED_CLASSNAMES_LIST ALLOWED_CLASSNAMES_LIST} instead. 118 */ 119 @Deprecated 120 public static final String[] ALLOWED_CLASSNAMES = 121 ALLOWED_CLASSNAMES_LIST.toArray(new String[0]); 122 123 124 /** 125 * @serial The fully qualified Java class name of open data values this 126 * type describes. 127 */ 128 private String className; 129 130 /** 131 * @serial The type description (should not be null or empty). 132 */ 133 private String description; 134 135 /** 136 * @serial The name given to this type (should not be null or empty). 137 */ 138 private String typeName; 139 140 /** 141 * Tells if this type describes an array (checked in constructor). 142 */ 143 private transient boolean isArray = false; 144 145 /** 146 * Cached Descriptor for this OpenType, constructed on demand. 147 */ 148 private transient Descriptor descriptor; 149 150 /* *** Constructor *** */ 151 152 /** 153 * Constructs an <code>OpenType</code> instance (actually a subclass instance as <code>OpenType</code> is abstract), 154 * checking for the validity of the given parameters. 155 * The validity constraints are described below for each parameter. 156 * <br> 157 * @param className The fully qualified Java class name of the open data values this open type describes. 158 * The valid Java class names allowed for open data values are listed in 159 * {@link #ALLOWED_CLASSNAMES_LIST ALLOWED_CLASSNAMES_LIST}. 160 * A multidimensional array of any one of these classes 161 * or their corresponding primitive types is also an allowed class, 162 * in which case the class name follows the rules defined by the method 163 * {@link Class#getName() getName()} of <code>java.lang.Class</code>. 164 * For example, a 3-dimensional array of Strings has for class name 165 * "<code>[[[Ljava.lang.String;</code>" (without the quotes). 166 * <br> 167 * @param typeName The name given to the open type this instance represents; cannot be a null or empty string. 168 * <br> 169 * @param description The human readable description of the open type this instance represents; 170 * cannot be a null or empty string. 171 * <br> 172 * @throws IllegalArgumentException if <var>className</var>, <var>typeName</var> or <var>description</var> 173 * is a null or empty string 174 * <br> 175 * @throws OpenDataException if <var>className</var> is not one of the allowed Java class names for open data 176 */ 177 protected OpenType(String className, 178 String typeName, 179 String description) throws OpenDataException { 180 checkClassNameOverride(); 181 this.typeName = valid("typeName", typeName); 182 this.description = valid("description", description); 183 this.className = validClassName(className); 184 this.isArray = (this.className != null && this.className.startsWith("[")); 185 } 186 187 /* Package-private constructor for callers we trust to get it right. */ 188 OpenType(String className, String typeName, String description, 189 boolean isArray) { 190 this.className = valid("className",className); 191 this.typeName = valid("typeName", typeName); 192 this.description = valid("description", description); 193 this.isArray = isArray; 194 } 195 196 private void checkClassNameOverride() throws SecurityException { 197 if (this.getClass().getClassLoader() == null) 198 return; // We trust bootstrap classes. 199 if (overridesGetClassName(this.getClass())) { 200 final GetPropertyAction getExtendOpenTypes = 201 new GetPropertyAction("jmx.extend.open.types"); 202 if (AccessController.doPrivileged(getExtendOpenTypes) == null) { 203 throw new SecurityException("Cannot override getClassName() " + 204 "unless -Djmx.extend.open.types"); 205 } 206 } 207 } 208 209 private static boolean overridesGetClassName(final Class<?> c) { 210 return AccessController.doPrivileged(new PrivilegedAction<Boolean>() { 211 public Boolean run() { 212 try { 213 return (c.getMethod("getClassName").getDeclaringClass() != 214 OpenType.class); 215 } catch (Exception e) { 216 return true; // fail safe 217 } 218 } 219 }); 220 } 221 222 private static String validClassName(String className) throws OpenDataException { 223 className = valid("className", className); 224 225 // Check if className describes an array class, and determines its elements' class name. 226 // (eg: a 3-dimensional array of Strings has for class name: "[[[Ljava.lang.String;") 227 // 228 int n = 0; 229 while (className.startsWith("[", n)) { 230 n++; 231 } 232 String eltClassName; // class name of array elements 233 boolean isPrimitiveArray = false; 234 if (n > 0) { 235 if (className.startsWith("L", n) && className.endsWith(";")) { 236 // removes the n leading '[' + the 'L' characters 237 // and the last ';' character 238 eltClassName = className.substring(n+1, className.length()-1); 239 } else if (n == className.length() - 1) { 240 // removes the n leading '[' characters 241 eltClassName = className.substring(n, className.length()); 242 isPrimitiveArray = true; 243 } else { 244 throw new OpenDataException("Argument className=\"" + className + 245 "\" is not a valid class name"); 246 } 247 } else { 248 // not an array 249 eltClassName = className; 250 } 251 252 // Check that eltClassName's value is one of the allowed basic data types for open data 253 // 254 boolean ok = false; 255 if (isPrimitiveArray) { 256 ok = ArrayType.isPrimitiveContentType(eltClassName); 257 } else { 258 ok = ALLOWED_CLASSNAMES_LIST.contains(eltClassName); 259 } 260 if ( ! ok ) { 261 throw new OpenDataException("Argument className=\""+ className + 262 "\" is not one of the allowed Java class names for open data."); 263 } 264 265 return className; 266 } 267 268 /* Return argValue.trim() provided argValue is neither null nor empty; 269 otherwise throw IllegalArgumentException. */ 270 private static String valid(String argName, String argValue) { 271 if (argValue == null || (argValue = argValue.trim()).isEmpty()) 272 throw new IllegalArgumentException("Argument " + argName + 273 " cannot be null or empty"); 274 return argValue; 275 } 276 277 /* Package-private access to a Descriptor containing this OpenType. */ 278 synchronized Descriptor getDescriptor() { 279 if (descriptor == null) { 280 descriptor = new ImmutableDescriptor(new String[] {"openType"}, 281 new Object[] {this}); 282 } 283 return descriptor; 284 } 285 286 /* *** Open type information methods *** */ 287 288 /** 289 * Returns the fully qualified Java class name of the open data values 290 * this open type describes. 291 * The only possible Java class names for open data values are listed in 292 * {@link #ALLOWED_CLASSNAMES_LIST ALLOWED_CLASSNAMES_LIST}. 293 * A multidimensional array of any one of these classes or their 294 * corresponding primitive types is also an allowed class, 295 * in which case the class name follows the rules defined by the method 296 * {@link Class#getName() getName()} of <code>java.lang.Class</code>. 297 * For example, a 3-dimensional array of Strings has for class name 298 * "<code>[[[Ljava.lang.String;</code>" (without the quotes), 299 * a 3-dimensional array of Integers has for class name 300 * "<code>[[[Ljava.lang.Integer;</code>" (without the quotes), 301 * and a 3-dimensional array of int has for class name 302 * "<code>[[[I</code>" (without the quotes) 303 * 304 * @return the class name. 305 */ 306 public String getClassName() { 307 return className; 308 } 309 310 // A version of getClassName() that can only be called from within this 311 // package and that cannot be overridden. 312 String safeGetClassName() { 313 return className; 314 } 315 316 /** 317 * Returns the name of this <code>OpenType</code> instance. 318 * 319 * @return the type name. 320 */ 321 public String getTypeName() { 322 323 return typeName; 324 } 325 326 /** 327 * Returns the text description of this <code>OpenType</code> instance. 328 * 329 * @return the description. 330 */ 331 public String getDescription() { 332 333 return description; 334 } 335 336 /** 337 * Returns <code>true</code> if the open data values this open 338 * type describes are arrays, <code>false</code> otherwise. 339 * 340 * @return true if this is an array type. 341 */ 342 public boolean isArray() { 343 344 return isArray; 345 } 346 347 /** 348 * Tests whether <var>obj</var> is a value for this open type. 349 * 350 * @param obj the object to be tested for validity. 351 * 352 * @return <code>true</code> if <var>obj</var> is a value for this 353 * open type, <code>false</code> otherwise. 354 */ 355 public abstract boolean isValue(Object obj) ; 356 357 /** 358 * Tests whether values of the given type can be assigned to this open type. 359 * The default implementation of this method returns true only if the 360 * types are equal. 361 * 362 * @param ot the type to be tested. 363 * 364 * @return true if {@code ot} is assignable to this open type. 365 */ 366 boolean isAssignableFrom(OpenType<?> ot) { 367 return this.equals(ot); 368 } 369 370 /* *** Methods overriden from class Object *** */ 371 372 /** 373 * Compares the specified <code>obj</code> parameter with this 374 * open type instance for equality. 375 * 376 * @param obj the object to compare to. 377 * 378 * @return true if this object and <code>obj</code> are equal. 379 */ 380 public abstract boolean equals(Object obj) ; 381 382 public abstract int hashCode() ; 383 384 /** 385 * Returns a string representation of this open type instance. 386 * 387 * @return the string representation. 388 */ 389 public abstract String toString() ; 390 391 /** 392 * Deserializes an {@link OpenType} from an {@link java.io.ObjectInputStream}. 393 */ 394 private void readObject(ObjectInputStream in) 395 throws IOException, ClassNotFoundException { 396 checkClassNameOverride(); 397 ObjectInputStream.GetField fields = in.readFields(); 398 final String classNameField; 399 final String descriptionField; 400 final String typeNameField; 401 try { 402 classNameField = 403 validClassName((String) fields.get("className", null)); 404 descriptionField = 405 valid("description", (String) fields.get("description", null)); 406 typeNameField = 407 valid("typeName", (String) fields.get("typeName", null)); 408 } catch (Exception e) { 409 IOException e2 = new InvalidObjectException(e.getMessage()); 410 e2.initCause(e); 411 throw e2; 412 } 413 className = classNameField; 414 description = descriptionField; 415 typeName = typeNameField; 416 isArray = (className.startsWith("[")); 417 } 418 }