|
1 /* |
|
2 * Copyright 1999-2006 Sun Microsystems, Inc. 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. Sun designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
|
22 * CA 95054 USA or visit www.sun.com if you need additional information or |
|
23 * have any questions. |
|
24 */ |
|
25 |
|
26 package javax.management; |
|
27 |
|
28 import java.io.IOException; |
|
29 import java.io.StreamCorruptedException; |
|
30 import java.io.Serializable; |
|
31 import java.io.ObjectOutputStream; |
|
32 import java.io.ObjectInputStream; |
|
33 import java.lang.reflect.Method; |
|
34 import java.util.Arrays; |
|
35 import java.util.Map; |
|
36 import java.util.WeakHashMap; |
|
37 import java.security.AccessController; |
|
38 import java.security.PrivilegedAction; |
|
39 |
|
40 import static javax.management.ImmutableDescriptor.nonNullDescriptor; |
|
41 |
|
42 /** |
|
43 * <p>Describes the management interface exposed by an MBean; that is, |
|
44 * the set of attributes and operations which are available for |
|
45 * management operations. Instances of this class are immutable. |
|
46 * Subclasses may be mutable but this is not recommended.</p> |
|
47 * |
|
48 * <p>The contents of the <code>MBeanInfo</code> for a Dynamic MBean |
|
49 * are determined by its {@link DynamicMBean#getMBeanInfo |
|
50 * getMBeanInfo()} method. This includes Open MBeans and Model |
|
51 * MBeans, which are kinds of Dynamic MBeans.</p> |
|
52 * |
|
53 * <p>The contents of the <code>MBeanInfo</code> for a Standard MBean |
|
54 * are determined by the MBean server as follows:</p> |
|
55 * |
|
56 * <ul> |
|
57 * |
|
58 * <li>{@link #getClassName()} returns the Java class name of the MBean |
|
59 * object; |
|
60 * |
|
61 * <li>{@link #getConstructors()} returns the list of all public |
|
62 * constructors in that object; |
|
63 * |
|
64 * <li>{@link #getAttributes()} returns the list of all attributes |
|
65 * whose existence is deduced from the presence in the MBean interface |
|
66 * of a <code>get<i>Name</i></code>, <code>is<i>Name</i></code>, or |
|
67 * <code>set<i>Name</i></code> method that conforms to the conventions |
|
68 * for Standard MBeans; |
|
69 * |
|
70 * <li>{@link #getOperations()} returns the list of all methods in |
|
71 * the MBean interface that do not represent attributes; |
|
72 * |
|
73 * <li>{@link #getNotifications()} returns an empty array if the MBean |
|
74 * does not implement the {@link NotificationBroadcaster} interface, |
|
75 * otherwise the result of calling {@link |
|
76 * NotificationBroadcaster#getNotificationInfo()} on it; |
|
77 * |
|
78 * <li>{@link #getDescriptor()} returns a descriptor containing the contents |
|
79 * of any descriptor annotations in the MBean interface. |
|
80 * |
|
81 * </ul> |
|
82 * |
|
83 * <p>The description returned by {@link #getDescription()} and the |
|
84 * descriptions of the contained attributes and operations are determined |
|
85 * by the corresponding <!-- link here --> Description annotations if any; |
|
86 * otherwise their contents are not specified.</p> |
|
87 * |
|
88 * <p>The remaining details of the <code>MBeanInfo</code> for a |
|
89 * Standard MBean are not specified. This includes the description of |
|
90 * any contained constructors, and notifications; the names |
|
91 * of parameters to constructors and operations; and the descriptions of |
|
92 * constructor parameters.</p> |
|
93 * |
|
94 * @since 1.5 |
|
95 */ |
|
96 public class MBeanInfo implements Cloneable, Serializable, DescriptorRead { |
|
97 |
|
98 /* Serial version */ |
|
99 static final long serialVersionUID = -6451021435135161911L; |
|
100 |
|
101 /** |
|
102 * @serial The Descriptor for the MBean. This field |
|
103 * can be null, which is equivalent to an empty Descriptor. |
|
104 */ |
|
105 private transient Descriptor descriptor; |
|
106 |
|
107 /** |
|
108 * @serial The human readable description of the class. |
|
109 */ |
|
110 private final String description; |
|
111 |
|
112 /** |
|
113 * @serial The MBean qualified name. |
|
114 */ |
|
115 private final String className; |
|
116 |
|
117 /** |
|
118 * @serial The MBean attribute descriptors. |
|
119 */ |
|
120 private final MBeanAttributeInfo[] attributes; |
|
121 |
|
122 /** |
|
123 * @serial The MBean operation descriptors. |
|
124 */ |
|
125 private final MBeanOperationInfo[] operations; |
|
126 |
|
127 /** |
|
128 * @serial The MBean constructor descriptors. |
|
129 */ |
|
130 private final MBeanConstructorInfo[] constructors; |
|
131 |
|
132 /** |
|
133 * @serial The MBean notification descriptors. |
|
134 */ |
|
135 private final MBeanNotificationInfo[] notifications; |
|
136 |
|
137 private transient int hashCode; |
|
138 |
|
139 /** |
|
140 * <p>True if this class is known not to override the array-valued |
|
141 * getters of MBeanInfo. Obviously true for MBeanInfo itself, and true |
|
142 * for a subclass where we succeed in reflecting on the methods |
|
143 * and discover they are not overridden.</p> |
|
144 * |
|
145 * <p>The purpose of this variable is to avoid cloning the arrays |
|
146 * when doing operations like {@link #equals} where we know they |
|
147 * will not be changed. If a subclass overrides a getter, we |
|
148 * cannot access the corresponding array directly.</p> |
|
149 */ |
|
150 private final transient boolean arrayGettersSafe; |
|
151 |
|
152 /** |
|
153 * Constructs an <CODE>MBeanInfo</CODE>. |
|
154 * |
|
155 * @param className The name of the Java class of the MBean described |
|
156 * by this <CODE>MBeanInfo</CODE>. This value may be any |
|
157 * syntactically legal Java class name. It does not have to be a |
|
158 * Java class known to the MBean server or to the MBean's |
|
159 * ClassLoader. If it is a Java class known to the MBean's |
|
160 * ClassLoader, it is recommended but not required that the |
|
161 * class's public methods include those that would appear in a |
|
162 * Standard MBean implementing the attributes and operations in |
|
163 * this MBeanInfo. |
|
164 * @param description A human readable description of the MBean (optional). |
|
165 * @param attributes The list of exposed attributes of the MBean. |
|
166 * This may be null with the same effect as a zero-length array. |
|
167 * @param constructors The list of public constructors of the |
|
168 * MBean. This may be null with the same effect as a zero-length |
|
169 * array. |
|
170 * @param operations The list of operations of the MBean. This |
|
171 * may be null with the same effect as a zero-length array. |
|
172 * @param notifications The list of notifications emitted. This |
|
173 * may be null with the same effect as a zero-length array. |
|
174 */ |
|
175 public MBeanInfo(String className, |
|
176 String description, |
|
177 MBeanAttributeInfo[] attributes, |
|
178 MBeanConstructorInfo[] constructors, |
|
179 MBeanOperationInfo[] operations, |
|
180 MBeanNotificationInfo[] notifications) |
|
181 throws IllegalArgumentException { |
|
182 this(className, description, attributes, constructors, operations, |
|
183 notifications, null); |
|
184 } |
|
185 |
|
186 /** |
|
187 * Constructs an <CODE>MBeanInfo</CODE>. |
|
188 * |
|
189 * @param className The name of the Java class of the MBean described |
|
190 * by this <CODE>MBeanInfo</CODE>. This value may be any |
|
191 * syntactically legal Java class name. It does not have to be a |
|
192 * Java class known to the MBean server or to the MBean's |
|
193 * ClassLoader. If it is a Java class known to the MBean's |
|
194 * ClassLoader, it is recommended but not required that the |
|
195 * class's public methods include those that would appear in a |
|
196 * Standard MBean implementing the attributes and operations in |
|
197 * this MBeanInfo. |
|
198 * @param description A human readable description of the MBean (optional). |
|
199 * @param attributes The list of exposed attributes of the MBean. |
|
200 * This may be null with the same effect as a zero-length array. |
|
201 * @param constructors The list of public constructors of the |
|
202 * MBean. This may be null with the same effect as a zero-length |
|
203 * array. |
|
204 * @param operations The list of operations of the MBean. This |
|
205 * may be null with the same effect as a zero-length array. |
|
206 * @param notifications The list of notifications emitted. This |
|
207 * may be null with the same effect as a zero-length array. |
|
208 * @param descriptor The descriptor for the MBean. This may be null |
|
209 * which is equivalent to an empty descriptor. |
|
210 * |
|
211 * @since 1.6 |
|
212 */ |
|
213 public MBeanInfo(String className, |
|
214 String description, |
|
215 MBeanAttributeInfo[] attributes, |
|
216 MBeanConstructorInfo[] constructors, |
|
217 MBeanOperationInfo[] operations, |
|
218 MBeanNotificationInfo[] notifications, |
|
219 Descriptor descriptor) |
|
220 throws IllegalArgumentException { |
|
221 |
|
222 this.className = className; |
|
223 |
|
224 this.description = description; |
|
225 |
|
226 if (attributes == null) |
|
227 attributes = MBeanAttributeInfo.NO_ATTRIBUTES; |
|
228 this.attributes = attributes; |
|
229 |
|
230 if (operations == null) |
|
231 operations = MBeanOperationInfo.NO_OPERATIONS; |
|
232 this.operations = operations; |
|
233 |
|
234 if (constructors == null) |
|
235 constructors = MBeanConstructorInfo.NO_CONSTRUCTORS; |
|
236 this.constructors = constructors; |
|
237 |
|
238 if (notifications == null) |
|
239 notifications = MBeanNotificationInfo.NO_NOTIFICATIONS; |
|
240 this.notifications = notifications; |
|
241 |
|
242 if (descriptor == null) |
|
243 descriptor = ImmutableDescriptor.EMPTY_DESCRIPTOR; |
|
244 this.descriptor = descriptor; |
|
245 |
|
246 this.arrayGettersSafe = |
|
247 arrayGettersSafe(this.getClass(), MBeanInfo.class); |
|
248 } |
|
249 |
|
250 /** |
|
251 * <p>Returns a shallow clone of this instance. |
|
252 * The clone is obtained by simply calling <tt>super.clone()</tt>, |
|
253 * thus calling the default native shallow cloning mechanism |
|
254 * implemented by <tt>Object.clone()</tt>. |
|
255 * No deeper cloning of any internal field is made.</p> |
|
256 * |
|
257 * <p>Since this class is immutable, the clone method is chiefly of |
|
258 * interest to subclasses.</p> |
|
259 */ |
|
260 public Object clone () { |
|
261 try { |
|
262 return super.clone() ; |
|
263 } catch (CloneNotSupportedException e) { |
|
264 // should not happen as this class is cloneable |
|
265 return null; |
|
266 } |
|
267 } |
|
268 |
|
269 |
|
270 /** |
|
271 * Returns the name of the Java class of the MBean described by |
|
272 * this <CODE>MBeanInfo</CODE>. |
|
273 * |
|
274 * @return the class name. |
|
275 */ |
|
276 public String getClassName() { |
|
277 return className; |
|
278 } |
|
279 |
|
280 /** |
|
281 * Returns a human readable description of the MBean. |
|
282 * |
|
283 * @return the description. |
|
284 */ |
|
285 public String getDescription() { |
|
286 return description; |
|
287 } |
|
288 |
|
289 /** |
|
290 * Returns the list of attributes exposed for management. |
|
291 * Each attribute is described by an <CODE>MBeanAttributeInfo</CODE> object. |
|
292 * |
|
293 * The returned array is a shallow copy of the internal array, |
|
294 * which means that it is a copy of the internal array of |
|
295 * references to the <CODE>MBeanAttributeInfo</CODE> objects |
|
296 * but that each referenced <CODE>MBeanAttributeInfo</CODE> object is not copied. |
|
297 * |
|
298 * @return An array of <CODE>MBeanAttributeInfo</CODE> objects. |
|
299 */ |
|
300 public MBeanAttributeInfo[] getAttributes() { |
|
301 MBeanAttributeInfo[] as = nonNullAttributes(); |
|
302 if (as.length == 0) |
|
303 return as; |
|
304 else |
|
305 return as.clone(); |
|
306 } |
|
307 |
|
308 private MBeanAttributeInfo[] fastGetAttributes() { |
|
309 if (arrayGettersSafe) |
|
310 return nonNullAttributes(); |
|
311 else |
|
312 return getAttributes(); |
|
313 } |
|
314 |
|
315 /** |
|
316 * Return the value of the attributes field, or an empty array if |
|
317 * the field is null. This can't happen with a |
|
318 * normally-constructed instance of this class, but can if the |
|
319 * instance was deserialized from another implementation that |
|
320 * allows the field to be null. It would be simpler if we enforced |
|
321 * the class invariant that these fields cannot be null by writing |
|
322 * a readObject() method, but that would require us to define the |
|
323 * various array fields as non-final, which is annoying because |
|
324 * conceptually they are indeed final. |
|
325 */ |
|
326 private MBeanAttributeInfo[] nonNullAttributes() { |
|
327 return (attributes == null) ? |
|
328 MBeanAttributeInfo.NO_ATTRIBUTES : attributes; |
|
329 } |
|
330 |
|
331 /** |
|
332 * Returns the list of operations of the MBean. |
|
333 * Each operation is described by an <CODE>MBeanOperationInfo</CODE> object. |
|
334 * |
|
335 * The returned array is a shallow copy of the internal array, |
|
336 * which means that it is a copy of the internal array of |
|
337 * references to the <CODE>MBeanOperationInfo</CODE> objects |
|
338 * but that each referenced <CODE>MBeanOperationInfo</CODE> object is not copied. |
|
339 * |
|
340 * @return An array of <CODE>MBeanOperationInfo</CODE> objects. |
|
341 */ |
|
342 public MBeanOperationInfo[] getOperations() { |
|
343 MBeanOperationInfo[] os = nonNullOperations(); |
|
344 if (os.length == 0) |
|
345 return os; |
|
346 else |
|
347 return os.clone(); |
|
348 } |
|
349 |
|
350 private MBeanOperationInfo[] fastGetOperations() { |
|
351 if (arrayGettersSafe) |
|
352 return nonNullOperations(); |
|
353 else |
|
354 return getOperations(); |
|
355 } |
|
356 |
|
357 private MBeanOperationInfo[] nonNullOperations() { |
|
358 return (operations == null) ? |
|
359 MBeanOperationInfo.NO_OPERATIONS : operations; |
|
360 } |
|
361 |
|
362 /** |
|
363 * <p>Returns the list of the public constructors of the MBean. |
|
364 * Each constructor is described by an |
|
365 * <CODE>MBeanConstructorInfo</CODE> object.</p> |
|
366 * |
|
367 * <p>The returned array is a shallow copy of the internal array, |
|
368 * which means that it is a copy of the internal array of |
|
369 * references to the <CODE>MBeanConstructorInfo</CODE> objects but |
|
370 * that each referenced <CODE>MBeanConstructorInfo</CODE> object |
|
371 * is not copied.</p> |
|
372 * |
|
373 * <p>The returned list is not necessarily exhaustive. That is, |
|
374 * the MBean may have a public constructor that is not in the |
|
375 * list. In this case, the MBean server can construct another |
|
376 * instance of this MBean's class using that constructor, even |
|
377 * though it is not listed here.</p> |
|
378 * |
|
379 * @return An array of <CODE>MBeanConstructorInfo</CODE> objects. |
|
380 */ |
|
381 public MBeanConstructorInfo[] getConstructors() { |
|
382 MBeanConstructorInfo[] cs = nonNullConstructors(); |
|
383 if (cs.length == 0) |
|
384 return cs; |
|
385 else |
|
386 return cs.clone(); |
|
387 } |
|
388 |
|
389 private MBeanConstructorInfo[] fastGetConstructors() { |
|
390 if (arrayGettersSafe) |
|
391 return nonNullConstructors(); |
|
392 else |
|
393 return getConstructors(); |
|
394 } |
|
395 |
|
396 private MBeanConstructorInfo[] nonNullConstructors() { |
|
397 return (constructors == null) ? |
|
398 MBeanConstructorInfo.NO_CONSTRUCTORS : constructors; |
|
399 } |
|
400 |
|
401 /** |
|
402 * Returns the list of the notifications emitted by the MBean. |
|
403 * Each notification is described by an <CODE>MBeanNotificationInfo</CODE> object. |
|
404 * |
|
405 * The returned array is a shallow copy of the internal array, |
|
406 * which means that it is a copy of the internal array of |
|
407 * references to the <CODE>MBeanNotificationInfo</CODE> objects |
|
408 * but that each referenced <CODE>MBeanNotificationInfo</CODE> object is not copied. |
|
409 * |
|
410 * @return An array of <CODE>MBeanNotificationInfo</CODE> objects. |
|
411 */ |
|
412 public MBeanNotificationInfo[] getNotifications() { |
|
413 MBeanNotificationInfo[] ns = nonNullNotifications(); |
|
414 if (ns.length == 0) |
|
415 return ns; |
|
416 else |
|
417 return ns.clone(); |
|
418 } |
|
419 |
|
420 private MBeanNotificationInfo[] fastGetNotifications() { |
|
421 if (arrayGettersSafe) |
|
422 return nonNullNotifications(); |
|
423 else |
|
424 return getNotifications(); |
|
425 } |
|
426 |
|
427 private MBeanNotificationInfo[] nonNullNotifications() { |
|
428 return (notifications == null) ? |
|
429 MBeanNotificationInfo.NO_NOTIFICATIONS : notifications; |
|
430 } |
|
431 |
|
432 /** |
|
433 * Get the descriptor of this MBeanInfo. Changing the returned value |
|
434 * will have no affect on the original descriptor. |
|
435 * |
|
436 * @return a descriptor that is either immutable or a copy of the original. |
|
437 * |
|
438 * @since 1.6 |
|
439 */ |
|
440 public Descriptor getDescriptor() { |
|
441 return (Descriptor) nonNullDescriptor(descriptor).clone(); |
|
442 } |
|
443 |
|
444 public String toString() { |
|
445 return |
|
446 getClass().getName() + "[" + |
|
447 "description=" + getDescription() + ", " + |
|
448 "attributes=" + Arrays.asList(fastGetAttributes()) + ", " + |
|
449 "constructors=" + Arrays.asList(fastGetConstructors()) + ", " + |
|
450 "operations=" + Arrays.asList(fastGetOperations()) + ", " + |
|
451 "notifications=" + Arrays.asList(fastGetNotifications()) + ", " + |
|
452 "descriptor=" + getDescriptor() + |
|
453 "]"; |
|
454 } |
|
455 |
|
456 /** |
|
457 * <p>Compare this MBeanInfo to another. Two MBeanInfo objects |
|
458 * are equal if and only if they return equal values for {@link |
|
459 * #getClassName()}, for {@link #getDescription()}, and for |
|
460 * {@link #getDescriptor()}, and the |
|
461 * arrays returned by the two objects for {@link |
|
462 * #getAttributes()}, {@link #getOperations()}, {@link |
|
463 * #getConstructors()}, and {@link #getNotifications()} are |
|
464 * pairwise equal. Here "equal" means {@link |
|
465 * Object#equals(Object)}, not identity.</p> |
|
466 * |
|
467 * <p>If two MBeanInfo objects return the same values in one of |
|
468 * their arrays but in a different order then they are not equal.</p> |
|
469 * |
|
470 * @param o the object to compare to. |
|
471 * |
|
472 * @return true if and only if <code>o</code> is an MBeanInfo that is equal |
|
473 * to this one according to the rules above. |
|
474 */ |
|
475 public boolean equals(Object o) { |
|
476 if (o == this) |
|
477 return true; |
|
478 if (!(o instanceof MBeanInfo)) |
|
479 return false; |
|
480 MBeanInfo p = (MBeanInfo) o; |
|
481 if (!isEqual(getClassName(), p.getClassName()) || |
|
482 !isEqual(getDescription(), p.getDescription()) || |
|
483 !getDescriptor().equals(p.getDescriptor())) { |
|
484 return false; |
|
485 } |
|
486 |
|
487 return |
|
488 (Arrays.equals(p.fastGetAttributes(), fastGetAttributes()) && |
|
489 Arrays.equals(p.fastGetOperations(), fastGetOperations()) && |
|
490 Arrays.equals(p.fastGetConstructors(), fastGetConstructors()) && |
|
491 Arrays.equals(p.fastGetNotifications(), fastGetNotifications())); |
|
492 } |
|
493 |
|
494 public int hashCode() { |
|
495 /* Since computing the hashCode is quite expensive, we cache it. |
|
496 If by some terrible misfortune the computed value is 0, the |
|
497 caching won't work and we will recompute it every time. |
|
498 |
|
499 We don't bother synchronizing, because, at worst, n different |
|
500 threads will compute the same hashCode at the same time. */ |
|
501 if (hashCode != 0) |
|
502 return hashCode; |
|
503 |
|
504 hashCode = |
|
505 getClassName().hashCode() ^ |
|
506 getDescriptor().hashCode() ^ |
|
507 arrayHashCode(fastGetAttributes()) ^ |
|
508 arrayHashCode(fastGetOperations()) ^ |
|
509 arrayHashCode(fastGetConstructors()) ^ |
|
510 arrayHashCode(fastGetNotifications()); |
|
511 |
|
512 return hashCode; |
|
513 } |
|
514 |
|
515 private static int arrayHashCode(Object[] array) { |
|
516 int hash = 0; |
|
517 for (int i = 0; i < array.length; i++) |
|
518 hash ^= array[i].hashCode(); |
|
519 return hash; |
|
520 } |
|
521 |
|
522 /** |
|
523 * Cached results of previous calls to arrayGettersSafe. This is |
|
524 * a WeakHashMap so that we don't prevent a class from being |
|
525 * garbage collected just because we know whether it's immutable. |
|
526 */ |
|
527 private static final Map<Class, Boolean> arrayGettersSafeMap = |
|
528 new WeakHashMap<Class, Boolean>(); |
|
529 |
|
530 /** |
|
531 * Return true if <code>subclass</code> is known to preserve the |
|
532 * immutability of <code>immutableClass</code>. The class |
|
533 * <code>immutableClass</code> is a reference class that is known |
|
534 * to be immutable. The subclass <code>subclass</code> is |
|
535 * considered immutable if it does not override any public method |
|
536 * of <code>immutableClass</code> whose name begins with "get". |
|
537 * This is obviously not an infallible test for immutability, |
|
538 * but it works for the public interfaces of the MBean*Info classes. |
|
539 */ |
|
540 static boolean arrayGettersSafe(Class subclass, Class immutableClass) { |
|
541 if (subclass == immutableClass) |
|
542 return true; |
|
543 synchronized (arrayGettersSafeMap) { |
|
544 Boolean safe = arrayGettersSafeMap.get(subclass); |
|
545 if (safe == null) { |
|
546 try { |
|
547 ArrayGettersSafeAction action = |
|
548 new ArrayGettersSafeAction(subclass, immutableClass); |
|
549 safe = AccessController.doPrivileged(action); |
|
550 } catch (Exception e) { // e.g. SecurityException |
|
551 /* We don't know, so we assume it isn't. */ |
|
552 safe = false; |
|
553 } |
|
554 arrayGettersSafeMap.put(subclass, safe); |
|
555 } |
|
556 return safe; |
|
557 } |
|
558 } |
|
559 |
|
560 /* |
|
561 * The PrivilegedAction stuff is probably overkill. We can be |
|
562 * pretty sure the caller does have the required privileges -- a |
|
563 * JMX user that can't do reflection can't even use Standard |
|
564 * MBeans! But there's probably a performance gain by not having |
|
565 * to check the whole call stack. |
|
566 */ |
|
567 private static class ArrayGettersSafeAction |
|
568 implements PrivilegedAction<Boolean> { |
|
569 |
|
570 private final Class<?> subclass; |
|
571 private final Class<?> immutableClass; |
|
572 |
|
573 ArrayGettersSafeAction(Class<?> subclass, Class<?> immutableClass) { |
|
574 this.subclass = subclass; |
|
575 this.immutableClass = immutableClass; |
|
576 } |
|
577 |
|
578 public Boolean run() { |
|
579 Method[] methods = immutableClass.getMethods(); |
|
580 for (int i = 0; i < methods.length; i++) { |
|
581 Method method = methods[i]; |
|
582 String methodName = method.getName(); |
|
583 if (methodName.startsWith("get") && |
|
584 method.getParameterTypes().length == 0 && |
|
585 method.getReturnType().isArray()) { |
|
586 try { |
|
587 Method submethod = |
|
588 subclass.getMethod(methodName); |
|
589 if (!submethod.equals(method)) |
|
590 return false; |
|
591 } catch (NoSuchMethodException e) { |
|
592 return false; |
|
593 } |
|
594 } |
|
595 } |
|
596 return true; |
|
597 } |
|
598 } |
|
599 |
|
600 private static boolean isEqual(String s1, String s2) { |
|
601 boolean ret; |
|
602 |
|
603 if (s1 == null) { |
|
604 ret = (s2 == null); |
|
605 } else { |
|
606 ret = s1.equals(s2); |
|
607 } |
|
608 |
|
609 return ret; |
|
610 } |
|
611 |
|
612 /** |
|
613 * Serializes an {@link MBeanInfo} to an {@link ObjectOutputStream}. |
|
614 * @serialData |
|
615 * For compatibility reasons, an object of this class is serialized as follows. |
|
616 * <ul> |
|
617 * The method {@link ObjectOutputStream#defaultWriteObject defaultWriteObject()} |
|
618 * is called first to serialize the object except the field {@code descriptor} |
|
619 * which is declared as transient. The field {@code descriptor} is serialized |
|
620 * as follows: |
|
621 * <ul> |
|
622 * <li> If {@code descriptor} is an instance of the class |
|
623 * {@link ImmutableDescriptor}, the method {@link ObjectOutputStream#write |
|
624 * write(int val)} is called to write a byte with the value {@code 1}, |
|
625 * then the method {@link ObjectOutputStream#writeObject writeObject(Object obj)} |
|
626 * is called twice to serialize the field names and the field values of the |
|
627 * {@code descriptor}, respectively as a {@code String[]} and an |
|
628 * {@code Object[]};</li> |
|
629 * <li> Otherwise, the method {@link ObjectOutputStream#write write(int val)} |
|
630 * is called to write a byte with the value {@code 0}, then the method |
|
631 * {@link ObjectOutputStream#writeObject writeObject(Object obj)} is called |
|
632 * to serialize the field {@code descriptor} directly. |
|
633 * </ul> |
|
634 * </ul> |
|
635 * @since 1.6 |
|
636 */ |
|
637 private void writeObject(ObjectOutputStream out) throws IOException { |
|
638 out.defaultWriteObject(); |
|
639 |
|
640 if (descriptor.getClass() == ImmutableDescriptor.class) { |
|
641 out.write(1); |
|
642 |
|
643 final String[] names = descriptor.getFieldNames(); |
|
644 |
|
645 out.writeObject(names); |
|
646 out.writeObject(descriptor.getFieldValues(names)); |
|
647 } else { |
|
648 out.write(0); |
|
649 |
|
650 out.writeObject(descriptor); |
|
651 } |
|
652 } |
|
653 |
|
654 /** |
|
655 * Deserializes an {@link MBeanInfo} from an {@link ObjectInputStream}. |
|
656 * @serialData |
|
657 * For compatibility reasons, an object of this class is deserialized as follows. |
|
658 * <ul> |
|
659 * The method {@link ObjectInputStream#defaultReadObject defaultReadObject()} |
|
660 * is called first to deserialize the object except the field |
|
661 * {@code descriptor}, which is not serialized in the default way. Then the method |
|
662 * {@link ObjectInputStream#read read()} is called to read a byte, the field |
|
663 * {@code descriptor} is deserialized according to the value of the byte value: |
|
664 * <ul> |
|
665 * <li>1. The method {@link ObjectInputStream#readObject readObject()} |
|
666 * is called twice to obtain the field names (a {@code String[]}) and |
|
667 * the field values (a {@code Object[]}) of the {@code descriptor}. |
|
668 * The two obtained values then are used to construct |
|
669 * an {@link ImmutableDescriptor} instance for the field |
|
670 * {@code descriptor};</li> |
|
671 * <li>0. The value for the field {@code descriptor} is obtained directly |
|
672 * by calling the method {@link ObjectInputStream#readObject readObject()}. |
|
673 * If the obtained value is null, the field {@code descriptor} is set to |
|
674 * {@link ImmutableDescriptor#EMPTY_DESCRIPTOR EMPTY_DESCRIPTOR};</li> |
|
675 * <li>-1. This means that there is no byte to read and that the object is from |
|
676 * an earlier version of the JMX API. The field {@code descriptor} is set to |
|
677 * {@link ImmutableDescriptor#EMPTY_DESCRIPTOR EMPTY_DESCRIPTOR}.</li> |
|
678 * <li>Any other value. A {@link StreamCorruptedException} is thrown.</li> |
|
679 * </ul> |
|
680 * </ul> |
|
681 * @since 1.6 |
|
682 */ |
|
683 |
|
684 private void readObject(ObjectInputStream in) |
|
685 throws IOException, ClassNotFoundException { |
|
686 |
|
687 in.defaultReadObject(); |
|
688 |
|
689 switch (in.read()) { |
|
690 case 1: |
|
691 final String[] names = (String[])in.readObject(); |
|
692 |
|
693 if (names.length == 0) { |
|
694 descriptor = ImmutableDescriptor.EMPTY_DESCRIPTOR; |
|
695 } else { |
|
696 final Object[] values = (Object[])in.readObject(); |
|
697 descriptor = new ImmutableDescriptor(names, values); |
|
698 } |
|
699 |
|
700 break; |
|
701 case 0: |
|
702 descriptor = (Descriptor)in.readObject(); |
|
703 |
|
704 if (descriptor == null) { |
|
705 descriptor = ImmutableDescriptor.EMPTY_DESCRIPTOR; |
|
706 } |
|
707 |
|
708 break; |
|
709 case -1: // from an earlier version of the JMX API |
|
710 descriptor = ImmutableDescriptor.EMPTY_DESCRIPTOR; |
|
711 |
|
712 break; |
|
713 default: |
|
714 throw new StreamCorruptedException("Got unexpected byte."); |
|
715 } |
|
716 } |
|
717 } |