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()).equals(""))
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 }
419