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