blob: 9e30d44a51d3ce587e9f6708472bf1ef6751cfcc [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
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
26package javax.management;
27
28import java.io.IOException;
29import java.io.StreamCorruptedException;
30import java.io.Serializable;
31import java.io.ObjectOutputStream;
32import java.io.ObjectInputStream;
33import java.lang.reflect.Method;
34import java.util.Arrays;
35import java.util.Map;
36import java.util.WeakHashMap;
37import java.security.AccessController;
38import java.security.PrivilegedAction;
39
40import 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 */
96public 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}