1 /* 2 * Copyright (c) 1996, 2010, 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 java.io; 27 28 import java.io.ObjectStreamClass.WeakClassKey; 29 import java.lang.ref.ReferenceQueue; 30 import java.security.AccessController; 31 import java.security.PrivilegedAction; 32 import java.util.ArrayList; 33 import java.util.Arrays; 34 import java.util.List; 35 import java.util.concurrent.ConcurrentHashMap; 36 import java.util.concurrent.ConcurrentMap; 37 import static java.io.ObjectStreamClass.processQueue; 38 import java.io.SerialCallbackContext; 39 40 /** 41 * An ObjectOutputStream writes primitive data types and graphs of Java objects 42 * to an OutputStream. The objects can be read (reconstituted) using an 43 * ObjectInputStream. Persistent storage of objects can be accomplished by 44 * using a file for the stream. If the stream is a network socket stream, the 45 * objects can be reconstituted on another host or in another process. 46 * 47 * <p>Only objects that support the java.io.Serializable interface can be 48 * written to streams. The class of each serializable object is encoded 49 * including the class name and signature of the class, the values of the 50 * object's fields and arrays, and the closure of any other objects referenced 51 * from the initial objects. 52 * 53 * <p>The method writeObject is used to write an object to the stream. Any 54 * object, including Strings and arrays, is written with writeObject. Multiple 55 * objects or primitives can be written to the stream. The objects must be 56 * read back from the corresponding ObjectInputstream with the same types and 57 * in the same order as they were written. 58 * 59 * <p>Primitive data types can also be written to the stream using the 60 * appropriate methods from DataOutput. Strings can also be written using the 61 * writeUTF method. 62 * 63 * <p>The default serialization mechanism for an object writes the class of the 64 * object, the class signature, and the values of all non-transient and 65 * non-static fields. References to other objects (except in transient or 66 * static fields) cause those objects to be written also. Multiple references 67 * to a single object are encoded using a reference sharing mechanism so that 68 * graphs of objects can be restored to the same shape as when the original was 69 * written. 70 * 71 * <p>For example to write an object that can be read by the example in 72 * ObjectInputStream: 73 * <br> 74 * <pre> 75 * FileOutputStream fos = new FileOutputStream("t.tmp"); 76 * ObjectOutputStream oos = new ObjectOutputStream(fos); 77 * 78 * oos.writeInt(12345); 79 * oos.writeObject("Today"); 80 * oos.writeObject(new Date()); 81 * 82 * oos.close(); 83 * </pre> 84 * 85 * <p>Classes that require special handling during the serialization and 86 * deserialization process must implement special methods with these exact 87 * signatures: 88 * <br> 89 * <pre> 90 * private void readObject(java.io.ObjectInputStream stream) 91 * throws IOException, ClassNotFoundException; 92 * private void writeObject(java.io.ObjectOutputStream stream) 93 * throws IOException 94 * private void readObjectNoData() 95 * throws ObjectStreamException; 96 * </pre> 97 * 98 * <p>The writeObject method is responsible for writing the state of the object 99 * for its particular class so that the corresponding readObject method can 100 * restore it. The method does not need to concern itself with the state 101 * belonging to the object's superclasses or subclasses. State is saved by 102 * writing the individual fields to the ObjectOutputStream using the 103 * writeObject method or by using the methods for primitive data types 104 * supported by DataOutput. 105 * 106 * <p>Serialization does not write out the fields of any object that does not 107 * implement the java.io.Serializable interface. Subclasses of Objects that 108 * are not serializable can be serializable. In this case the non-serializable 109 * class must have a no-arg constructor to allow its fields to be initialized. 110 * In this case it is the responsibility of the subclass to save and restore 111 * the state of the non-serializable class. It is frequently the case that the 112 * fields of that class are accessible (public, package, or protected) or that 113 * there are get and set methods that can be used to restore the state. 114 * 115 * <p>Serialization of an object can be prevented by implementing writeObject 116 * and readObject methods that throw the NotSerializableException. The 117 * exception will be caught by the ObjectOutputStream and abort the 118 * serialization process. 119 * 120 * <p>Implementing the Externalizable interface allows the object to assume 121 * complete control over the contents and format of the object's serialized 122 * form. The methods of the Externalizable interface, writeExternal and 123 * readExternal, are called to save and restore the objects state. When 124 * implemented by a class they can write and read their own state using all of 125 * the methods of ObjectOutput and ObjectInput. It is the responsibility of 126 * the objects to handle any versioning that occurs. 127 * 128 * <p>Enum constants are serialized differently than ordinary serializable or 129 * externalizable objects. The serialized form of an enum constant consists 130 * solely of its name; field values of the constant are not transmitted. To 131 * serialize an enum constant, ObjectOutputStream writes the string returned by 132 * the constant's name method. Like other serializable or externalizable 133 * objects, enum constants can function as the targets of back references 134 * appearing subsequently in the serialization stream. The process by which 135 * enum constants are serialized cannot be customized; any class-specific 136 * writeObject and writeReplace methods defined by enum types are ignored 137 * during serialization. Similarly, any serialPersistentFields or 138 * serialVersionUID field declarations are also ignored--all enum types have a 139 * fixed serialVersionUID of 0L. 140 * 141 * <p>Primitive data, excluding serializable fields and externalizable data, is 142 * written to the ObjectOutputStream in block-data records. A block data record 143 * is composed of a header and data. The block data header consists of a marker 144 * and the number of bytes to follow the header. Consecutive primitive data 145 * writes are merged into one block-data record. The blocking factor used for 146 * a block-data record will be 1024 bytes. Each block-data record will be 147 * filled up to 1024 bytes, or be written whenever there is a termination of 148 * block-data mode. Calls to the ObjectOutputStream methods writeObject, 149 * defaultWriteObject and writeFields initially terminate any existing 150 * block-data record. 151 * 152 * @author Mike Warres 153 * @author Roger Riggs 154 * @see java.io.DataOutput 155 * @see java.io.ObjectInputStream 156 * @see java.io.Serializable 157 * @see java.io.Externalizable 158 * @see <a href="../../../platform/serialization/spec/output.html">Object Serialization Specification, Section 2, Object Output Classes</a> 159 * @since JDK1.1 160 */ 161 public class ObjectOutputStream 162 extends OutputStream implements ObjectOutput, ObjectStreamConstants 163 { 164 165 private static class Caches { 166 /** cache of subclass security audit results */ 167 static final ConcurrentMap<WeakClassKey,Boolean> subclassAudits = 168 new ConcurrentHashMap<WeakClassKey,Boolean>(); 169 170 /** queue for WeakReferences to audited subclasses */ 171 static final ReferenceQueue<Class<?>> subclassAuditsQueue = 172 new ReferenceQueue<Class<?>>(); 173 } 174 175 /** filter stream for handling block data conversion */ 176 private final BlockDataOutputStream bout; 177 /** obj -> wire handle map */ 178 private final HandleTable handles; 179 /** obj -> replacement obj map */ 180 private final ReplaceTable subs; 181 /** stream protocol version */ 182 private int protocol = PROTOCOL_VERSION_2; 183 /** recursion depth */ 184 private int depth; 185 186 /** buffer for writing primitive field values */ 187 private byte[] primVals; 188 189 /** if true, invoke writeObjectOverride() instead of writeObject() */ 190 private final boolean enableOverride; 191 /** if true, invoke replaceObject() */ 192 private boolean enableReplace; 193 194 // values below valid only during upcalls to writeObject()/writeExternal() 195 /** 196 * Context during upcalls to class-defined writeObject methods; holds 197 * object currently being serialized and descriptor for current class. 198 * Null when not during writeObject upcall. 199 */ 200 private SerialCallbackContext curContext; 201 /** current PutField object */ 202 private PutFieldImpl curPut; 203 204 /** custom storage for debug trace info */ 205 private final DebugTraceInfoStack debugInfoStack; 206 207 /** 208 * value of "sun.io.serialization.extendedDebugInfo" property, 209 * as true or false for extended information about exception's place 210 */ 211 private static final boolean extendedDebugInfo = 212 java.security.AccessController.doPrivileged( 213 new sun.security.action.GetBooleanAction( 214 "sun.io.serialization.extendedDebugInfo")).booleanValue(); 215 216 /** 217 * Creates an ObjectOutputStream that writes to the specified OutputStream. 218 * This constructor writes the serialization stream header to the 219 * underlying stream; callers may wish to flush the stream immediately to 220 * ensure that constructors for receiving ObjectInputStreams will not block 221 * when reading the header. 222 * 223 * <p>If a security manager is installed, this constructor will check for 224 * the "enableSubclassImplementation" SerializablePermission when invoked 225 * directly or indirectly by the constructor of a subclass which overrides 226 * the ObjectOutputStream.putFields or ObjectOutputStream.writeUnshared 227 * methods. 228 * 229 * @param out output stream to write to 230 * @throws IOException if an I/O error occurs while writing stream header 231 * @throws SecurityException if untrusted subclass illegally overrides 232 * security-sensitive methods 233 * @throws NullPointerException if <code>out</code> is <code>null</code> 234 * @since 1.4 235 * @see ObjectOutputStream#ObjectOutputStream() 236 * @see ObjectOutputStream#putFields() 237 * @see ObjectInputStream#ObjectInputStream(InputStream) 238 */ 239 public ObjectOutputStream(OutputStream out) throws IOException { 240 verifySubclass(); 241 bout = new BlockDataOutputStream(out); 242 handles = new HandleTable(10, (float) 3.00); 243 subs = new ReplaceTable(10, (float) 3.00); 244 enableOverride = false; 245 writeStreamHeader(); 246 bout.setBlockDataMode(true); 247 if (extendedDebugInfo) { 248 debugInfoStack = new DebugTraceInfoStack(); 249 } else { 250 debugInfoStack = null; 251 } 252 } 253 254 /** 255 * Provide a way for subclasses that are completely reimplementing 256 * ObjectOutputStream to not have to allocate private data just used by 257 * this implementation of ObjectOutputStream. 258 * 259 * <p>If there is a security manager installed, this method first calls the 260 * security manager's <code>checkPermission</code> method with a 261 * <code>SerializablePermission("enableSubclassImplementation")</code> 262 * permission to ensure it's ok to enable subclassing. 263 * 264 * @throws SecurityException if a security manager exists and its 265 * <code>checkPermission</code> method denies enabling 266 * subclassing. 267 * @see SecurityManager#checkPermission 268 * @see java.io.SerializablePermission 269 */ 270 protected ObjectOutputStream() throws IOException, SecurityException { 271 SecurityManager sm = System.getSecurityManager(); 272 if (sm != null) { 273 sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); 274 } 275 bout = null; 276 handles = null; 277 subs = null; 278 enableOverride = true; 279 debugInfoStack = null; 280 } 281 282 /** 283 * Specify stream protocol version to use when writing the stream. 284 * 285 * <p>This routine provides a hook to enable the current version of 286 * Serialization to write in a format that is backwards compatible to a 287 * previous version of the stream format. 288 * 289 * <p>Every effort will be made to avoid introducing additional 290 * backwards incompatibilities; however, sometimes there is no 291 * other alternative. 292 * 293 * @param version use ProtocolVersion from java.io.ObjectStreamConstants. 294 * @throws IllegalStateException if called after any objects 295 * have been serialized. 296 * @throws IllegalArgumentException if invalid version is passed in. 297 * @throws IOException if I/O errors occur 298 * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1 299 * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_2 300 * @since 1.2 301 */ 302 public void useProtocolVersion(int version) throws IOException { 303 if (handles.size() != 0) { 304 // REMIND: implement better check for pristine stream? 305 throw new IllegalStateException("stream non-empty"); 306 } 307 switch (version) { 308 case PROTOCOL_VERSION_1: 309 case PROTOCOL_VERSION_2: 310 protocol = version; 311 break; 312 313 default: 314 throw new IllegalArgumentException( 315 "unknown version: " + version); 316 } 317 } 318 319 /** 320 * Write the specified object to the ObjectOutputStream. The class of the 321 * object, the signature of the class, and the values of the non-transient 322 * and non-static fields of the class and all of its supertypes are 323 * written. Default serialization for a class can be overridden using the 324 * writeObject and the readObject methods. Objects referenced by this 325 * object are written transitively so that a complete equivalent graph of 326 * objects can be reconstructed by an ObjectInputStream. 327 * 328 * <p>Exceptions are thrown for problems with the OutputStream and for 329 * classes that should not be serialized. All exceptions are fatal to the 330 * OutputStream, which is left in an indeterminate state, and it is up to 331 * the caller to ignore or recover the stream state. 332 * 333 * @throws InvalidClassException Something is wrong with a class used by 334 * serialization. 335 * @throws NotSerializableException Some object to be serialized does not 336 * implement the java.io.Serializable interface. 337 * @throws IOException Any exception thrown by the underlying 338 * OutputStream. 339 */ 340 public final void writeObject(Object obj) throws IOException { 341 if (enableOverride) { 342 writeObjectOverride(obj); 343 return; 344 } 345 try { 346 writeObject0(obj, false); 347 } catch (IOException ex) { 348 if (depth == 0) { 349 writeFatalException(ex); 350 } 351 throw ex; 352 } 353 } 354 355 /** 356 * Method used by subclasses to override the default writeObject method. 357 * This method is called by trusted subclasses of ObjectInputStream that 358 * constructed ObjectInputStream using the protected no-arg constructor. 359 * The subclass is expected to provide an override method with the modifier 360 * "final". 361 * 362 * @param obj object to be written to the underlying stream 363 * @throws IOException if there are I/O errors while writing to the 364 * underlying stream 365 * @see #ObjectOutputStream() 366 * @see #writeObject(Object) 367 * @since 1.2 368 */ 369 protected void writeObjectOverride(Object obj) throws IOException { 370 } 371 372 /** 373 * Writes an "unshared" object to the ObjectOutputStream. This method is 374 * identical to writeObject, except that it always writes the given object 375 * as a new, unique object in the stream (as opposed to a back-reference 376 * pointing to a previously serialized instance). Specifically: 377 * <ul> 378 * <li>An object written via writeUnshared is always serialized in the 379 * same manner as a newly appearing object (an object that has not 380 * been written to the stream yet), regardless of whether or not the 381 * object has been written previously. 382 * 383 * <li>If writeObject is used to write an object that has been previously 384 * written with writeUnshared, the previous writeUnshared operation 385 * is treated as if it were a write of a separate object. In other 386 * words, ObjectOutputStream will never generate back-references to 387 * object data written by calls to writeUnshared. 388 * </ul> 389 * While writing an object via writeUnshared does not in itself guarantee a 390 * unique reference to the object when it is deserialized, it allows a 391 * single object to be defined multiple times in a stream, so that multiple 392 * calls to readUnshared by the receiver will not conflict. Note that the 393 * rules described above only apply to the base-level object written with 394 * writeUnshared, and not to any transitively referenced sub-objects in the 395 * object graph to be serialized. 396 * 397 * <p>ObjectOutputStream subclasses which override this method can only be 398 * constructed in security contexts possessing the 399 * "enableSubclassImplementation" SerializablePermission; any attempt to 400 * instantiate such a subclass without this permission will cause a 401 * SecurityException to be thrown. 402 * 403 * @param obj object to write to stream 404 * @throws NotSerializableException if an object in the graph to be 405 * serialized does not implement the Serializable interface 406 * @throws InvalidClassException if a problem exists with the class of an 407 * object to be serialized 408 * @throws IOException if an I/O error occurs during serialization 409 * @since 1.4 410 */ 411 public void writeUnshared(Object obj) throws IOException { 412 try { 413 writeObject0(obj, true); 414 } catch (IOException ex) { 415 if (depth == 0) { 416 writeFatalException(ex); 417 } 418 throw ex; 419 } 420 } 421 422 /** 423 * Write the non-static and non-transient fields of the current class to 424 * this stream. This may only be called from the writeObject method of the 425 * class being serialized. It will throw the NotActiveException if it is 426 * called otherwise. 427 * 428 * @throws IOException if I/O errors occur while writing to the underlying 429 * <code>OutputStream</code> 430 */ 431 public void defaultWriteObject() throws IOException { 432 if ( curContext == null ) { 433 throw new NotActiveException("not in call to writeObject"); 434 } 435 Object curObj = curContext.getObj(); 436 ObjectStreamClass curDesc = curContext.getDesc(); 437 bout.setBlockDataMode(false); 438 defaultWriteFields(curObj, curDesc); 439 bout.setBlockDataMode(true); 440 } 441 442 /** 443 * Retrieve the object used to buffer persistent fields to be written to 444 * the stream. The fields will be written to the stream when writeFields 445 * method is called. 446 * 447 * @return an instance of the class Putfield that holds the serializable 448 * fields 449 * @throws IOException if I/O errors occur 450 * @since 1.2 451 */ 452 public ObjectOutputStream.PutField putFields() throws IOException { 453 if (curPut == null) { 454 if (curContext == null) { 455 throw new NotActiveException("not in call to writeObject"); 456 } 457 Object curObj = curContext.getObj(); 458 ObjectStreamClass curDesc = curContext.getDesc(); 459 curPut = new PutFieldImpl(curDesc); 460 } 461 return curPut; 462 } 463 464 /** 465 * Write the buffered fields to the stream. 466 * 467 * @throws IOException if I/O errors occur while writing to the underlying 468 * stream 469 * @throws NotActiveException Called when a classes writeObject method was 470 * not called to write the state of the object. 471 * @since 1.2 472 */ 473 public void writeFields() throws IOException { 474 if (curPut == null) { 475 throw new NotActiveException("no current PutField object"); 476 } 477 bout.setBlockDataMode(false); 478 curPut.writeFields(); 479 bout.setBlockDataMode(true); 480 } 481 482 /** 483 * Reset will disregard the state of any objects already written to the 484 * stream. The state is reset to be the same as a new ObjectOutputStream. 485 * The current point in the stream is marked as reset so the corresponding 486 * ObjectInputStream will be reset at the same point. Objects previously 487 * written to the stream will not be refered to as already being in the 488 * stream. They will be written to the stream again. 489 * 490 * @throws IOException if reset() is invoked while serializing an object. 491 */ 492 public void reset() throws IOException { 493 if (depth != 0) { 494 throw new IOException("stream active"); 495 } 496 bout.setBlockDataMode(false); 497 bout.writeByte(TC_RESET); 498 clear(); 499 bout.setBlockDataMode(true); 500 } 501 502 /** 503 * Subclasses may implement this method to allow class data to be stored in 504 * the stream. By default this method does nothing. The corresponding 505 * method in ObjectInputStream is resolveClass. This method is called 506 * exactly once for each unique class in the stream. The class name and 507 * signature will have already been written to the stream. This method may 508 * make free use of the ObjectOutputStream to save any representation of 509 * the class it deems suitable (for example, the bytes of the class file). 510 * The resolveClass method in the corresponding subclass of 511 * ObjectInputStream must read and use any data or objects written by 512 * annotateClass. 513 * 514 * @param cl the class to annotate custom data for 515 * @throws IOException Any exception thrown by the underlying 516 * OutputStream. 517 */ 518 protected void annotateClass(Class<?> cl) throws IOException { 519 } 520 521 /** 522 * Subclasses may implement this method to store custom data in the stream 523 * along with descriptors for dynamic proxy classes. 524 * 525 * <p>This method is called exactly once for each unique proxy class 526 * descriptor in the stream. The default implementation of this method in 527 * <code>ObjectOutputStream</code> does nothing. 528 * 529 * <p>The corresponding method in <code>ObjectInputStream</code> is 530 * <code>resolveProxyClass</code>. For a given subclass of 531 * <code>ObjectOutputStream</code> that overrides this method, the 532 * <code>resolveProxyClass</code> method in the corresponding subclass of 533 * <code>ObjectInputStream</code> must read any data or objects written by 534 * <code>annotateProxyClass</code>. 535 * 536 * @param cl the proxy class to annotate custom data for 537 * @throws IOException any exception thrown by the underlying 538 * <code>OutputStream</code> 539 * @see ObjectInputStream#resolveProxyClass(String[]) 540 * @since 1.3 541 */ 542 protected void annotateProxyClass(Class<?> cl) throws IOException { 543 } 544 545 /** 546 * This method will allow trusted subclasses of ObjectOutputStream to 547 * substitute one object for another during serialization. Replacing 548 * objects is disabled until enableReplaceObject is called. The 549 * enableReplaceObject method checks that the stream requesting to do 550 * replacement can be trusted. The first occurrence of each object written 551 * into the serialization stream is passed to replaceObject. Subsequent 552 * references to the object are replaced by the object returned by the 553 * original call to replaceObject. To ensure that the private state of 554 * objects is not unintentionally exposed, only trusted streams may use 555 * replaceObject. 556 * 557 * <p>The ObjectOutputStream.writeObject method takes a parameter of type 558 * Object (as opposed to type Serializable) to allow for cases where 559 * non-serializable objects are replaced by serializable ones. 560 * 561 * <p>When a subclass is replacing objects it must insure that either a 562 * complementary substitution must be made during deserialization or that 563 * the substituted object is compatible with every field where the 564 * reference will be stored. Objects whose type is not a subclass of the 565 * type of the field or array element abort the serialization by raising an 566 * exception and the object is not be stored. 567 * 568 * <p>This method is called only once when each object is first 569 * encountered. All subsequent references to the object will be redirected 570 * to the new object. This method should return the object to be 571 * substituted or the original object. 572 * 573 * <p>Null can be returned as the object to be substituted, but may cause 574 * NullReferenceException in classes that contain references to the 575 * original object since they may be expecting an object instead of 576 * null. 577 * 578 * @param obj the object to be replaced 579 * @return the alternate object that replaced the specified one 580 * @throws IOException Any exception thrown by the underlying 581 * OutputStream. 582 */ 583 protected Object replaceObject(Object obj) throws IOException { 584 return obj; 585 } 586 587 /** 588 * Enable the stream to do replacement of objects in the stream. When 589 * enabled, the replaceObject method is called for every object being 590 * serialized. 591 * 592 * <p>If <code>enable</code> is true, and there is a security manager 593 * installed, this method first calls the security manager's 594 * <code>checkPermission</code> method with a 595 * <code>SerializablePermission("enableSubstitution")</code> permission to 596 * ensure it's ok to enable the stream to do replacement of objects in the 597 * stream. 598 * 599 * @param enable boolean parameter to enable replacement of objects 600 * @return the previous setting before this method was invoked 601 * @throws SecurityException if a security manager exists and its 602 * <code>checkPermission</code> method denies enabling the stream 603 * to do replacement of objects in the stream. 604 * @see SecurityManager#checkPermission 605 * @see java.io.SerializablePermission 606 */ 607 protected boolean enableReplaceObject(boolean enable) 608 throws SecurityException 609 { 610 if (enable == enableReplace) { 611 return enable; 612 } 613 if (enable) { 614 SecurityManager sm = System.getSecurityManager(); 615 if (sm != null) { 616 sm.checkPermission(SUBSTITUTION_PERMISSION); 617 } 618 } 619 enableReplace = enable; 620 return !enableReplace; 621 } 622 623 /** 624 * The writeStreamHeader method is provided so subclasses can append or 625 * prepend their own header to the stream. It writes the magic number and 626 * version to the stream. 627 * 628 * @throws IOException if I/O errors occur while writing to the underlying 629 * stream 630 */ 631 protected void writeStreamHeader() throws IOException { 632 bout.writeShort(STREAM_MAGIC); 633 bout.writeShort(STREAM_VERSION); 634 } 635 636 /** 637 * Write the specified class descriptor to the ObjectOutputStream. Class 638 * descriptors are used to identify the classes of objects written to the 639 * stream. Subclasses of ObjectOutputStream may override this method to 640 * customize the way in which class descriptors are written to the 641 * serialization stream. The corresponding method in ObjectInputStream, 642 * <code>readClassDescriptor</code>, should then be overridden to 643 * reconstitute the class descriptor from its custom stream representation. 644 * By default, this method writes class descriptors according to the format 645 * defined in the Object Serialization specification. 646 * 647 * <p>Note that this method will only be called if the ObjectOutputStream 648 * is not using the old serialization stream format (set by calling 649 * ObjectOutputStream's <code>useProtocolVersion</code> method). If this 650 * serialization stream is using the old format 651 * (<code>PROTOCOL_VERSION_1</code>), the class descriptor will be written 652 * internally in a manner that cannot be overridden or customized. 653 * 654 * @param desc class descriptor to write to the stream 655 * @throws IOException If an I/O error has occurred. 656 * @see java.io.ObjectInputStream#readClassDescriptor() 657 * @see #useProtocolVersion(int) 658 * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1 659 * @since 1.3 660 */ 661 protected void writeClassDescriptor(ObjectStreamClass desc) 662 throws IOException 663 { 664 desc.writeNonProxy(this); 665 } 666 667 /** 668 * Writes a byte. This method will block until the byte is actually 669 * written. 670 * 671 * @param val the byte to be written to the stream 672 * @throws IOException If an I/O error has occurred. 673 */ 674 public void write(int val) throws IOException { 675 bout.write(val); 676 } 677 678 /** 679 * Writes an array of bytes. This method will block until the bytes are 680 * actually written. 681 * 682 * @param buf the data to be written 683 * @throws IOException If an I/O error has occurred. 684 */ 685 public void write(byte[] buf) throws IOException { 686 bout.write(buf, 0, buf.length, false); 687 } 688 689 /** 690 * Writes a sub array of bytes. 691 * 692 * @param buf the data to be written 693 * @param off the start offset in the data 694 * @param len the number of bytes that are written 695 * @throws IOException If an I/O error has occurred. 696 */ 697 public void write(byte[] buf, int off, int len) throws IOException { 698 if (buf == null) { 699 throw new NullPointerException(); 700 } 701 int endoff = off + len; 702 if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) { 703 throw new IndexOutOfBoundsException(); 704 } 705 bout.write(buf, off, len, false); 706 } 707 708 /** 709 * Flushes the stream. This will write any buffered output bytes and flush 710 * through to the underlying stream. 711 * 712 * @throws IOException If an I/O error has occurred. 713 */ 714 public void flush() throws IOException { 715 bout.flush(); 716 } 717 718 /** 719 * Drain any buffered data in ObjectOutputStream. Similar to flush but 720 * does not propagate the flush to the underlying stream. 721 * 722 * @throws IOException if I/O errors occur while writing to the underlying 723 * stream 724 */ 725 protected void drain() throws IOException { 726 bout.drain(); 727 } 728 729 /** 730 * Closes the stream. This method must be called to release any resources 731 * associated with the stream. 732 * 733 * @throws IOException If an I/O error has occurred. 734 */ 735 public void close() throws IOException { 736 flush(); 737 clear(); 738 bout.close(); 739 } 740 741 /** 742 * Writes a boolean. 743 * 744 * @param val the boolean to be written 745 * @throws IOException if I/O errors occur while writing to the underlying 746 * stream 747 */ 748 public void writeBoolean(boolean val) throws IOException { 749 bout.writeBoolean(val); 750 } 751 752 /** 753 * Writes an 8 bit byte. 754 * 755 * @param val the byte value to be written 756 * @throws IOException if I/O errors occur while writing to the underlying 757 * stream 758 */ 759 public void writeByte(int val) throws IOException { 760 bout.writeByte(val); 761 } 762 763 /** 764 * Writes a 16 bit short. 765 * 766 * @param val the short value to be written 767 * @throws IOException if I/O errors occur while writing to the underlying 768 * stream 769 */ 770 public void writeShort(int val) throws IOException { 771 bout.writeShort(val); 772 } 773 774 /** 775 * Writes a 16 bit char. 776 * 777 * @param val the char value to be written 778 * @throws IOException if I/O errors occur while writing to the underlying 779 * stream 780 */ 781 public void writeChar(int val) throws IOException { 782 bout.writeChar(val); 783 } 784 785 /** 786 * Writes a 32 bit int. 787 * 788 * @param val the integer value to be written 789 * @throws IOException if I/O errors occur while writing to the underlying 790 * stream 791 */ 792 public void writeInt(int val) throws IOException { 793 bout.writeInt(val); 794 } 795 796 /** 797 * Writes a 64 bit long. 798 * 799 * @param val the long value to be written 800 * @throws IOException if I/O errors occur while writing to the underlying 801 * stream 802 */ 803 public void writeLong(long val) throws IOException { 804 bout.writeLong(val); 805 } 806 807 /** 808 * Writes a 32 bit float. 809 * 810 * @param val the float value to be written 811 * @throws IOException if I/O errors occur while writing to the underlying 812 * stream 813 */ 814 public void writeFloat(float val) throws IOException { 815 bout.writeFloat(val); 816 } 817 818 /** 819 * Writes a 64 bit double. 820 * 821 * @param val the double value to be written 822 * @throws IOException if I/O errors occur while writing to the underlying 823 * stream 824 */ 825 public void writeDouble(double val) throws IOException { 826 bout.writeDouble(val); 827 } 828 829 /** 830 * Writes a String as a sequence of bytes. 831 * 832 * @param str the String of bytes to be written 833 * @throws IOException if I/O errors occur while writing to the underlying 834 * stream 835 */ 836 public void writeBytes(String str) throws IOException { 837 bout.writeBytes(str); 838 } 839 840 /** 841 * Writes a String as a sequence of chars. 842 * 843 * @param str the String of chars to be written 844 * @throws IOException if I/O errors occur while writing to the underlying 845 * stream 846 */ 847 public void writeChars(String str) throws IOException { 848 bout.writeChars(str); 849 } 850 851 /** 852 * Primitive data write of this String in 853 * <a href="DataInput.html#modified-utf-8">modified UTF-8</a> 854 * format. Note that there is a 855 * significant difference between writing a String into the stream as 856 * primitive data or as an Object. A String instance written by writeObject 857 * is written into the stream as a String initially. Future writeObject() 858 * calls write references to the string into the stream. 859 * 860 * @param str the String to be written 861 * @throws IOException if I/O errors occur while writing to the underlying 862 * stream 863 */ 864 public void writeUTF(String str) throws IOException { 865 bout.writeUTF(str); 866 } 867 868 /** 869 * Provide programmatic access to the persistent fields to be written 870 * to ObjectOutput. 871 * 872 * @since 1.2 873 */ 874 public static abstract class PutField { 875 876 /** 877 * Put the value of the named boolean field into the persistent field. 878 * 879 * @param name the name of the serializable field 880 * @param val the value to assign to the field 881 * @throws IllegalArgumentException if <code>name</code> does not 882 * match the name of a serializable field for the class whose fields 883 * are being written, or if the type of the named field is not 884 * <code>boolean</code> 885 */ 886 public abstract void put(String name, boolean val); 887 888 /** 889 * Put the value of the named byte field into the persistent field. 890 * 891 * @param name the name of the serializable field 892 * @param val the value to assign to the field 893 * @throws IllegalArgumentException if <code>name</code> does not 894 * match the name of a serializable field for the class whose fields 895 * are being written, or if the type of the named field is not 896 * <code>byte</code> 897 */ 898 public abstract void put(String name, byte val); 899 900 /** 901 * Put the value of the named char field into the persistent field. 902 * 903 * @param name the name of the serializable field 904 * @param val the value to assign to the field 905 * @throws IllegalArgumentException if <code>name</code> does not 906 * match the name of a serializable field for the class whose fields 907 * are being written, or if the type of the named field is not 908 * <code>char</code> 909 */ 910 public abstract void put(String name, char val); 911 912 /** 913 * Put the value of the named short field into the persistent field. 914 * 915 * @param name the name of the serializable field 916 * @param val the value to assign to the field 917 * @throws IllegalArgumentException if <code>name</code> does not 918 * match the name of a serializable field for the class whose fields 919 * are being written, or if the type of the named field is not 920 * <code>short</code> 921 */ 922 public abstract void put(String name, short val); 923 924 /** 925 * Put the value of the named int field into the persistent field. 926 * 927 * @param name the name of the serializable field 928 * @param val the value to assign to the field 929 * @throws IllegalArgumentException if <code>name</code> does not 930 * match the name of a serializable field for the class whose fields 931 * are being written, or if the type of the named field is not 932 * <code>int</code> 933 */ 934 public abstract void put(String name, int val); 935 936 /** 937 * Put the value of the named long field into the persistent field. 938 * 939 * @param name the name of the serializable field 940 * @param val the value to assign to the field 941 * @throws IllegalArgumentException if <code>name</code> does not 942 * match the name of a serializable field for the class whose fields 943 * are being written, or if the type of the named field is not 944 * <code>long</code> 945 */ 946 public abstract void put(String name, long val); 947 948 /** 949 * Put the value of the named float field into the persistent field. 950 * 951 * @param name the name of the serializable field 952 * @param val the value to assign to the field 953 * @throws IllegalArgumentException if <code>name</code> does not 954 * match the name of a serializable field for the class whose fields 955 * are being written, or if the type of the named field is not 956 * <code>float</code> 957 */ 958 public abstract void put(String name, float val); 959 960 /** 961 * Put the value of the named double field into the persistent field. 962 * 963 * @param name the name of the serializable field 964 * @param val the value to assign to the field 965 * @throws IllegalArgumentException if <code>name</code> does not 966 * match the name of a serializable field for the class whose fields 967 * are being written, or if the type of the named field is not 968 * <code>double</code> 969 */ 970 public abstract void put(String name, double val); 971 972 /** 973 * Put the value of the named Object field into the persistent field. 974 * 975 * @param name the name of the serializable field 976 * @param val the value to assign to the field 977 * (which may be <code>null</code>) 978 * @throws IllegalArgumentException if <code>name</code> does not 979 * match the name of a serializable field for the class whose fields 980 * are being written, or if the type of the named field is not a 981 * reference type 982 */ 983 public abstract void put(String name, Object val); 984 985 /** 986 * Write the data and fields to the specified ObjectOutput stream, 987 * which must be the same stream that produced this 988 * <code>PutField</code> object. 989 * 990 * @param out the stream to write the data and fields to 991 * @throws IOException if I/O errors occur while writing to the 992 * underlying stream 993 * @throws IllegalArgumentException if the specified stream is not 994 * the same stream that produced this <code>PutField</code> 995 * object 996 * @deprecated This method does not write the values contained by this 997 * <code>PutField</code> object in a proper format, and may 998 * result in corruption of the serialization stream. The 999 * correct way to write <code>PutField</code> data is by 1000 * calling the {@link java.io.ObjectOutputStream#writeFields()} 1001 * method. 1002 */ 1003 @Deprecated 1004 public abstract void write(ObjectOutput out) throws IOException; 1005 } 1006 1007 1008 /** 1009 * Returns protocol version in use. 1010 */ 1011 int getProtocolVersion() { 1012 return protocol; 1013 } 1014 1015 /** 1016 * Writes string without allowing it to be replaced in stream. Used by 1017 * ObjectStreamClass to write class descriptor type strings. 1018 */ 1019 void writeTypeString(String str) throws IOException { 1020 int handle; 1021 if (str == null) { 1022 writeNull(); 1023 } else if ((handle = handles.lookup(str)) != -1) { 1024 writeHandle(handle); 1025 } else { 1026 writeString(str, false); 1027 } 1028 } 1029 1030 /** 1031 * Verifies that this (possibly subclass) instance can be constructed 1032 * without violating security constraints: the subclass must not override 1033 * security-sensitive non-final methods, or else the 1034 * "enableSubclassImplementation" SerializablePermission is checked. 1035 */ 1036 private void verifySubclass() { 1037 Class cl = getClass(); 1038 if (cl == ObjectOutputStream.class) { 1039 return; 1040 } 1041 SecurityManager sm = System.getSecurityManager(); 1042 if (sm == null) { 1043 return; 1044 } 1045 processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits); 1046 WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue); 1047 Boolean result = Caches.subclassAudits.get(key); 1048 if (result == null) { 1049 result = Boolean.valueOf(auditSubclass(cl)); 1050 Caches.subclassAudits.putIfAbsent(key, result); 1051 } 1052 if (result.booleanValue()) { 1053 return; 1054 } 1055 sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); 1056 } 1057 1058 /** 1059 * Performs reflective checks on given subclass to verify that it doesn't 1060 * override security-sensitive non-final methods. Returns true if subclass 1061 * is "safe", false otherwise. 1062 */ 1063 private static boolean auditSubclass(final Class subcl) { 1064 Boolean result = AccessController.doPrivileged( 1065 new PrivilegedAction<Boolean>() { 1066 public Boolean run() { 1067 for (Class cl = subcl; 1068 cl != ObjectOutputStream.class; 1069 cl = cl.getSuperclass()) 1070 { 1071 try { 1072 cl.getDeclaredMethod( 1073 "writeUnshared", new Class[] { Object.class }); 1074 return Boolean.FALSE; 1075 } catch (NoSuchMethodException ex) { 1076 } 1077 try { 1078 cl.getDeclaredMethod("putFields", (Class[]) null); 1079 return Boolean.FALSE; 1080 } catch (NoSuchMethodException ex) { 1081 } 1082 } 1083 return Boolean.TRUE; 1084 } 1085 } 1086 ); 1087 return result.booleanValue(); 1088 } 1089 1090 /** 1091 * Clears internal data structures. 1092 */ 1093 private void clear() { 1094 subs.clear(); 1095 handles.clear(); 1096 } 1097 1098 /** 1099 * Underlying writeObject/writeUnshared implementation. 1100 */ 1101 private void writeObject0(Object obj, boolean unshared) 1102 throws IOException 1103 { 1104 boolean oldMode = bout.setBlockDataMode(false); 1105 depth++; 1106 try { 1107 // handle previously written and non-replaceable objects 1108 int h; 1109 if ((obj = subs.lookup(obj)) == null) { 1110 writeNull(); 1111 return; 1112 } else if (!unshared && (h = handles.lookup(obj)) != -1) { 1113 writeHandle(h); 1114 return; 1115 } else if (obj instanceof Class) { 1116 writeClass((Class) obj, unshared); 1117 return; 1118 } else if (obj instanceof ObjectStreamClass) { 1119 writeClassDesc((ObjectStreamClass) obj, unshared); 1120 return; 1121 } 1122 1123 // check for replacement object 1124 Object orig = obj; 1125 Class cl = obj.getClass(); 1126 ObjectStreamClass desc; 1127 for (;;) { 1128 // REMIND: skip this check for strings/arrays? 1129 Class repCl; 1130 desc = ObjectStreamClass.lookup(cl, true); 1131 if (!desc.hasWriteReplaceMethod() || 1132 (obj = desc.invokeWriteReplace(obj)) == null || 1133 (repCl = obj.getClass()) == cl) 1134 { 1135 break; 1136 } 1137 cl = repCl; 1138 } 1139 if (enableReplace) { 1140 Object rep = replaceObject(obj); 1141 if (rep != obj && rep != null) { 1142 cl = rep.getClass(); 1143 desc = ObjectStreamClass.lookup(cl, true); 1144 } 1145 obj = rep; 1146 } 1147 1148 // if object replaced, run through original checks a second time 1149 if (obj != orig) { 1150 subs.assign(orig, obj); 1151 if (obj == null) { 1152 writeNull(); 1153 return; 1154 } else if (!unshared && (h = handles.lookup(obj)) != -1) { 1155 writeHandle(h); 1156 return; 1157 } else if (obj instanceof Class) { 1158 writeClass((Class) obj, unshared); 1159 return; 1160 } else if (obj instanceof ObjectStreamClass) { 1161 writeClassDesc((ObjectStreamClass) obj, unshared); 1162 return; 1163 } 1164 } 1165 1166 // remaining cases 1167 if (obj instanceof String) { 1168 writeString((String) obj, unshared); 1169 } else if (cl.isArray()) { 1170 writeArray(obj, desc, unshared); 1171 } else if (obj instanceof Enum) { 1172 writeEnum((Enum) obj, desc, unshared); 1173 } else if (obj instanceof Serializable) { 1174 writeOrdinaryObject(obj, desc, unshared); 1175 } else { 1176 if (extendedDebugInfo) { 1177 throw new NotSerializableException( 1178 cl.getName() + "\n" + debugInfoStack.toString()); 1179 } else { 1180 throw new NotSerializableException(cl.getName()); 1181 } 1182 } 1183 } finally { 1184 depth--; 1185 bout.setBlockDataMode(oldMode); 1186 } 1187 } 1188 1189 /** 1190 * Writes null code to stream. 1191 */ 1192 private void writeNull() throws IOException { 1193 bout.writeByte(TC_NULL); 1194 } 1195 1196 /** 1197 * Writes given object handle to stream. 1198 */ 1199 private void writeHandle(int handle) throws IOException { 1200 bout.writeByte(TC_REFERENCE); 1201 bout.writeInt(baseWireHandle + handle); 1202 } 1203 1204 /** 1205 * Writes representation of given class to stream. 1206 */ 1207 private void writeClass(Class cl, boolean unshared) throws IOException { 1208 bout.writeByte(TC_CLASS); 1209 writeClassDesc(ObjectStreamClass.lookup(cl, true), false); 1210 handles.assign(unshared ? null : cl); 1211 } 1212 1213 /** 1214 * Writes representation of given class descriptor to stream. 1215 */ 1216 private void writeClassDesc(ObjectStreamClass desc, boolean unshared) 1217 throws IOException 1218 { 1219 int handle; 1220 if (desc == null) { 1221 writeNull(); 1222 } else if (!unshared && (handle = handles.lookup(desc)) != -1) { 1223 writeHandle(handle); 1224 } else if (desc.isProxy()) { 1225 writeProxyDesc(desc, unshared); 1226 } else { 1227 writeNonProxyDesc(desc, unshared); 1228 } 1229 } 1230 1231 /** 1232 * Writes class descriptor representing a dynamic proxy class to stream. 1233 */ 1234 private void writeProxyDesc(ObjectStreamClass desc, boolean unshared) 1235 throws IOException 1236 { 1237 bout.writeByte(TC_PROXYCLASSDESC); 1238 handles.assign(unshared ? null : desc); 1239 1240 Class cl = desc.forClass(); 1241 Class[] ifaces = cl.getInterfaces(); 1242 bout.writeInt(ifaces.length); 1243 for (int i = 0; i < ifaces.length; i++) { 1244 bout.writeUTF(ifaces[i].getName()); 1245 } 1246 1247 bout.setBlockDataMode(true); 1248 annotateProxyClass(cl); 1249 bout.setBlockDataMode(false); 1250 bout.writeByte(TC_ENDBLOCKDATA); 1251 1252 writeClassDesc(desc.getSuperDesc(), false); 1253 } 1254 1255 /** 1256 * Writes class descriptor representing a standard (i.e., not a dynamic 1257 * proxy) class to stream. 1258 */ 1259 private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared) 1260 throws IOException 1261 { 1262 bout.writeByte(TC_CLASSDESC); 1263 handles.assign(unshared ? null : desc); 1264 1265 if (protocol == PROTOCOL_VERSION_1) { 1266 // do not invoke class descriptor write hook with old protocol 1267 desc.writeNonProxy(this); 1268 } else { 1269 writeClassDescriptor(desc); 1270 } 1271 1272 Class cl = desc.forClass(); 1273 bout.setBlockDataMode(true); 1274 annotateClass(cl); 1275 bout.setBlockDataMode(false); 1276 bout.writeByte(TC_ENDBLOCKDATA); 1277 1278 writeClassDesc(desc.getSuperDesc(), false); 1279 } 1280 1281 /** 1282 * Writes given string to stream, using standard or long UTF format 1283 * depending on string length. 1284 */ 1285 private void writeString(String str, boolean unshared) throws IOException { 1286 handles.assign(unshared ? null : str); 1287 long utflen = bout.getUTFLength(str); 1288 if (utflen <= 0xFFFF) { 1289 bout.writeByte(TC_STRING); 1290 bout.writeUTF(str, utflen); 1291 } else { 1292 bout.writeByte(TC_LONGSTRING); 1293 bout.writeLongUTF(str, utflen); 1294 } 1295 } 1296 1297 /** 1298 * Writes given array object to stream. 1299 */ 1300 private void writeArray(Object array, 1301 ObjectStreamClass desc, 1302 boolean unshared) 1303 throws IOException 1304 { 1305 bout.writeByte(TC_ARRAY); 1306 writeClassDesc(desc, false); 1307 handles.assign(unshared ? null : array); 1308 1309 Class ccl = desc.forClass().getComponentType(); 1310 if (ccl.isPrimitive()) { 1311 if (ccl == Integer.TYPE) { 1312 int[] ia = (int[]) array; 1313 bout.writeInt(ia.length); 1314 bout.writeInts(ia, 0, ia.length); 1315 } else if (ccl == Byte.TYPE) { 1316 byte[] ba = (byte[]) array; 1317 bout.writeInt(ba.length); 1318 bout.write(ba, 0, ba.length, true); 1319 } else if (ccl == Long.TYPE) { 1320 long[] ja = (long[]) array; 1321 bout.writeInt(ja.length); 1322 bout.writeLongs(ja, 0, ja.length); 1323 } else if (ccl == Float.TYPE) { 1324 float[] fa = (float[]) array; 1325 bout.writeInt(fa.length); 1326 bout.writeFloats(fa, 0, fa.length); 1327 } else if (ccl == Double.TYPE) { 1328 double[] da = (double[]) array; 1329 bout.writeInt(da.length); 1330 bout.writeDoubles(da, 0, da.length); 1331 } else if (ccl == Short.TYPE) { 1332 short[] sa = (short[]) array; 1333 bout.writeInt(sa.length); 1334 bout.writeShorts(sa, 0, sa.length); 1335 } else if (ccl == Character.TYPE) { 1336 char[] ca = (char[]) array; 1337 bout.writeInt(ca.length); 1338 bout.writeChars(ca, 0, ca.length); 1339 } else if (ccl == Boolean.TYPE) { 1340 boolean[] za = (boolean[]) array; 1341 bout.writeInt(za.length); 1342 bout.writeBooleans(za, 0, za.length); 1343 } else { 1344 throw new InternalError(); 1345 } 1346 } else { 1347 Object[] objs = (Object[]) array; 1348 int len = objs.length; 1349 bout.writeInt(len); 1350 if (extendedDebugInfo) { 1351 debugInfoStack.push( 1352 "array (class \"" + array.getClass().getName() + 1353 "\", size: " + len + ")"); 1354 } 1355 try { 1356 for (int i = 0; i < len; i++) { 1357 if (extendedDebugInfo) { 1358 debugInfoStack.push( 1359 "element of array (index: " + i + ")"); 1360 } 1361 try { 1362 writeObject0(objs[i], false); 1363 } finally { 1364 if (extendedDebugInfo) { 1365 debugInfoStack.pop(); 1366 } 1367 } 1368 } 1369 } finally { 1370 if (extendedDebugInfo) { 1371 debugInfoStack.pop(); 1372 } 1373 } 1374 } 1375 } 1376 1377 /** 1378 * Writes given enum constant to stream. 1379 */ 1380 private void writeEnum(Enum en, 1381 ObjectStreamClass desc, 1382 boolean unshared) 1383 throws IOException 1384 { 1385 bout.writeByte(TC_ENUM); 1386 ObjectStreamClass sdesc = desc.getSuperDesc(); 1387 writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc, false); 1388 handles.assign(unshared ? null : en); 1389 writeString(en.name(), false); 1390 } 1391 1392 /** 1393 * Writes representation of a "ordinary" (i.e., not a String, Class, 1394 * ObjectStreamClass, array, or enum constant) serializable object to the 1395 * stream. 1396 */ 1397 private void writeOrdinaryObject(Object obj, 1398 ObjectStreamClass desc, 1399 boolean unshared) 1400 throws IOException 1401 { 1402 if (extendedDebugInfo) { 1403 debugInfoStack.push( 1404 (depth == 1 ? "root " : "") + "object (class \"" + 1405 obj.getClass().getName() + "\", " + obj.toString() + ")"); 1406 } 1407 try { 1408 desc.checkSerialize(); 1409 1410 bout.writeByte(TC_OBJECT); 1411 writeClassDesc(desc, false); 1412 handles.assign(unshared ? null : obj); 1413 if (desc.isExternalizable() && !desc.isProxy()) { 1414 writeExternalData((Externalizable) obj); 1415 } else { 1416 writeSerialData(obj, desc); 1417 } 1418 } finally { 1419 if (extendedDebugInfo) { 1420 debugInfoStack.pop(); 1421 } 1422 } 1423 } 1424 1425 /** 1426 * Writes externalizable data of given object by invoking its 1427 * writeExternal() method. 1428 */ 1429 private void writeExternalData(Externalizable obj) throws IOException { 1430 PutFieldImpl oldPut = curPut; 1431 curPut = null; 1432 1433 if (extendedDebugInfo) { 1434 debugInfoStack.push("writeExternal data"); 1435 } 1436 SerialCallbackContext oldContext = curContext; 1437 try { 1438 curContext = null; 1439 if (protocol == PROTOCOL_VERSION_1) { 1440 obj.writeExternal(this); 1441 } else { 1442 bout.setBlockDataMode(true); 1443 obj.writeExternal(this); 1444 bout.setBlockDataMode(false); 1445 bout.writeByte(TC_ENDBLOCKDATA); 1446 } 1447 } finally { 1448 curContext = oldContext; 1449 if (extendedDebugInfo) { 1450 debugInfoStack.pop(); 1451 } 1452 } 1453 1454 curPut = oldPut; 1455 } 1456 1457 /** 1458 * Writes instance data for each serializable class of given object, from 1459 * superclass to subclass. 1460 */ 1461 private void writeSerialData(Object obj, ObjectStreamClass desc) 1462 throws IOException 1463 { 1464 ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout(); 1465 for (int i = 0; i < slots.length; i++) { 1466 ObjectStreamClass slotDesc = slots[i].desc; 1467 if (slotDesc.hasWriteObjectMethod()) { 1468 PutFieldImpl oldPut = curPut; 1469 curPut = null; 1470 SerialCallbackContext oldContext = curContext; 1471 1472 if (extendedDebugInfo) { 1473 debugInfoStack.push( 1474 "custom writeObject data (class \"" + 1475 slotDesc.getName() + "\")"); 1476 } 1477 try { 1478 curContext = new SerialCallbackContext(obj, slotDesc); 1479 bout.setBlockDataMode(true); 1480 slotDesc.invokeWriteObject(obj, this); 1481 bout.setBlockDataMode(false); 1482 bout.writeByte(TC_ENDBLOCKDATA); 1483 } finally { 1484 curContext.setUsed(); 1485 curContext = oldContext; 1486 if (extendedDebugInfo) { 1487 debugInfoStack.pop(); 1488 } 1489 } 1490 1491 curPut = oldPut; 1492 } else { 1493 defaultWriteFields(obj, slotDesc); 1494 } 1495 } 1496 } 1497 1498 /** 1499 * Fetches and writes values of serializable fields of given object to 1500 * stream. The given class descriptor specifies which field values to 1501 * write, and in which order they should be written. 1502 */ 1503 private void defaultWriteFields(Object obj, ObjectStreamClass desc) 1504 throws IOException 1505 { 1506 // REMIND: perform conservative isInstance check here? 1507 desc.checkDefaultSerialize(); 1508 1509 int primDataSize = desc.getPrimDataSize(); 1510 if (primVals == null || primVals.length < primDataSize) { 1511 primVals = new byte[primDataSize]; 1512 } 1513 desc.getPrimFieldValues(obj, primVals); 1514 bout.write(primVals, 0, primDataSize, false); 1515 1516 ObjectStreamField[] fields = desc.getFields(false); 1517 Object[] objVals = new Object[desc.getNumObjFields()]; 1518 int numPrimFields = fields.length - objVals.length; 1519 desc.getObjFieldValues(obj, objVals); 1520 for (int i = 0; i < objVals.length; i++) { 1521 if (extendedDebugInfo) { 1522 debugInfoStack.push( 1523 "field (class \"" + desc.getName() + "\", name: \"" + 1524 fields[numPrimFields + i].getName() + "\", type: \"" + 1525 fields[numPrimFields + i].getType() + "\")"); 1526 } 1527 try { 1528 writeObject0(objVals[i], 1529 fields[numPrimFields + i].isUnshared()); 1530 } finally { 1531 if (extendedDebugInfo) { 1532 debugInfoStack.pop(); 1533 } 1534 } 1535 } 1536 } 1537 1538 /** 1539 * Attempts to write to stream fatal IOException that has caused 1540 * serialization to abort. 1541 */ 1542 private void writeFatalException(IOException ex) throws IOException { 1543 /* 1544 * Note: the serialization specification states that if a second 1545 * IOException occurs while attempting to serialize the original fatal 1546 * exception to the stream, then a StreamCorruptedException should be 1547 * thrown (section 2.1). However, due to a bug in previous 1548 * implementations of serialization, StreamCorruptedExceptions were 1549 * rarely (if ever) actually thrown--the "root" exceptions from 1550 * underlying streams were thrown instead. This historical behavior is 1551 * followed here for consistency. 1552 */ 1553 clear(); 1554 boolean oldMode = bout.setBlockDataMode(false); 1555 try { 1556 bout.writeByte(TC_EXCEPTION); 1557 writeObject0(ex, false); 1558 clear(); 1559 } finally { 1560 bout.setBlockDataMode(oldMode); 1561 } 1562 } 1563 1564 /** 1565 * Converts specified span of float values into byte values. 1566 */ 1567 // REMIND: remove once hotspot inlines Float.floatToIntBits 1568 private static native void floatsToBytes(float[] src, int srcpos, 1569 byte[] dst, int dstpos, 1570 int nfloats); 1571 1572 /** 1573 * Converts specified span of double values into byte values. 1574 */ 1575 // REMIND: remove once hotspot inlines Double.doubleToLongBits 1576 private static native void doublesToBytes(double[] src, int srcpos, 1577 byte[] dst, int dstpos, 1578 int ndoubles); 1579 1580 /** 1581 * Default PutField implementation. 1582 */ 1583 private class PutFieldImpl extends PutField { 1584 1585 /** class descriptor describing serializable fields */ 1586 private final ObjectStreamClass desc; 1587 /** primitive field values */ 1588 private final byte[] primVals; 1589 /** object field values */ 1590 private final Object[] objVals; 1591 1592 /** 1593 * Creates PutFieldImpl object for writing fields defined in given 1594 * class descriptor. 1595 */ 1596 PutFieldImpl(ObjectStreamClass desc) { 1597 this.desc = desc; 1598 primVals = new byte[desc.getPrimDataSize()]; 1599 objVals = new Object[desc.getNumObjFields()]; 1600 } 1601 1602 public void put(String name, boolean val) { 1603 Bits.putBoolean(primVals, getFieldOffset(name, Boolean.TYPE), val); 1604 } 1605 1606 public void put(String name, byte val) { 1607 primVals[getFieldOffset(name, Byte.TYPE)] = val; 1608 } 1609 1610 public void put(String name, char val) { 1611 Bits.putChar(primVals, getFieldOffset(name, Character.TYPE), val); 1612 } 1613 1614 public void put(String name, short val) { 1615 Bits.putShort(primVals, getFieldOffset(name, Short.TYPE), val); 1616 } 1617 1618 public void put(String name, int val) { 1619 Bits.putInt(primVals, getFieldOffset(name, Integer.TYPE), val); 1620 } 1621 1622 public void put(String name, float val) { 1623 Bits.putFloat(primVals, getFieldOffset(name, Float.TYPE), val); 1624 } 1625 1626 public void put(String name, long val) { 1627 Bits.putLong(primVals, getFieldOffset(name, Long.TYPE), val); 1628 } 1629 1630 public void put(String name, double val) { 1631 Bits.putDouble(primVals, getFieldOffset(name, Double.TYPE), val); 1632 } 1633 1634 public void put(String name, Object val) { 1635 objVals[getFieldOffset(name, Object.class)] = val; 1636 } 1637 1638 // deprecated in ObjectOutputStream.PutField 1639 public void write(ObjectOutput out) throws IOException { 1640 /* 1641 * Applications should *not* use this method to write PutField 1642 * data, as it will lead to stream corruption if the PutField 1643 * object writes any primitive data (since block data mode is not 1644 * unset/set properly, as is done in OOS.writeFields()). This 1645 * broken implementation is being retained solely for behavioral 1646 * compatibility, in order to support applications which use 1647 * OOS.PutField.write() for writing only non-primitive data. 1648 * 1649 * Serialization of unshared objects is not implemented here since 1650 * it is not necessary for backwards compatibility; also, unshared 1651 * semantics may not be supported by the given ObjectOutput 1652 * instance. Applications which write unshared objects using the 1653 * PutField API must use OOS.writeFields(). 1654 */ 1655 if (ObjectOutputStream.this != out) { 1656 throw new IllegalArgumentException("wrong stream"); 1657 } 1658 out.write(primVals, 0, primVals.length); 1659 1660 ObjectStreamField[] fields = desc.getFields(false); 1661 int numPrimFields = fields.length - objVals.length; 1662 // REMIND: warn if numPrimFields > 0? 1663 for (int i = 0; i < objVals.length; i++) { 1664 if (fields[numPrimFields + i].isUnshared()) { 1665 throw new IOException("cannot write unshared object"); 1666 } 1667 out.writeObject(objVals[i]); 1668 } 1669 } 1670 1671 /** 1672 * Writes buffered primitive data and object fields to stream. 1673 */ 1674 void writeFields() throws IOException { 1675 bout.write(primVals, 0, primVals.length, false); 1676 1677 ObjectStreamField[] fields = desc.getFields(false); 1678 int numPrimFields = fields.length - objVals.length; 1679 for (int i = 0; i < objVals.length; i++) { 1680 if (extendedDebugInfo) { 1681 debugInfoStack.push( 1682 "field (class \"" + desc.getName() + "\", name: \"" + 1683 fields[numPrimFields + i].getName() + "\", type: \"" + 1684 fields[numPrimFields + i].getType() + "\")"); 1685 } 1686 try { 1687 writeObject0(objVals[i], 1688 fields[numPrimFields + i].isUnshared()); 1689 } finally { 1690 if (extendedDebugInfo) { 1691 debugInfoStack.pop(); 1692 } 1693 } 1694 } 1695 } 1696 1697 /** 1698 * Returns offset of field with given name and type. A specified type 1699 * of null matches all types, Object.class matches all non-primitive 1700 * types, and any other non-null type matches assignable types only. 1701 * Throws IllegalArgumentException if no matching field found. 1702 */ 1703 private int getFieldOffset(String name, Class type) { 1704 ObjectStreamField field = desc.getField(name, type); 1705 if (field == null) { 1706 throw new IllegalArgumentException("no such field " + name + 1707 " with type " + type); 1708 } 1709 return field.getOffset(); 1710 } 1711 } 1712 1713 /** 1714 * Buffered output stream with two modes: in default mode, outputs data in 1715 * same format as DataOutputStream; in "block data" mode, outputs data 1716 * bracketed by block data markers (see object serialization specification 1717 * for details). 1718 */ 1719 private static class BlockDataOutputStream 1720 extends OutputStream implements DataOutput 1721 { 1722 /** maximum data block length */ 1723 private static final int MAX_BLOCK_SIZE = 1024; 1724 /** maximum data block header length */ 1725 private static final int MAX_HEADER_SIZE = 5; 1726 /** (tunable) length of char buffer (for writing strings) */ 1727 private static final int CHAR_BUF_SIZE = 256; 1728 1729 /** buffer for writing general/block data */ 1730 private final byte[] buf = new byte[MAX_BLOCK_SIZE]; 1731 /** buffer for writing block data headers */ 1732 private final byte[] hbuf = new byte[MAX_HEADER_SIZE]; 1733 /** char buffer for fast string writes */ 1734 private final char[] cbuf = new char[CHAR_BUF_SIZE]; 1735 1736 /** block data mode */ 1737 private boolean blkmode = false; 1738 /** current offset into buf */ 1739 private int pos = 0; 1740 1741 /** underlying output stream */ 1742 private final OutputStream out; 1743 /** loopback stream (for data writes that span data blocks) */ 1744 private final DataOutputStream dout; 1745 1746 /** 1747 * Creates new BlockDataOutputStream on top of given underlying stream. 1748 * Block data mode is turned off by default. 1749 */ 1750 BlockDataOutputStream(OutputStream out) { 1751 this.out = out; 1752 dout = new DataOutputStream(this); 1753 } 1754 1755 /** 1756 * Sets block data mode to the given mode (true == on, false == off) 1757 * and returns the previous mode value. If the new mode is the same as 1758 * the old mode, no action is taken. If the new mode differs from the 1759 * old mode, any buffered data is flushed before switching to the new 1760 * mode. 1761 */ 1762 boolean setBlockDataMode(boolean mode) throws IOException { 1763 if (blkmode == mode) { 1764 return blkmode; 1765 } 1766 drain(); 1767 blkmode = mode; 1768 return !blkmode; 1769 } 1770 1771 /** 1772 * Returns true if the stream is currently in block data mode, false 1773 * otherwise. 1774 */ 1775 boolean getBlockDataMode() { 1776 return blkmode; 1777 } 1778 1779 /* ----------------- generic output stream methods ----------------- */ 1780 /* 1781 * The following methods are equivalent to their counterparts in 1782 * OutputStream, except that they partition written data into data 1783 * blocks when in block data mode. 1784 */ 1785 1786 public void write(int b) throws IOException { 1787 if (pos >= MAX_BLOCK_SIZE) { 1788 drain(); 1789 } 1790 buf[pos++] = (byte) b; 1791 } 1792 1793 public void write(byte[] b) throws IOException { 1794 write(b, 0, b.length, false); 1795 } 1796 1797 public void write(byte[] b, int off, int len) throws IOException { 1798 write(b, off, len, false); 1799 } 1800 1801 public void flush() throws IOException { 1802 drain(); 1803 out.flush(); 1804 } 1805 1806 public void close() throws IOException { 1807 flush(); 1808 out.close(); 1809 } 1810 1811 /** 1812 * Writes specified span of byte values from given array. If copy is 1813 * true, copies the values to an intermediate buffer before writing 1814 * them to underlying stream (to avoid exposing a reference to the 1815 * original byte array). 1816 */ 1817 void write(byte[] b, int off, int len, boolean copy) 1818 throws IOException 1819 { 1820 if (!(copy || blkmode)) { // write directly 1821 drain(); 1822 out.write(b, off, len); 1823 return; 1824 } 1825 1826 while (len > 0) { 1827 if (pos >= MAX_BLOCK_SIZE) { 1828 drain(); 1829 } 1830 if (len >= MAX_BLOCK_SIZE && !copy && pos == 0) { 1831 // avoid unnecessary copy 1832 writeBlockHeader(MAX_BLOCK_SIZE); 1833 out.write(b, off, MAX_BLOCK_SIZE); 1834 off += MAX_BLOCK_SIZE; 1835 len -= MAX_BLOCK_SIZE; 1836 } else { 1837 int wlen = Math.min(len, MAX_BLOCK_SIZE - pos); 1838 System.arraycopy(b, off, buf, pos, wlen); 1839 pos += wlen; 1840 off += wlen; 1841 len -= wlen; 1842 } 1843 } 1844 } 1845 1846 /** 1847 * Writes all buffered data from this stream to the underlying stream, 1848 * but does not flush underlying stream. 1849 */ 1850 void drain() throws IOException { 1851 if (pos == 0) { 1852 return; 1853 } 1854 if (blkmode) { 1855 writeBlockHeader(pos); 1856 } 1857 out.write(buf, 0, pos); 1858 pos = 0; 1859 } 1860 1861 /** 1862 * Writes block data header. Data blocks shorter than 256 bytes are 1863 * prefixed with a 2-byte header; all others start with a 5-byte 1864 * header. 1865 */ 1866 private void writeBlockHeader(int len) throws IOException { 1867 if (len <= 0xFF) { 1868 hbuf[0] = TC_BLOCKDATA; 1869 hbuf[1] = (byte) len; 1870 out.write(hbuf, 0, 2); 1871 } else { 1872 hbuf[0] = TC_BLOCKDATALONG; 1873 Bits.putInt(hbuf, 1, len); 1874 out.write(hbuf, 0, 5); 1875 } 1876 } 1877 1878 1879 /* ----------------- primitive data output methods ----------------- */ 1880 /* 1881 * The following methods are equivalent to their counterparts in 1882 * DataOutputStream, except that they partition written data into data 1883 * blocks when in block data mode. 1884 */ 1885 1886 public void writeBoolean(boolean v) throws IOException { 1887 if (pos >= MAX_BLOCK_SIZE) { 1888 drain(); 1889 } 1890 Bits.putBoolean(buf, pos++, v); 1891 } 1892 1893 public void writeByte(int v) throws IOException { 1894 if (pos >= MAX_BLOCK_SIZE) { 1895 drain(); 1896 } 1897 buf[pos++] = (byte) v; 1898 } 1899 1900 public void writeChar(int v) throws IOException { 1901 if (pos + 2 <= MAX_BLOCK_SIZE) { 1902 Bits.putChar(buf, pos, (char) v); 1903 pos += 2; 1904 } else { 1905 dout.writeChar(v); 1906 } 1907 } 1908 1909 public void writeShort(int v) throws IOException { 1910 if (pos + 2 <= MAX_BLOCK_SIZE) { 1911 Bits.putShort(buf, pos, (short) v); 1912 pos += 2; 1913 } else { 1914 dout.writeShort(v); 1915 } 1916 } 1917 1918 public void writeInt(int v) throws IOException { 1919 if (pos + 4 <= MAX_BLOCK_SIZE) { 1920 Bits.putInt(buf, pos, v); 1921 pos += 4; 1922 } else { 1923 dout.writeInt(v); 1924 } 1925 } 1926 1927 public void writeFloat(float v) throws IOException { 1928 if (pos + 4 <= MAX_BLOCK_SIZE) { 1929 Bits.putFloat(buf, pos, v); 1930 pos += 4; 1931 } else { 1932 dout.writeFloat(v); 1933 } 1934 } 1935 1936 public void writeLong(long v) throws IOException { 1937 if (pos + 8 <= MAX_BLOCK_SIZE) { 1938 Bits.putLong(buf, pos, v); 1939 pos += 8; 1940 } else { 1941 dout.writeLong(v); 1942 } 1943 } 1944 1945 public void writeDouble(double v) throws IOException { 1946 if (pos + 8 <= MAX_BLOCK_SIZE) { 1947 Bits.putDouble(buf, pos, v); 1948 pos += 8; 1949 } else { 1950 dout.writeDouble(v); 1951 } 1952 } 1953 1954 public void writeBytes(String s) throws IOException { 1955 int endoff = s.length(); 1956 int cpos = 0; 1957 int csize = 0; 1958 for (int off = 0; off < endoff; ) { 1959 if (cpos >= csize) { 1960 cpos = 0; 1961 csize = Math.min(endoff - off, CHAR_BUF_SIZE); 1962 s.getChars(off, off + csize, cbuf, 0); 1963 } 1964 if (pos >= MAX_BLOCK_SIZE) { 1965 drain(); 1966 } 1967 int n = Math.min(csize - cpos, MAX_BLOCK_SIZE - pos); 1968 int stop = pos + n; 1969 while (pos < stop) { 1970 buf[pos++] = (byte) cbuf[cpos++]; 1971 } 1972 off += n; 1973 } 1974 } 1975 1976 public void writeChars(String s) throws IOException { 1977 int endoff = s.length(); 1978 for (int off = 0; off < endoff; ) { 1979 int csize = Math.min(endoff - off, CHAR_BUF_SIZE); 1980 s.getChars(off, off + csize, cbuf, 0); 1981 writeChars(cbuf, 0, csize); 1982 off += csize; 1983 } 1984 } 1985 1986 public void writeUTF(String s) throws IOException { 1987 writeUTF(s, getUTFLength(s)); 1988 } 1989 1990 1991 /* -------------- primitive data array output methods -------------- */ 1992 /* 1993 * The following methods write out spans of primitive data values. 1994 * Though equivalent to calling the corresponding primitive write 1995 * methods repeatedly, these methods are optimized for writing groups 1996 * of primitive data values more efficiently. 1997 */ 1998 1999 void writeBooleans(boolean[] v, int off, int len) throws IOException { 2000 int endoff = off + len; 2001 while (off < endoff) { 2002 if (pos >= MAX_BLOCK_SIZE) { 2003 drain(); 2004 } 2005 int stop = Math.min(endoff, off + (MAX_BLOCK_SIZE - pos)); 2006 while (off < stop) { 2007 Bits.putBoolean(buf, pos++, v[off++]); 2008 } 2009 } 2010 } 2011 2012 void writeChars(char[] v, int off, int len) throws IOException { 2013 int limit = MAX_BLOCK_SIZE - 2; 2014 int endoff = off + len; 2015 while (off < endoff) { 2016 if (pos <= limit) { 2017 int avail = (MAX_BLOCK_SIZE - pos) >> 1; 2018 int stop = Math.min(endoff, off + avail); 2019 while (off < stop) { 2020 Bits.putChar(buf, pos, v[off++]); 2021 pos += 2; 2022 } 2023 } else { 2024 dout.writeChar(v[off++]); 2025 } 2026 } 2027 } 2028 2029 void writeShorts(short[] v, int off, int len) throws IOException { 2030 int limit = MAX_BLOCK_SIZE - 2; 2031 int endoff = off + len; 2032 while (off < endoff) { 2033 if (pos <= limit) { 2034 int avail = (MAX_BLOCK_SIZE - pos) >> 1; 2035 int stop = Math.min(endoff, off + avail); 2036 while (off < stop) { 2037 Bits.putShort(buf, pos, v[off++]); 2038 pos += 2; 2039 } 2040 } else { 2041 dout.writeShort(v[off++]); 2042 } 2043 } 2044 } 2045 2046 void writeInts(int[] v, int off, int len) throws IOException { 2047 int limit = MAX_BLOCK_SIZE - 4; 2048 int endoff = off + len; 2049 while (off < endoff) { 2050 if (pos <= limit) { 2051 int avail = (MAX_BLOCK_SIZE - pos) >> 2; 2052 int stop = Math.min(endoff, off + avail); 2053 while (off < stop) { 2054 Bits.putInt(buf, pos, v[off++]); 2055 pos += 4; 2056 } 2057 } else { 2058 dout.writeInt(v[off++]); 2059 } 2060 } 2061 } 2062 2063 void writeFloats(float[] v, int off, int len) throws IOException { 2064 int limit = MAX_BLOCK_SIZE - 4; 2065 int endoff = off + len; 2066 while (off < endoff) { 2067 if (pos <= limit) { 2068 int avail = (MAX_BLOCK_SIZE - pos) >> 2; 2069 int chunklen = Math.min(endoff - off, avail); 2070 floatsToBytes(v, off, buf, pos, chunklen); 2071 off += chunklen; 2072 pos += chunklen << 2; 2073 } else { 2074 dout.writeFloat(v[off++]); 2075 } 2076 } 2077 } 2078 2079 void writeLongs(long[] v, int off, int len) throws IOException { 2080 int limit = MAX_BLOCK_SIZE - 8; 2081 int endoff = off + len; 2082 while (off < endoff) { 2083 if (pos <= limit) { 2084 int avail = (MAX_BLOCK_SIZE - pos) >> 3; 2085 int stop = Math.min(endoff, off + avail); 2086 while (off < stop) { 2087 Bits.putLong(buf, pos, v[off++]); 2088 pos += 8; 2089 } 2090 } else { 2091 dout.writeLong(v[off++]); 2092 } 2093 } 2094 } 2095 2096 void writeDoubles(double[] v, int off, int len) throws IOException { 2097 int limit = MAX_BLOCK_SIZE - 8; 2098 int endoff = off + len; 2099 while (off < endoff) { 2100 if (pos <= limit) { 2101 int avail = (MAX_BLOCK_SIZE - pos) >> 3; 2102 int chunklen = Math.min(endoff - off, avail); 2103 doublesToBytes(v, off, buf, pos, chunklen); 2104 off += chunklen; 2105 pos += chunklen << 3; 2106 } else { 2107 dout.writeDouble(v[off++]); 2108 } 2109 } 2110 } 2111 2112 /** 2113 * Returns the length in bytes of the UTF encoding of the given string. 2114 */ 2115 long getUTFLength(String s) { 2116 int len = s.length(); 2117 long utflen = 0; 2118 for (int off = 0; off < len; ) { 2119 int csize = Math.min(len - off, CHAR_BUF_SIZE); 2120 s.getChars(off, off + csize, cbuf, 0); 2121 for (int cpos = 0; cpos < csize; cpos++) { 2122 char c = cbuf[cpos]; 2123 if (c >= 0x0001 && c <= 0x007F) { 2124 utflen++; 2125 } else if (c > 0x07FF) { 2126 utflen += 3; 2127 } else { 2128 utflen += 2; 2129 } 2130 } 2131 off += csize; 2132 } 2133 return utflen; 2134 } 2135 2136 /** 2137 * Writes the given string in UTF format. This method is used in 2138 * situations where the UTF encoding length of the string is already 2139 * known; specifying it explicitly avoids a prescan of the string to 2140 * determine its UTF length. 2141 */ 2142 void writeUTF(String s, long utflen) throws IOException { 2143 if (utflen > 0xFFFFL) { 2144 throw new UTFDataFormatException(); 2145 } 2146 writeShort((int) utflen); 2147 if (utflen == (long) s.length()) { 2148 writeBytes(s); 2149 } else { 2150 writeUTFBody(s); 2151 } 2152 } 2153 2154 /** 2155 * Writes given string in "long" UTF format. "Long" UTF format is 2156 * identical to standard UTF, except that it uses an 8 byte header 2157 * (instead of the standard 2 bytes) to convey the UTF encoding length. 2158 */ 2159 void writeLongUTF(String s) throws IOException { 2160 writeLongUTF(s, getUTFLength(s)); 2161 } 2162 2163 /** 2164 * Writes given string in "long" UTF format, where the UTF encoding 2165 * length of the string is already known. 2166 */ 2167 void writeLongUTF(String s, long utflen) throws IOException { 2168 writeLong(utflen); 2169 if (utflen == (long) s.length()) { 2170 writeBytes(s); 2171 } else { 2172 writeUTFBody(s); 2173 } 2174 } 2175 2176 /** 2177 * Writes the "body" (i.e., the UTF representation minus the 2-byte or 2178 * 8-byte length header) of the UTF encoding for the given string. 2179 */ 2180 private void writeUTFBody(String s) throws IOException { 2181 int limit = MAX_BLOCK_SIZE - 3; 2182 int len = s.length(); 2183 for (int off = 0; off < len; ) { 2184 int csize = Math.min(len - off, CHAR_BUF_SIZE); 2185 s.getChars(off, off + csize, cbuf, 0); 2186 for (int cpos = 0; cpos < csize; cpos++) { 2187 char c = cbuf[cpos]; 2188 if (pos <= limit) { 2189 if (c <= 0x007F && c != 0) { 2190 buf[pos++] = (byte) c; 2191 } else if (c > 0x07FF) { 2192 buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F)); 2193 buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F)); 2194 buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F)); 2195 pos += 3; 2196 } else { 2197 buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F)); 2198 buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F)); 2199 pos += 2; 2200 } 2201 } else { // write one byte at a time to normalize block 2202 if (c <= 0x007F && c != 0) { 2203 write(c); 2204 } else if (c > 0x07FF) { 2205 write(0xE0 | ((c >> 12) & 0x0F)); 2206 write(0x80 | ((c >> 6) & 0x3F)); 2207 write(0x80 | ((c >> 0) & 0x3F)); 2208 } else { 2209 write(0xC0 | ((c >> 6) & 0x1F)); 2210 write(0x80 | ((c >> 0) & 0x3F)); 2211 } 2212 } 2213 } 2214 off += csize; 2215 } 2216 } 2217 } 2218 2219 /** 2220 * Lightweight identity hash table which maps objects to integer handles, 2221 * assigned in ascending order. 2222 */ 2223 private static class HandleTable { 2224 2225 /* number of mappings in table/next available handle */ 2226 private int size; 2227 /* size threshold determining when to expand hash spine */ 2228 private int threshold; 2229 /* factor for computing size threshold */ 2230 private final float loadFactor; 2231 /* maps hash value -> candidate handle value */ 2232 private int[] spine; 2233 /* maps handle value -> next candidate handle value */ 2234 private int[] next; 2235 /* maps handle value -> associated object */ 2236 private Object[] objs; 2237 2238 /** 2239 * Creates new HandleTable with given capacity and load factor. 2240 */ 2241 HandleTable(int initialCapacity, float loadFactor) { 2242 this.loadFactor = loadFactor; 2243 spine = new int[initialCapacity]; 2244 next = new int[initialCapacity]; 2245 objs = new Object[initialCapacity]; 2246 threshold = (int) (initialCapacity * loadFactor); 2247 clear(); 2248 } 2249 2250 /** 2251 * Assigns next available handle to given object, and returns handle 2252 * value. Handles are assigned in ascending order starting at 0. 2253 */ 2254 int assign(Object obj) { 2255 if (size >= next.length) { 2256 growEntries(); 2257 } 2258 if (size >= threshold) { 2259 growSpine(); 2260 } 2261 insert(obj, size); 2262 return size++; 2263 } 2264 2265 /** 2266 * Looks up and returns handle associated with given object, or -1 if 2267 * no mapping found. 2268 */ 2269 int lookup(Object obj) { 2270 if (size == 0) { 2271 return -1; 2272 } 2273 int index = hash(obj) % spine.length; 2274 for (int i = spine[index]; i >= 0; i = next[i]) { 2275 if (objs[i] == obj) { 2276 return i; 2277 } 2278 } 2279 return -1; 2280 } 2281 2282 /** 2283 * Resets table to its initial (empty) state. 2284 */ 2285 void clear() { 2286 Arrays.fill(spine, -1); 2287 Arrays.fill(objs, 0, size, null); 2288 size = 0; 2289 } 2290 2291 /** 2292 * Returns the number of mappings currently in table. 2293 */ 2294 int size() { 2295 return size; 2296 } 2297 2298 /** 2299 * Inserts mapping object -> handle mapping into table. Assumes table 2300 * is large enough to accommodate new mapping. 2301 */ 2302 private void insert(Object obj, int handle) { 2303 int index = hash(obj) % spine.length; 2304 objs[handle] = obj; 2305 next[handle] = spine[index]; 2306 spine[index] = handle; 2307 } 2308 2309 /** 2310 * Expands the hash "spine" -- equivalent to increasing the number of 2311 * buckets in a conventional hash table. 2312 */ 2313 private void growSpine() { 2314 spine = new int[(spine.length << 1) + 1]; 2315 threshold = (int) (spine.length * loadFactor); 2316 Arrays.fill(spine, -1); 2317 for (int i = 0; i < size; i++) { 2318 insert(objs[i], i); 2319 } 2320 } 2321 2322 /** 2323 * Increases hash table capacity by lengthening entry arrays. 2324 */ 2325 private void growEntries() { 2326 int newLength = (next.length << 1) + 1; 2327 int[] newNext = new int[newLength]; 2328 System.arraycopy(next, 0, newNext, 0, size); 2329 next = newNext; 2330 2331 Object[] newObjs = new Object[newLength]; 2332 System.arraycopy(objs, 0, newObjs, 0, size); 2333 objs = newObjs; 2334 } 2335 2336 /** 2337 * Returns hash value for given object. 2338 */ 2339 private int hash(Object obj) { 2340 return System.identityHashCode(obj) & 0x7FFFFFFF; 2341 } 2342 } 2343 2344 /** 2345 * Lightweight identity hash table which maps objects to replacement 2346 * objects. 2347 */ 2348 private static class ReplaceTable { 2349 2350 /* maps object -> index */ 2351 private final HandleTable htab; 2352 /* maps index -> replacement object */ 2353 private Object[] reps; 2354 2355 /** 2356 * Creates new ReplaceTable with given capacity and load factor. 2357 */ 2358 ReplaceTable(int initialCapacity, float loadFactor) { 2359 htab = new HandleTable(initialCapacity, loadFactor); 2360 reps = new Object[initialCapacity]; 2361 } 2362 2363 /** 2364 * Enters mapping from object to replacement object. 2365 */ 2366 void assign(Object obj, Object rep) { 2367 int index = htab.assign(obj); 2368 while (index >= reps.length) { 2369 grow(); 2370 } 2371 reps[index] = rep; 2372 } 2373 2374 /** 2375 * Looks up and returns replacement for given object. If no 2376 * replacement is found, returns the lookup object itself. 2377 */ 2378 Object lookup(Object obj) { 2379 int index = htab.lookup(obj); 2380 return (index >= 0) ? reps[index] : obj; 2381 } 2382 2383 /** 2384 * Resets table to its initial (empty) state. 2385 */ 2386 void clear() { 2387 Arrays.fill(reps, 0, htab.size(), null); 2388 htab.clear(); 2389 } 2390 2391 /** 2392 * Returns the number of mappings currently in table. 2393 */ 2394 int size() { 2395 return htab.size(); 2396 } 2397 2398 /** 2399 * Increases table capacity. 2400 */ 2401 private void grow() { 2402 Object[] newReps = new Object[(reps.length << 1) + 1]; 2403 System.arraycopy(reps, 0, newReps, 0, reps.length); 2404 reps = newReps; 2405 } 2406 } 2407 2408 /** 2409 * Stack to keep debug information about the state of the 2410 * serialization process, for embedding in exception messages. 2411 */ 2412 private static class DebugTraceInfoStack { 2413 private final List<String> stack; 2414 2415 DebugTraceInfoStack() { 2416 stack = new ArrayList<String>(); 2417 } 2418 2419 /** 2420 * Removes all of the elements from enclosed list. 2421 */ 2422 void clear() { 2423 stack.clear(); 2424 } 2425 2426 /** 2427 * Removes the object at the top of enclosed list. 2428 */ 2429 void pop() { 2430 stack.remove(stack.size()-1); 2431 } 2432 2433 /** 2434 * Pushes a String onto the top of enclosed list. 2435 */ 2436 void push(String entry) { 2437 stack.add("\t- " + entry); 2438 } 2439 2440 /** 2441 * Returns a string representation of this object 2442 */ 2443 public String toString() { 2444 StringBuilder buffer = new StringBuilder(); 2445 if (!stack.isEmpty()) { 2446 for(int i = stack.size(); i > 0; i-- ) { 2447 buffer.append(stack.get(i-1) + ((i != 1) ? "\n" : "")); 2448 } 2449 } 2450 return buffer.toString(); 2451 } 2452 } 2453 2454 }