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</code> 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</code> 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</code> 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</CODE>. 165 * 166 * @param className The name of the Java class of the MBean described 167 * by this <CODE>MBeanInfo</CODE>. 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 */ MBeanInfo(String className, String description, MBeanAttributeInfo[] attributes, MBeanConstructorInfo[] constructors, MBeanOperationInfo[] operations, MBeanNotificationInfo[] notifications)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</CODE>. 199 * 200 * @param className The name of the Java class of the MBean described 201 * by this <CODE>MBeanInfo</CODE>. 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 */ MBeanInfo(String className, String description, MBeanAttributeInfo[] attributes, MBeanConstructorInfo[] constructors, MBeanOperationInfo[] operations, MBeanNotificationInfo[] notifications, Descriptor descriptor)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 <tt>super.clone()</tt>, 264 * thus calling the default native shallow cloning mechanism 265 * implemented by <tt>Object.clone()</tt>. 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 clone()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</CODE>. 285 * 286 * @return the class name. 287 */ getClassName()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 */ getDescription()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</CODE> 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</CODE> objects 308 * but that each referenced <CODE>MBeanAttributeInfo</CODE> object is not copied. 309 * 310 * @return An array of <CODE>MBeanAttributeInfo</CODE> objects. 311 */ getAttributes()312 public MBeanAttributeInfo[] getAttributes() { 313 MBeanAttributeInfo[] as = nonNullAttributes(); 314 if (as.length == 0) 315 return as; 316 else 317 return as.clone(); 318 } 319 fastGetAttributes()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 */ nonNullAttributes()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</CODE> 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</CODE> objects 350 * but that each referenced <CODE>MBeanOperationInfo</CODE> object is not copied. 351 * 352 * @return An array of <CODE>MBeanOperationInfo</CODE> objects. 353 */ getOperations()354 public MBeanOperationInfo[] getOperations() { 355 MBeanOperationInfo[] os = nonNullOperations(); 356 if (os.length == 0) 357 return os; 358 else 359 return os.clone(); 360 } 361 fastGetOperations()362 private MBeanOperationInfo[] fastGetOperations() { 363 if (arrayGettersSafe) 364 return nonNullOperations(); 365 else 366 return getOperations(); 367 } 368 nonNullOperations()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</CODE> 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</CODE> objects but 382 * that each referenced <CODE>MBeanConstructorInfo</CODE> 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</CODE> objects. 392 */ getConstructors()393 public MBeanConstructorInfo[] getConstructors() { 394 MBeanConstructorInfo[] cs = nonNullConstructors(); 395 if (cs.length == 0) 396 return cs; 397 else 398 return cs.clone(); 399 } 400 fastGetConstructors()401 private MBeanConstructorInfo[] fastGetConstructors() { 402 if (arrayGettersSafe) 403 return nonNullConstructors(); 404 else 405 return getConstructors(); 406 } 407 nonNullConstructors()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</CODE> 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</CODE> objects 420 * but that each referenced <CODE>MBeanNotificationInfo</CODE> object is not copied. 421 * 422 * @return An array of <CODE>MBeanNotificationInfo</CODE> objects. 423 */ getNotifications()424 public MBeanNotificationInfo[] getNotifications() { 425 MBeanNotificationInfo[] ns = nonNullNotifications(); 426 if (ns.length == 0) 427 return ns; 428 else 429 return ns.clone(); 430 } 431 fastGetNotifications()432 private MBeanNotificationInfo[] fastGetNotifications() { 433 if (arrayGettersSafe) 434 return nonNullNotifications(); 435 else 436 return getNotifications(); 437 } 438 nonNullNotifications()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 */ getDescriptor()452 public Descriptor getDescriptor() { 453 return (Descriptor) nonNullDescriptor(descriptor).clone(); 454 } 455 456 @Override toString()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</code> is an MBeanInfo that is equal 486 * to this one according to the rules above. 487 */ 488 @Override equals(Object o)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 hashCode()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</code> is known to preserve the 538 * immutability of <code>immutableClass</code>. The class 539 * <code>immutableClass</code> is a reference class that is known 540 * to be immutable. The subclass <code>subclass</code> is 541 * considered immutable if it does not override any public method 542 * of <code>immutableClass</code> 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 */ arrayGettersSafe(Class<?> subclass, Class<?> immutableClass)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 ArrayGettersSafeAction(Class<?> subclass, Class<?> immutableClass)579 ArrayGettersSafeAction(Class<?> subclass, Class<?> immutableClass) { 580 this.subclass = subclass; 581 this.immutableClass = immutableClass; 582 } 583 run()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 isEqual(String s1, String s2)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 */ writeObject(ObjectOutputStream out)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 (a {@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 readObject(ObjectInputStream in)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