1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package java.io; 19 20import java.lang.reflect.Field; 21import java.lang.reflect.InvocationTargetException; 22import java.lang.reflect.Method; 23import java.lang.reflect.Proxy; 24import java.nio.ByteOrder; 25import java.nio.charset.ModifiedUtf8; 26import java.util.List; 27import libcore.io.Memory; 28import libcore.io.SizeOf; 29 30/** 31 * A specialized {@link OutputStream} that is able to write (serialize) Java 32 * objects as well as primitive data types (int, byte, char etc.). The data can 33 * later be loaded using an ObjectInputStream. 34 * 35 * @see ObjectInputStream 36 * @see ObjectOutput 37 * @see Serializable 38 * @see Externalizable 39 */ 40public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants { 41 42 /* 43 * Mask to zero SC_BLOC_DATA bit. 44 */ 45 private static final byte NOT_SC_BLOCK_DATA = (byte) (SC_BLOCK_DATA ^ 0xFF); 46 47 /* 48 * How many nested levels to writeObject. 49 */ 50 private int nestedLevels; 51 52 /* 53 * Where we write to 54 */ 55 private DataOutputStream output; 56 57 /* 58 * If object replacement is enabled or not 59 */ 60 private boolean enableReplace; 61 62 /* 63 * Where we write primitive types to 64 */ 65 private DataOutputStream primitiveTypes; 66 67 /* 68 * Where the write primitive types are actually written to 69 */ 70 private ByteArrayOutputStream primitiveTypesBuffer; 71 72 /* 73 * Table mapping Object -> Integer (handle) 74 */ 75 private SerializationHandleMap objectsWritten; 76 77 /* 78 * All objects are assigned an ID (integer handle) 79 */ 80 private int currentHandle; 81 82 /* 83 * Used by defaultWriteObject 84 */ 85 private Object currentObject; 86 87 /* 88 * Used by defaultWriteObject 89 */ 90 private ObjectStreamClass currentClass; 91 92 /* 93 * Either ObjectStreamConstants.PROTOCOL_VERSION_1 or 94 * ObjectStreamConstants.PROTOCOL_VERSION_2 95 */ 96 private int protocolVersion; 97 98 /* 99 * Used to keep track of the PutField object for the class/object being 100 * written 101 */ 102 private EmulatedFieldsForDumping currentPutField; 103 104 /* 105 * Allows the receiver to decide if it needs to call writeObjectOverride 106 */ 107 private boolean subclassOverridingImplementation; 108 109 /* 110 * Descriptor for java.lang.reflect.Proxy 111 */ 112 private final ObjectStreamClass proxyClassDesc = ObjectStreamClass.lookup(Proxy.class); 113 114 /** 115 * PutField is an inner class to provide access to the persistent fields 116 * that are written to the target stream. 117 */ 118 public static abstract class PutField { 119 /** 120 * Puts the value of the boolean field identified by {@code name} to the 121 * persistent field. 122 * 123 * @param name 124 * the name of the field to serialize. 125 * @param value 126 * the value that is put to the persistent field. 127 */ 128 public abstract void put(String name, boolean value); 129 130 /** 131 * Puts the value of the character field identified by {@code name} to 132 * the persistent field. 133 * 134 * @param name 135 * the name of the field to serialize. 136 * @param value 137 * the value that is put to the persistent field. 138 */ 139 public abstract void put(String name, char value); 140 141 /** 142 * Puts the value of the byte field identified by {@code name} to the 143 * persistent field. 144 * 145 * @param name 146 * the name of the field to serialize. 147 * @param value 148 * the value that is put to the persistent field. 149 */ 150 public abstract void put(String name, byte value); 151 152 /** 153 * Puts the value of the short field identified by {@code name} to the 154 * persistent field. 155 * 156 * @param name 157 * the name of the field to serialize. 158 * @param value 159 * the value that is put to the persistent field. 160 */ 161 public abstract void put(String name, short value); 162 163 /** 164 * Puts the value of the integer field identified by {@code name} to the 165 * persistent field. 166 * 167 * @param name 168 * the name of the field to serialize. 169 * @param value 170 * the value that is put to the persistent field. 171 */ 172 public abstract void put(String name, int value); 173 174 /** 175 * Puts the value of the long field identified by {@code name} to the 176 * persistent field. 177 * 178 * @param name 179 * the name of the field to serialize. 180 * @param value 181 * the value that is put to the persistent field. 182 */ 183 public abstract void put(String name, long value); 184 185 /** 186 * Puts the value of the float field identified by {@code name} to the 187 * persistent field. 188 * 189 * @param name 190 * the name of the field to serialize. 191 * @param value 192 * the value that is put to the persistent field. 193 */ 194 public abstract void put(String name, float value); 195 196 /** 197 * Puts the value of the double field identified by {@code name} to the 198 * persistent field. 199 * 200 * @param name 201 * the name of the field to serialize. 202 * @param value 203 * the value that is put to the persistent field. 204 */ 205 public abstract void put(String name, double value); 206 207 /** 208 * Puts the value of the Object field identified by {@code name} to the 209 * persistent field. 210 * 211 * @param name 212 * the name of the field to serialize. 213 * @param value 214 * the value that is put to the persistent field. 215 */ 216 public abstract void put(String name, Object value); 217 218 /** 219 * Writes the fields to the target stream {@code out}. 220 * 221 * @param out 222 * the target stream 223 * @throws IOException 224 * if an error occurs while writing to the target stream. 225 * @deprecated This method is unsafe and may corrupt the target stream. 226 * Use ObjectOutputStream#writeFields() instead. 227 */ 228 @Deprecated 229 public abstract void write(ObjectOutput out) throws IOException; 230 } 231 232 /** 233 * Constructs a new {@code ObjectOutputStream}. This default constructor can 234 * be used by subclasses that do not want to use the public constructor if 235 * it allocates unneeded data. 236 * 237 * @throws IOException 238 * if an error occurs when creating this stream. 239 */ 240 protected ObjectOutputStream() throws IOException { 241 /* 242 * WARNING - we should throw IOException if not called from a subclass 243 * according to the JavaDoc. Add the test. 244 */ 245 this.subclassOverridingImplementation = true; 246 } 247 248 /** 249 * Constructs a new ObjectOutputStream that writes to the OutputStream 250 * {@code output}. 251 * 252 * @param output 253 * the non-null OutputStream to filter writes on. 254 * 255 * @throws IOException 256 * if an error occurs while writing the object stream 257 * header 258 */ 259 public ObjectOutputStream(OutputStream output) throws IOException { 260 this.output = (output instanceof DataOutputStream) ? (DataOutputStream) output 261 : new DataOutputStream(output); 262 this.enableReplace = false; 263 this.protocolVersion = PROTOCOL_VERSION_2; 264 this.subclassOverridingImplementation = false; 265 266 resetState(); 267 // So write...() methods can be used by 268 // subclasses during writeStreamHeader() 269 primitiveTypes = this.output; 270 // Has to be done here according to the specification 271 writeStreamHeader(); 272 primitiveTypes = null; 273 } 274 275 /** 276 * Writes optional information for class {@code aClass} to the output 277 * stream. This optional data can be read when deserializing the class 278 * descriptor (ObjectStreamClass) for this class from an input stream. By 279 * default, no extra data is saved. 280 * 281 * @param aClass 282 * the class to annotate. 283 * @throws IOException 284 * if an error occurs while writing to the target stream. 285 * @see ObjectInputStream#resolveClass(ObjectStreamClass) 286 */ 287 protected void annotateClass(Class<?> aClass) throws IOException { 288 // By default no extra info is saved. Subclasses can override 289 } 290 291 /** 292 * Writes optional information for a proxy class to the target stream. This 293 * optional data can be read when deserializing the proxy class from an 294 * input stream. By default, no extra data is saved. 295 * 296 * @param aClass 297 * the proxy class to annotate. 298 * @throws IOException 299 * if an error occurs while writing to the target stream. 300 * @see ObjectInputStream#resolveProxyClass(String[]) 301 */ 302 protected void annotateProxyClass(Class<?> aClass) throws IOException { 303 // By default no extra info is saved. Subclasses can override 304 } 305 306 /** 307 * Do the necessary work to see if the receiver can be used to write 308 * primitive types like int, char, etc. 309 */ 310 private void checkWritePrimitiveTypes() { 311 if (primitiveTypes == null) { 312 // If we got here we have no Stream previously created 313 // WARNING - if the stream does not grow, this code is wrong 314 primitiveTypesBuffer = new ByteArrayOutputStream(128); 315 primitiveTypes = new DataOutputStream(primitiveTypesBuffer); 316 } 317 } 318 319 /** 320 * Closes this stream. Any buffered data is flushed. This implementation 321 * closes the target stream. 322 * 323 * @throws IOException 324 * if an error occurs while closing this stream. 325 */ 326 @Override 327 public void close() throws IOException { 328 // First flush what is needed (primitive data, etc) 329 flush(); 330 output.close(); 331 } 332 333 /** 334 * Computes the collection of emulated fields that users can manipulate to 335 * store a representation different than the one declared by the class of 336 * the object being dumped. 337 * 338 * @see #writeFields 339 * @see #writeFieldValues(EmulatedFieldsForDumping) 340 */ 341 private void computePutField() { 342 currentPutField = new EmulatedFieldsForDumping(this, currentClass); 343 } 344 345 /** 346 * Default method to write objects to this stream. Serializable fields 347 * defined in the object's class and superclasses are written to the output 348 * stream. 349 * 350 * @throws IOException 351 * if an error occurs while writing to the target stream. 352 * @throws NotActiveException 353 * if this method is not called from {@code writeObject()}. 354 * @see ObjectInputStream#defaultReadObject 355 */ 356 public void defaultWriteObject() throws IOException { 357 if (currentObject == null) { 358 throw new NotActiveException(); 359 } 360 writeFieldValues(currentObject, currentClass); 361 } 362 363 /** 364 * Writes buffered data to the target stream. This is similar to {@code 365 * flush} but the flush is not propagated to the target stream. 366 * 367 * @throws IOException 368 * if an error occurs while writing to the target stream. 369 */ 370 protected void drain() throws IOException { 371 if (primitiveTypes == null || primitiveTypesBuffer == null) { 372 return; 373 } 374 375 // If we got here we have a Stream previously created 376 int offset = 0; 377 byte[] written = primitiveTypesBuffer.toByteArray(); 378 // Normalize the primitive data 379 while (offset < written.length) { 380 int toWrite = written.length - offset > 1024 ? 1024 381 : written.length - offset; 382 if (toWrite < 256) { 383 output.writeByte(TC_BLOCKDATA); 384 output.writeByte((byte) toWrite); 385 } else { 386 output.writeByte(TC_BLOCKDATALONG); 387 output.writeInt(toWrite); 388 } 389 390 // write primitive types we had and the marker of end-of-buffer 391 output.write(written, offset, toWrite); 392 offset += toWrite; 393 } 394 395 // and now we're clean to a state where we can write an object 396 primitiveTypes = null; 397 primitiveTypesBuffer = null; 398 } 399 400 /** 401 * Dumps the parameter {@code obj} only if it is {@code null} 402 * or an object that has already been dumped previously. 403 * 404 * @param obj 405 * Object to check if an instance previously dumped by this 406 * stream. 407 * @return -1 if it is an instance which has not been dumped yet (and this 408 * method does nothing). The handle if {@code obj} is an 409 * instance which has been dumped already. 410 * 411 * @throws IOException 412 * If an error occurs attempting to save {@code null} or 413 * a cyclic reference. 414 */ 415 private int dumpCycle(Object obj) throws IOException { 416 // If the object has been saved already, save its handle only 417 int handle = objectsWritten.get(obj); 418 if (handle != -1) { 419 writeCyclicReference(handle); 420 return handle; 421 } 422 return -1; 423 } 424 425 /** 426 * Enables object replacement for this stream. By default this is not 427 * enabled. Only trusted subclasses (loaded with system class loader) are 428 * allowed to change this status. 429 * 430 * @param enable 431 * {@code true} to enable object replacement; {@code false} to 432 * disable it. 433 * @return the previous setting. 434 * @see #replaceObject 435 * @see ObjectInputStream#enableResolveObject 436 */ 437 protected boolean enableReplaceObject(boolean enable) { 438 boolean originalValue = enableReplace; 439 enableReplace = enable; 440 return originalValue; 441 } 442 443 /** 444 * Writes buffered data to the target stream and calls the {@code flush} 445 * method of the target stream. 446 * 447 * @throws IOException 448 * if an error occurs while writing to or flushing the output 449 * stream. 450 */ 451 @Override 452 public void flush() throws IOException { 453 drain(); 454 output.flush(); 455 } 456 457 /** 458 * Return the next handle to be used to indicate cyclic 459 * references being saved to the stream. 460 * 461 * @return the next handle to represent the next cyclic reference 462 */ 463 private int nextHandle() { 464 return currentHandle++; 465 } 466 467 /** 468 * Gets this stream's {@code PutField} object. This object provides access 469 * to the persistent fields that are eventually written to the output 470 * stream. It is used to transfer the values from the fields of the object 471 * that is currently being written to the persistent fields. 472 * 473 * @return the PutField object from which persistent fields can be accessed 474 * by name. 475 * @throws IOException 476 * if an I/O error occurs. 477 * @throws NotActiveException 478 * if this method is not called from {@code writeObject()}. 479 * @see ObjectInputStream#defaultReadObject 480 */ 481 public PutField putFields() throws IOException { 482 if (currentObject == null) { 483 throw new NotActiveException(); 484 } 485 if (currentPutField == null) { 486 computePutField(); 487 } 488 return currentPutField; 489 } 490 491 private int registerObjectWritten(Object obj) { 492 int handle = nextHandle(); 493 objectsWritten.put(obj, handle); 494 return handle; 495 } 496 497 /** 498 * Remove the unshared object from the table, and restore any previous 499 * handle. 500 * 501 * @param obj 502 * Non-null object being dumped. 503 * @param previousHandle 504 * The handle of the previous identical object dumped 505 */ 506 private void removeUnsharedReference(Object obj, int previousHandle) { 507 if (previousHandle != -1) { 508 objectsWritten.put(obj, previousHandle); 509 } else { 510 objectsWritten.remove(obj); 511 } 512 } 513 514 /** 515 * Allows trusted subclasses to substitute the specified original {@code 516 * object} with a new object. Object substitution has to be activated first 517 * with calling {@code enableReplaceObject(true)}. This implementation just 518 * returns {@code object}. 519 * 520 * @param object 521 * the original object for which a replacement may be defined. 522 * @return the replacement object for {@code object}. 523 * @throws IOException 524 * if any I/O error occurs while creating the replacement 525 * object. 526 * @see #enableReplaceObject 527 * @see ObjectInputStream#enableResolveObject 528 * @see ObjectInputStream#resolveObject 529 */ 530 protected Object replaceObject(Object object) throws IOException { 531 // By default no object replacement. Subclasses can override 532 return object; 533 } 534 535 /** 536 * Resets the state of this stream. A marker is written to the stream, so 537 * that the corresponding input stream will also perform a reset at the same 538 * point. Objects previously written are no longer remembered, so they will 539 * be written again (instead of a cyclical reference) if found in the object 540 * graph. 541 * 542 * @throws IOException 543 * if {@code reset()} is called during the serialization of an 544 * object. 545 */ 546 public void reset() throws IOException { 547 // First we flush what we have 548 drain(); 549 /* 550 * And dump a reset marker, so that the ObjectInputStream can reset 551 * itself at the same point 552 */ 553 output.writeByte(TC_RESET); 554 // Now we reset ourselves 555 resetState(); 556 } 557 558 /** 559 * Reset the collection of objects already dumped by the receiver. If the 560 * objects are found again in the object graph, the receiver will dump them 561 * again, instead of a handle (cyclic reference). 562 * 563 */ 564 private void resetSeenObjects() { 565 objectsWritten = new SerializationHandleMap(); 566 currentHandle = baseWireHandle; 567 } 568 569 /** 570 * Reset the receiver. The collection of objects already dumped by the 571 * receiver is reset, and internal structures are also reset so that the 572 * receiver knows it is in a fresh clean state. 573 * 574 */ 575 private void resetState() { 576 resetSeenObjects(); 577 nestedLevels = 0; 578 } 579 580 /** 581 * Sets the specified protocol version to be used by this stream. 582 * 583 * @param version 584 * the protocol version to be used. Use a {@code 585 * PROTOCOL_VERSION_x} constant from {@code 586 * java.io.ObjectStreamConstants}. 587 * @throws IllegalArgumentException 588 * if an invalid {@code version} is specified. 589 * @throws IOException 590 * if an I/O error occurs. 591 * @see ObjectStreamConstants#PROTOCOL_VERSION_1 592 * @see ObjectStreamConstants#PROTOCOL_VERSION_2 593 */ 594 public void useProtocolVersion(int version) throws IOException { 595 if (!objectsWritten.isEmpty()) { 596 throw new IllegalStateException("Cannot set protocol version when stream in use"); 597 } 598 if (version != ObjectStreamConstants.PROTOCOL_VERSION_1 599 && version != ObjectStreamConstants.PROTOCOL_VERSION_2) { 600 throw new IllegalArgumentException("Unknown protocol: " + version); 601 } 602 protocolVersion = version; 603 } 604 605 /** 606 * Writes {@code count} bytes from the byte array {@code buffer} starting at 607 * offset {@code index} to the target stream. Blocks until all bytes are 608 * written. 609 * 610 * @param buffer 611 * the buffer to write. 612 * @param offset 613 * the index of the first byte in {@code buffer} to write. 614 * @param length 615 * the number of bytes from {@code buffer} to write to the output 616 * stream. 617 * @throws IOException 618 * if an error occurs while writing to the target stream. 619 */ 620 @Override 621 public void write(byte[] buffer, int offset, int length) throws IOException { 622 checkWritePrimitiveTypes(); 623 primitiveTypes.write(buffer, offset, length); 624 } 625 626 /** 627 * Writes a single byte to the target stream. Only the least significant 628 * byte of the integer {@code value} is written to the stream. Blocks until 629 * the byte is actually written. 630 * 631 * @param value 632 * the byte to write. 633 * @throws IOException 634 * if an error occurs while writing to the target stream. 635 */ 636 @Override 637 public void write(int value) throws IOException { 638 checkWritePrimitiveTypes(); 639 primitiveTypes.write(value); 640 } 641 642 /** 643 * Writes a boolean to the target stream. 644 * 645 * @param value 646 * the boolean value to write to the target stream. 647 * @throws IOException 648 * if an error occurs while writing to the target stream. 649 */ 650 public void writeBoolean(boolean value) throws IOException { 651 checkWritePrimitiveTypes(); 652 primitiveTypes.writeBoolean(value); 653 } 654 655 /** 656 * Writes a byte (8 bit) to the target stream. 657 * 658 * @param value 659 * the byte to write to the target stream. 660 * @throws IOException 661 * if an error occurs while writing to the target stream. 662 */ 663 public void writeByte(int value) throws IOException { 664 checkWritePrimitiveTypes(); 665 primitiveTypes.writeByte(value); 666 } 667 668 /** 669 * Writes the string {@code value} as a sequence of bytes to the target 670 * stream. Only the least significant byte of each character in the string 671 * is written. 672 * 673 * @param value 674 * the string to write to the target stream. 675 * @throws IOException 676 * if an error occurs while writing to the target stream. 677 */ 678 public void writeBytes(String value) throws IOException { 679 checkWritePrimitiveTypes(); 680 primitiveTypes.writeBytes(value); 681 } 682 683 /** 684 * Writes a character (16 bit) to the target stream. 685 * 686 * @param value 687 * the character to write to the target stream. 688 * @throws IOException 689 * if an error occurs while writing to the target stream. 690 */ 691 public void writeChar(int value) throws IOException { 692 checkWritePrimitiveTypes(); 693 primitiveTypes.writeChar(value); 694 } 695 696 /** 697 * Writes the string {@code value} as a sequence of characters to the target 698 * stream. 699 * 700 * @param value 701 * the string to write to the target stream. 702 * @throws IOException 703 * if an error occurs while writing to the target stream. 704 */ 705 public void writeChars(String value) throws IOException { 706 checkWritePrimitiveTypes(); 707 primitiveTypes.writeChars(value); 708 } 709 710 /** 711 * Write a class descriptor {@code classDesc} (an 712 * {@code ObjectStreamClass}) to the stream. 713 * 714 * @param classDesc 715 * The class descriptor (an {@code ObjectStreamClass}) to 716 * be dumped 717 * @param unshared 718 * Write the object unshared 719 * @return the handle assigned to the class descriptor 720 * 721 * @throws IOException 722 * If an IO exception happened when writing the class 723 * descriptor. 724 */ 725 private int writeClassDesc(ObjectStreamClass classDesc, boolean unshared) throws IOException { 726 if (classDesc == null) { 727 writeNull(); 728 return -1; 729 } 730 int handle = -1; 731 if (!unshared) { 732 handle = dumpCycle(classDesc); 733 } 734 if (handle == -1) { 735 Class<?> classToWrite = classDesc.forClass(); 736 int previousHandle = -1; 737 if (unshared) { 738 previousHandle = objectsWritten.get(classDesc); 739 } 740 // If we got here, it is a new (non-null) classDesc that will have 741 // to be registered as well 742 handle = registerObjectWritten(classDesc); 743 744 if (classDesc.isProxy()) { 745 output.writeByte(TC_PROXYCLASSDESC); 746 Class<?>[] interfaces = classToWrite.getInterfaces(); 747 output.writeInt(interfaces.length); 748 for (int i = 0; i < interfaces.length; i++) { 749 output.writeUTF(interfaces[i].getName()); 750 } 751 annotateProxyClass(classToWrite); 752 output.writeByte(TC_ENDBLOCKDATA); 753 writeClassDesc(proxyClassDesc, false); 754 if (unshared) { 755 // remove reference to unshared object 756 removeUnsharedReference(classDesc, previousHandle); 757 } 758 return handle; 759 } 760 761 output.writeByte(TC_CLASSDESC); 762 if (protocolVersion == PROTOCOL_VERSION_1) { 763 writeNewClassDesc(classDesc); 764 } else { 765 // So write...() methods can be used by 766 // subclasses during writeClassDescriptor() 767 primitiveTypes = output; 768 writeClassDescriptor(classDesc); 769 primitiveTypes = null; 770 } 771 // Extra class info (optional) 772 annotateClass(classToWrite); 773 drain(); // flush primitive types in the annotation 774 output.writeByte(TC_ENDBLOCKDATA); 775 writeClassDesc(classDesc.getSuperclass(), unshared); 776 if (unshared) { 777 // remove reference to unshared object 778 removeUnsharedReference(classDesc, previousHandle); 779 } 780 } 781 return handle; 782 } 783 784 /** 785 * Writes a handle representing a cyclic reference (object previously 786 * dumped). 787 */ 788 private void writeCyclicReference(int handle) throws IOException { 789 output.writeByte(TC_REFERENCE); 790 output.writeInt(handle); 791 } 792 793 /** 794 * Writes a double (64 bit) to the target stream. 795 * 796 * @param value 797 * the double to write to the target stream. 798 * @throws IOException 799 * if an error occurs while writing to the target stream. 800 */ 801 public void writeDouble(double value) throws IOException { 802 checkWritePrimitiveTypes(); 803 primitiveTypes.writeDouble(value); 804 } 805 806 /** 807 * Writes a collection of field descriptors (name, type name, etc) for the 808 * class descriptor {@code classDesc} (an 809 * {@code ObjectStreamClass}) 810 * 811 * @param classDesc 812 * The class descriptor (an {@code ObjectStreamClass}) 813 * for which to write field information 814 * @param externalizable 815 * true if the descriptors are externalizable 816 * 817 * @throws IOException 818 * If an IO exception happened when writing the field 819 * descriptors. 820 * 821 * @see #writeObject(Object) 822 */ 823 private void writeFieldDescriptors(ObjectStreamClass classDesc, boolean externalizable) throws IOException { 824 Class<?> loadedClass = classDesc.forClass(); 825 ObjectStreamField[] fields = null; 826 int fieldCount = 0; 827 828 // The fields of String are not needed since Strings are treated as 829 // primitive types 830 if (!externalizable && loadedClass != ObjectStreamClass.STRINGCLASS) { 831 fields = classDesc.fields(); 832 fieldCount = fields.length; 833 } 834 835 // Field count 836 output.writeShort(fieldCount); 837 // Field names 838 for (int i = 0; i < fieldCount; i++) { 839 ObjectStreamField f = fields[i]; 840 boolean wasPrimitive = f.writeField(output); 841 if (!wasPrimitive) { 842 writeObject(f.getTypeString()); 843 } 844 } 845 } 846 847 /** 848 * Writes the fields of the object currently being written to the target 849 * stream. The field values are buffered in the currently active {@code 850 * PutField} object, which can be accessed by calling {@code putFields()}. 851 * 852 * @throws IOException 853 * if an error occurs while writing to the target stream. 854 * @throws NotActiveException 855 * if there are no fields to write to the target stream. 856 * @see #putFields 857 */ 858 public void writeFields() throws IOException { 859 // Has to have fields to write 860 if (currentPutField == null) { 861 throw new NotActiveException(); 862 } 863 writeFieldValues(currentPutField); 864 } 865 866 /** 867 * Writes a collection of field values for the emulated fields 868 * {@code emulatedFields} 869 * 870 * @param emulatedFields 871 * an {@code EmulatedFieldsForDumping}, concrete subclass 872 * of {@code PutField} 873 * 874 * @throws IOException 875 * If an IO exception happened when writing the field values. 876 * 877 * @see #writeFields 878 * @see #writeObject(Object) 879 */ 880 private void writeFieldValues(EmulatedFieldsForDumping emulatedFields) throws IOException { 881 // Access internal fields which we can set/get. Users can't do this. 882 EmulatedFields accessibleSimulatedFields = emulatedFields.emulatedFields(); 883 for (EmulatedFields.ObjectSlot slot : accessibleSimulatedFields.slots()) { 884 Object fieldValue = slot.getFieldValue(); 885 Class<?> type = slot.getField().getType(); 886 if (type == int.class) { 887 output.writeInt(fieldValue != null ? ((Integer) fieldValue).intValue() : 0); 888 } else if (type == byte.class) { 889 output.writeByte(fieldValue != null ? ((Byte) fieldValue).byteValue() : 0); 890 } else if (type == char.class) { 891 output.writeChar(fieldValue != null ? ((Character) fieldValue).charValue() : 0); 892 } else if (type == short.class) { 893 output.writeShort(fieldValue != null ? ((Short) fieldValue).shortValue() : 0); 894 } else if (type == boolean.class) { 895 output.writeBoolean(fieldValue != null ? ((Boolean) fieldValue).booleanValue() : false); 896 } else if (type == long.class) { 897 output.writeLong(fieldValue != null ? ((Long) fieldValue).longValue() : 0); 898 } else if (type == float.class) { 899 output.writeFloat(fieldValue != null ? ((Float) fieldValue).floatValue() : 0); 900 } else if (type == double.class) { 901 output.writeDouble(fieldValue != null ? ((Double) fieldValue).doubleValue() : 0); 902 } else { 903 // Either array or Object 904 writeObject(fieldValue); 905 } 906 } 907 } 908 909 /** 910 * Writes a collection of field values for the fields described by class 911 * descriptor {@code classDesc} (an {@code ObjectStreamClass}). 912 * This is the default mechanism, when emulated fields (an 913 * {@code PutField}) are not used. Actual values to dump are fetched 914 * directly from object {@code obj}. 915 * 916 * @param obj 917 * Instance from which to fetch field values to dump. 918 * @param classDesc 919 * A class descriptor (an {@code ObjectStreamClass}) 920 * defining which fields should be dumped. 921 * 922 * @throws IOException 923 * If an IO exception happened when writing the field values. 924 * 925 * @see #writeObject(Object) 926 */ 927 private void writeFieldValues(Object obj, ObjectStreamClass classDesc) throws IOException { 928 for (ObjectStreamField fieldDesc : classDesc.fields()) { 929 try { 930 Class<?> type = fieldDesc.getTypeInternal(); 931 Field field = classDesc.checkAndGetReflectionField(fieldDesc); 932 if (field == null) { 933 throw new InvalidClassException(classDesc.getName() 934 + " doesn't have a serializable field " + fieldDesc.getName() 935 + " of type " + type); 936 } 937 if (type == byte.class) { 938 output.writeByte(field.getByte(obj)); 939 } else if (type == char.class) { 940 output.writeChar(field.getChar(obj)); 941 } else if (type == double.class) { 942 output.writeDouble(field.getDouble(obj)); 943 } else if (type == float.class) { 944 output.writeFloat(field.getFloat(obj)); 945 } else if (type == int.class) { 946 output.writeInt(field.getInt(obj)); 947 } else if (type == long.class) { 948 output.writeLong(field.getLong(obj)); 949 } else if (type == short.class) { 950 output.writeShort(field.getShort(obj)); 951 } else if (type == boolean.class) { 952 output.writeBoolean(field.getBoolean(obj)); 953 } else { 954 // Reference types (including arrays). 955 Object objField = field.get(obj); 956 if (fieldDesc.isUnshared()) { 957 writeUnshared(objField); 958 } else { 959 writeObject(objField); 960 } 961 } 962 } catch (IllegalAccessException iae) { 963 // ObjectStreamField should have called setAccessible(true). 964 throw new AssertionError(iae); 965 } catch (NoSuchFieldError nsf) { 966 // The user defined serialPersistentFields but did not provide 967 // the glue to transfer values in writeObject, so we ended up using 968 // the default mechanism but failed to set the emulated field. 969 throw new InvalidClassException(classDesc.getName()); 970 } 971 } 972 } 973 974 /** 975 * Writes a float (32 bit) to the target stream. 976 * 977 * @param value 978 * the float to write to the target stream. 979 * @throws IOException 980 * if an error occurs while writing to the target stream. 981 */ 982 public void writeFloat(float value) throws IOException { 983 checkWritePrimitiveTypes(); 984 primitiveTypes.writeFloat(value); 985 } 986 987 /** 988 * Walks the hierarchy of classes described by class descriptor 989 * {@code classDesc} and writes the field values corresponding to 990 * fields declared by the corresponding class descriptor. The instance to 991 * fetch field values from is {@code object}. If the class 992 * (corresponding to class descriptor {@code classDesc}) defines 993 * private instance method {@code writeObject} it will be used to 994 * dump field values. 995 * 996 * @param object 997 * Instance from which to fetch field values to dump. 998 * @param classDesc 999 * A class descriptor (an {@code ObjectStreamClass}) 1000 * defining which fields should be dumped. 1001 * 1002 * @throws IOException 1003 * If an IO exception happened when writing the field values in 1004 * the hierarchy. 1005 * @throws NotActiveException 1006 * If the given object is not active 1007 * 1008 * @see #defaultWriteObject 1009 * @see #writeObject(Object) 1010 */ 1011 private void writeHierarchy(Object object, ObjectStreamClass classDesc) 1012 throws IOException, NotActiveException { 1013 if (object == null) { 1014 throw new NotActiveException(); 1015 } 1016 1017 // Fields are written from class closest to Object to leaf class 1018 // (down the chain) 1019 List<ObjectStreamClass> hierarchy = classDesc.getHierarchy(); 1020 for (int i = 0, end = hierarchy.size(); i < end; ++i) { 1021 ObjectStreamClass osc = hierarchy.get(i); 1022 // Have to do this before calling defaultWriteObject or anything 1023 // that calls defaultWriteObject 1024 currentObject = object; 1025 currentClass = osc; 1026 1027 // See if the object has a writeObject method. If so, run it 1028 try { 1029 boolean executed = false; 1030 if (osc.hasMethodWriteObject()) { 1031 final Method method = osc.getMethodWriteObject(); 1032 try { 1033 method.invoke(object, new Object[] { this }); 1034 executed = true; 1035 } catch (InvocationTargetException e) { 1036 Throwable ex = e.getTargetException(); 1037 if (ex instanceof RuntimeException) { 1038 throw (RuntimeException) ex; 1039 } else if (ex instanceof Error) { 1040 throw (Error) ex; 1041 } 1042 throw (IOException) ex; 1043 } catch (IllegalAccessException e) { 1044 throw new RuntimeException(e.toString()); 1045 } 1046 } 1047 1048 if (executed) { 1049 drain(); 1050 output.writeByte(TC_ENDBLOCKDATA); 1051 } else { 1052 // If the object did not have a writeMethod, call 1053 // defaultWriteObject 1054 defaultWriteObject(); 1055 } 1056 } finally { 1057 // Cleanup, needs to run always so that we can later detect 1058 // invalid calls to defaultWriteObject 1059 currentObject = null; 1060 currentClass = null; 1061 currentPutField = null; 1062 } 1063 } 1064 } 1065 1066 /** 1067 * Writes an integer (32 bit) to the target stream. 1068 * 1069 * @param value 1070 * the integer to write to the target stream. 1071 * @throws IOException 1072 * if an error occurs while writing to the target stream. 1073 */ 1074 public void writeInt(int value) throws IOException { 1075 checkWritePrimitiveTypes(); 1076 primitiveTypes.writeInt(value); 1077 } 1078 1079 /** 1080 * Writes a long (64 bit) to the target stream. 1081 * 1082 * @param value 1083 * the long to write to the target stream. 1084 * @throws IOException 1085 * if an error occurs while writing to the target stream. 1086 */ 1087 public void writeLong(long value) throws IOException { 1088 checkWritePrimitiveTypes(); 1089 primitiveTypes.writeLong(value); 1090 } 1091 1092 /** 1093 * Write array {@code array} of class {@code arrayClass} with 1094 * component type {@code componentType} into the receiver. It is 1095 * assumed the array has not been dumped yet. Returns 1096 * the handle for this object (array) which is dumped here. 1097 * 1098 * @param array 1099 * The array object to dump 1100 * @param arrayClass 1101 * A {@code java.lang.Class} representing the class of the 1102 * array 1103 * @param componentType 1104 * A {@code java.lang.Class} representing the array 1105 * component type 1106 * @return the handle assigned to the array 1107 * 1108 * @throws IOException 1109 * If an IO exception happened when writing the array. 1110 */ 1111 private int writeNewArray(Object array, Class<?> arrayClass, ObjectStreamClass arrayClDesc, 1112 Class<?> componentType, boolean unshared) throws IOException { 1113 output.writeByte(TC_ARRAY); 1114 writeClassDesc(arrayClDesc, false); 1115 1116 int handle = nextHandle(); 1117 if (!unshared) { 1118 objectsWritten.put(array, handle); 1119 } 1120 1121 // Now we have code duplication just because Java is typed. We have to 1122 // write N elements and assign to array positions, but we must typecast 1123 // the array first, and also call different methods depending on the 1124 // elements. 1125 1126 if (componentType.isPrimitive()) { 1127 if (componentType == int.class) { 1128 int[] intArray = (int[]) array; 1129 output.writeInt(intArray.length); 1130 for (int i = 0; i < intArray.length; i++) { 1131 output.writeInt(intArray[i]); 1132 } 1133 } else if (componentType == byte.class) { 1134 byte[] byteArray = (byte[]) array; 1135 output.writeInt(byteArray.length); 1136 output.write(byteArray, 0, byteArray.length); 1137 } else if (componentType == char.class) { 1138 char[] charArray = (char[]) array; 1139 output.writeInt(charArray.length); 1140 for (int i = 0; i < charArray.length; i++) { 1141 output.writeChar(charArray[i]); 1142 } 1143 } else if (componentType == short.class) { 1144 short[] shortArray = (short[]) array; 1145 output.writeInt(shortArray.length); 1146 for (int i = 0; i < shortArray.length; i++) { 1147 output.writeShort(shortArray[i]); 1148 } 1149 } else if (componentType == boolean.class) { 1150 boolean[] booleanArray = (boolean[]) array; 1151 output.writeInt(booleanArray.length); 1152 for (int i = 0; i < booleanArray.length; i++) { 1153 output.writeBoolean(booleanArray[i]); 1154 } 1155 } else if (componentType == long.class) { 1156 long[] longArray = (long[]) array; 1157 output.writeInt(longArray.length); 1158 for (int i = 0; i < longArray.length; i++) { 1159 output.writeLong(longArray[i]); 1160 } 1161 } else if (componentType == float.class) { 1162 float[] floatArray = (float[]) array; 1163 output.writeInt(floatArray.length); 1164 for (int i = 0; i < floatArray.length; i++) { 1165 output.writeFloat(floatArray[i]); 1166 } 1167 } else if (componentType == double.class) { 1168 double[] doubleArray = (double[]) array; 1169 output.writeInt(doubleArray.length); 1170 for (int i = 0; i < doubleArray.length; i++) { 1171 output.writeDouble(doubleArray[i]); 1172 } 1173 } else { 1174 throw new InvalidClassException("Wrong base type in " + arrayClass.getName()); 1175 } 1176 } else { 1177 // Array of Objects 1178 Object[] objectArray = (Object[]) array; 1179 output.writeInt(objectArray.length); 1180 for (int i = 0; i < objectArray.length; i++) { 1181 // TODO: This place is the opportunity for enhancement 1182 // We can implement writing elements through fast-path, 1183 // without setting up the context (see writeObject()) for 1184 // each element with public API 1185 writeObject(objectArray[i]); 1186 } 1187 } 1188 return handle; 1189 } 1190 1191 /** 1192 * Write class {@code object} into the receiver. It is assumed the 1193 * class has not been dumped yet. Classes are not really dumped, but a class 1194 * descriptor ({@code ObjectStreamClass}) that corresponds to them. 1195 * Returns the handle for this object (class) which is dumped here. 1196 * 1197 * @param object 1198 * The {@code java.lang.Class} object to dump 1199 * @return the handle assigned to the class being dumped 1200 * 1201 * @throws IOException 1202 * If an IO exception happened when writing the class. 1203 */ 1204 private int writeNewClass(Class<?> object, boolean unshared) throws IOException { 1205 output.writeByte(TC_CLASS); 1206 1207 // Instances of java.lang.Class are always Serializable, even if their 1208 // instances aren't (e.g. java.lang.Object.class). 1209 // We cannot call lookup because it returns null if the parameter 1210 // represents instances that cannot be serialized, and that is not what 1211 // we want. 1212 ObjectStreamClass clDesc = ObjectStreamClass.lookupStreamClass(object); 1213 1214 // The handle for the classDesc is NOT the handle for the class object 1215 // being dumped. We must allocate a new handle and return it. 1216 if (clDesc.isEnum()) { 1217 writeEnumDesc(clDesc, unshared); 1218 } else { 1219 writeClassDesc(clDesc, unshared); 1220 } 1221 1222 int handle = nextHandle(); 1223 if (!unshared) { 1224 objectsWritten.put(object, handle); 1225 } 1226 1227 return handle; 1228 } 1229 1230 /** 1231 * Write class descriptor {@code classDesc} into the receiver. It is 1232 * assumed the class descriptor has not been dumped yet. The class 1233 * descriptors for the superclass chain will be dumped as well. Returns 1234 * the handle for this object (class descriptor) which is dumped here. 1235 * 1236 * @param classDesc 1237 * The {@code ObjectStreamClass} object to dump 1238 * 1239 * @throws IOException 1240 * If an IO exception happened when writing the class 1241 * descriptor. 1242 */ 1243 private void writeNewClassDesc(ObjectStreamClass classDesc) 1244 throws IOException { 1245 output.writeUTF(classDesc.getName()); 1246 output.writeLong(classDesc.getSerialVersionUID()); 1247 byte flags = classDesc.getFlags(); 1248 1249 boolean externalizable = classDesc.isExternalizable(); 1250 1251 if (externalizable) { 1252 if (protocolVersion == PROTOCOL_VERSION_1) { 1253 flags &= NOT_SC_BLOCK_DATA; 1254 } else { 1255 // Change for 1.2. Objects can be saved in old format 1256 // (PROTOCOL_VERSION_1) or in the 1.2 format (PROTOCOL_VERSION_2). 1257 flags |= SC_BLOCK_DATA; 1258 } 1259 } 1260 output.writeByte(flags); 1261 if ((SC_ENUM | SC_SERIALIZABLE) != classDesc.getFlags()) { 1262 writeFieldDescriptors(classDesc, externalizable); 1263 } else { 1264 // enum write no fields 1265 output.writeShort(0); 1266 } 1267 } 1268 1269 /** 1270 * Writes a class descriptor to the target stream. 1271 * 1272 * @param classDesc 1273 * the class descriptor to write to the target stream. 1274 * @throws IOException 1275 * if an error occurs while writing to the target stream. 1276 */ 1277 protected void writeClassDescriptor(ObjectStreamClass classDesc) 1278 throws IOException { 1279 writeNewClassDesc(classDesc); 1280 } 1281 1282 /** 1283 * Write exception {@code ex} into the receiver. It is assumed the 1284 * exception has not been dumped yet. Returns 1285 * the handle for this object (exception) which is dumped here. 1286 * This is used to dump the exception instance that happened (if any) when 1287 * dumping the original object graph. The set of seen objects will be reset 1288 * just before and just after dumping this exception object. 1289 * 1290 * When exceptions are found normally in the object graph, they are dumped 1291 * as a regular object, and not by this method. In that case, the set of 1292 * "known objects" is not reset. 1293 * 1294 * @param ex 1295 * Exception object to dump 1296 * 1297 * @throws IOException 1298 * If an IO exception happened when writing the exception 1299 * object. 1300 */ 1301 private void writeNewException(Exception ex) throws IOException { 1302 output.writeByte(TC_EXCEPTION); 1303 resetSeenObjects(); 1304 writeObjectInternal(ex, false, false, false); // No replacements 1305 resetSeenObjects(); 1306 } 1307 1308 /** 1309 * Write object {@code object} of class {@code theClass} into 1310 * the receiver. It is assumed the object has not been dumped yet. 1311 * Return the handle for this object which 1312 * is dumped here. 1313 * 1314 * If the object implements {@code Externalizable} its 1315 * {@code writeExternal} is called. Otherwise, all fields described 1316 * by the class hierarchy is dumped. Each class can define how its declared 1317 * instance fields are dumped by defining a private method 1318 * {@code writeObject} 1319 * 1320 * @param object 1321 * The object to dump 1322 * @param theClass 1323 * A {@code java.lang.Class} representing the class of the 1324 * object 1325 * @param unshared 1326 * Write the object unshared 1327 * @return the handle assigned to the object 1328 * 1329 * @throws IOException 1330 * If an IO exception happened when writing the object. 1331 */ 1332 private int writeNewObject(Object object, Class<?> theClass, ObjectStreamClass clDesc, 1333 boolean unshared) throws IOException { 1334 // Not String, not null, not array, not cyclic reference 1335 1336 EmulatedFieldsForDumping originalCurrentPutField = currentPutField; // save 1337 currentPutField = null; // null it, to make sure one will be computed if 1338 // needed 1339 1340 boolean externalizable = clDesc.isExternalizable(); 1341 boolean serializable = clDesc.isSerializable(); 1342 if (!externalizable && !serializable) { 1343 // Object is neither externalizable nor serializable. Error 1344 throw new NotSerializableException(theClass.getName()); 1345 } 1346 1347 // Either serializable or externalizable, now we can save info 1348 output.writeByte(TC_OBJECT); 1349 writeClassDesc(clDesc, false); 1350 int previousHandle = -1; 1351 if (unshared) { 1352 previousHandle = objectsWritten.get(object); 1353 } 1354 1355 int handle = registerObjectWritten(object); 1356 1357 // This is how we know what to do in defaultWriteObject. And it is also 1358 // used by defaultWriteObject to check if it was called from an invalid 1359 // place. 1360 // It allows writeExternal to call defaultWriteObject and have it work. 1361 currentObject = object; 1362 currentClass = clDesc; 1363 try { 1364 if (externalizable) { 1365 boolean noBlockData = protocolVersion == PROTOCOL_VERSION_1; 1366 if (noBlockData) { 1367 primitiveTypes = output; 1368 } 1369 // Object is externalizable, just call its own method 1370 ((Externalizable) object).writeExternal(this); 1371 if (noBlockData) { 1372 primitiveTypes = null; 1373 } else { 1374 // Similar to the code in writeHierarchy when object 1375 // implements writeObject. 1376 // Any primitive data has to be flushed and a tag must be 1377 // written 1378 drain(); 1379 output.writeByte(TC_ENDBLOCKDATA); 1380 } 1381 } else { // If it got here, it has to be Serializable 1382 // Object is serializable. Walk the class chain writing the 1383 // fields 1384 writeHierarchy(object, currentClass); 1385 } 1386 } finally { 1387 // Cleanup, needs to run always so that we can later detect invalid 1388 // calls to defaultWriteObject 1389 if (unshared) { 1390 // remove reference to unshared object 1391 removeUnsharedReference(object, previousHandle); 1392 } 1393 currentObject = null; 1394 currentClass = null; 1395 currentPutField = originalCurrentPutField; 1396 } 1397 1398 return handle; 1399 } 1400 1401 /** 1402 * Write String {@code object} into the receiver. It is assumed the 1403 * String has not been dumped yet. Returns the handle for this object (String) which is dumped here. 1404 * Strings are saved encoded with {@link DataInput modified UTF-8}. 1405 * 1406 * @param object 1407 * the string to dump. 1408 * @return the handle assigned to the String being dumped 1409 * 1410 * @throws IOException 1411 * If an IO exception happened when writing the String. 1412 */ 1413 private int writeNewString(String object, boolean unshared) throws IOException { 1414 long count = ModifiedUtf8.countBytes(object, false); 1415 byte[] buffer; 1416 int offset = 0; 1417 if (count <= 0xffff) { 1418 buffer = new byte[1 + SizeOf.SHORT + (int) count]; 1419 buffer[offset++] = TC_STRING; 1420 Memory.pokeShort(buffer, offset, (short) count, ByteOrder.BIG_ENDIAN); 1421 offset += SizeOf.SHORT; 1422 } else { 1423 buffer = new byte[1 + SizeOf.LONG + (int) count]; 1424 buffer[offset++] = TC_LONGSTRING; 1425 Memory.pokeLong(buffer, offset, count, ByteOrder.BIG_ENDIAN); 1426 offset += SizeOf.LONG; 1427 } 1428 ModifiedUtf8.encode(buffer, offset, object); 1429 output.write(buffer, 0, buffer.length); 1430 1431 int handle = nextHandle(); 1432 if (!unshared) { 1433 objectsWritten.put(object, handle); 1434 } 1435 1436 return handle; 1437 } 1438 1439 /** 1440 * Write a special tag that indicates the value {@code null} into the 1441 * receiver. 1442 * 1443 * @throws IOException 1444 * If an IO exception happened when writing the tag for 1445 * {@code null}. 1446 */ 1447 private void writeNull() throws IOException { 1448 output.writeByte(TC_NULL); 1449 } 1450 1451 /** 1452 * Writes an object to the target stream. 1453 * 1454 * @param object 1455 * the object to write to the target stream. 1456 * @throws IOException 1457 * if an error occurs while writing to the target stream. 1458 * @see ObjectInputStream#readObject() 1459 */ 1460 public final void writeObject(Object object) throws IOException { 1461 writeObject(object, false); 1462 } 1463 1464 /** 1465 * Writes an unshared object to the target stream. This method is identical 1466 * to {@code writeObject}, except that it always writes a new object to the 1467 * stream versus the use of back-referencing for identical objects by 1468 * {@code writeObject}. 1469 * 1470 * @param object 1471 * the object to write to the target stream. 1472 * @throws IOException 1473 * if an error occurs while writing to the target stream. 1474 * @see ObjectInputStream#readUnshared() 1475 */ 1476 public void writeUnshared(Object object) throws IOException { 1477 writeObject(object, true); 1478 } 1479 1480 private void writeObject(Object object, boolean unshared) throws IOException { 1481 boolean setOutput = (primitiveTypes == output); 1482 if (setOutput) { 1483 primitiveTypes = null; 1484 } 1485 // This is the specified behavior in JDK 1.2. Very bizarre way to allow 1486 // behavior overriding. 1487 if (subclassOverridingImplementation && !unshared) { 1488 writeObjectOverride(object); 1489 return; 1490 } 1491 1492 try { 1493 // First we need to flush primitive types if they were written 1494 drain(); 1495 // Actual work, and class-based replacement should be computed 1496 // if needed. 1497 writeObjectInternal(object, unshared, true, true); 1498 if (setOutput) { 1499 primitiveTypes = output; 1500 } 1501 } catch (IOException ioEx1) { 1502 // This will make it pass through until the top caller. Only the top caller writes the 1503 // exception (where it can). 1504 if (nestedLevels == 0) { 1505 try { 1506 writeNewException(ioEx1); 1507 } catch (IOException ioEx2) { 1508 // If writing the exception to the output stream causes another exception there 1509 // is no need to propagate the second exception or generate a third exception, 1510 // both of which might obscure details of the root cause. 1511 } 1512 } 1513 throw ioEx1; // and then we propagate the original exception 1514 } 1515 } 1516 1517 /** 1518 * Write object {@code object} into the receiver's underlying stream. 1519 * 1520 * @param object 1521 * The object to write 1522 * @param unshared 1523 * Write the object unshared 1524 * @param computeClassBasedReplacement 1525 * A boolean indicating if class-based replacement should be 1526 * computed (if supported) for the object. 1527 * @param computeStreamReplacement 1528 * A boolean indicating if stream-based replacement should be 1529 * computed (if supported) for the object. 1530 * @return the handle assigned to the final object being dumped 1531 * 1532 * @throws IOException 1533 * If an IO exception happened when writing the object 1534 * 1535 * @see ObjectInputStream#readObject() 1536 */ 1537 private int writeObjectInternal(Object object, boolean unshared, 1538 boolean computeClassBasedReplacement, 1539 boolean computeStreamReplacement) throws IOException { 1540 1541 if (object == null) { 1542 writeNull(); 1543 return -1; 1544 } 1545 if (!unshared) { 1546 int handle = dumpCycle(object); 1547 if (handle != -1) { 1548 return handle; // cyclic reference 1549 } 1550 } 1551 1552 // Non-null object, first time seen... 1553 Class<?> objClass = object.getClass(); 1554 ObjectStreamClass clDesc = ObjectStreamClass.lookupStreamClass(objClass); 1555 1556 nestedLevels++; 1557 try { 1558 1559 if (!(enableReplace && computeStreamReplacement)) { 1560 // Is it a Class ? 1561 if (objClass == ObjectStreamClass.CLASSCLASS) { 1562 return writeNewClass((Class<?>) object, unshared); 1563 } 1564 // Is it an ObjectStreamClass ? 1565 if (objClass == ObjectStreamClass.OBJECTSTREAMCLASSCLASS) { 1566 return writeClassDesc((ObjectStreamClass) object, unshared); 1567 } 1568 } 1569 1570 if (clDesc.isSerializable() && computeClassBasedReplacement) { 1571 if (clDesc.hasMethodWriteReplace()){ 1572 Method methodWriteReplace = clDesc.getMethodWriteReplace(); 1573 Object replObj; 1574 try { 1575 replObj = methodWriteReplace.invoke(object, (Object[]) null); 1576 } catch (IllegalAccessException iae) { 1577 replObj = object; 1578 } catch (InvocationTargetException ite) { 1579 // WARNING - Not sure this is the right thing to do 1580 // if we can't run the method 1581 Throwable target = ite.getTargetException(); 1582 if (target instanceof ObjectStreamException) { 1583 throw (ObjectStreamException) target; 1584 } else if (target instanceof Error) { 1585 throw (Error) target; 1586 } else { 1587 throw (RuntimeException) target; 1588 } 1589 } 1590 if (replObj != object) { 1591 // All over, class-based replacement off this time. 1592 int replacementHandle = writeObjectInternal(replObj, false, false, 1593 computeStreamReplacement); 1594 // Make the original object also map to the same 1595 // handle. 1596 if (replacementHandle != -1) { 1597 objectsWritten.put(object, replacementHandle); 1598 } 1599 return replacementHandle; 1600 } 1601 } 1602 1603 } 1604 1605 // We get here either if class-based replacement was not needed or 1606 // if it was needed but produced the same object or if it could not 1607 // be computed. 1608 if (enableReplace && computeStreamReplacement) { 1609 // Now we compute the stream-defined replacement. 1610 Object streamReplacement = replaceObject(object); 1611 if (streamReplacement != object) { 1612 // All over, class-based replacement off this time. 1613 int replacementHandle = writeObjectInternal(streamReplacement, false, 1614 computeClassBasedReplacement, false); 1615 // Make the original object also map to the same handle. 1616 if (replacementHandle != -1) { 1617 objectsWritten.put(object, replacementHandle); 1618 } 1619 return replacementHandle; 1620 } 1621 } 1622 1623 // We get here if stream-based replacement produced the same object 1624 1625 // Is it a Class ? 1626 if (objClass == ObjectStreamClass.CLASSCLASS) { 1627 return writeNewClass((Class<?>) object, unshared); 1628 } 1629 1630 // Is it an ObjectStreamClass ? 1631 if (objClass == ObjectStreamClass.OBJECTSTREAMCLASSCLASS) { 1632 return writeClassDesc((ObjectStreamClass) object, unshared); 1633 } 1634 1635 // Is it a String ? (instanceof, but == is faster) 1636 if (objClass == ObjectStreamClass.STRINGCLASS) { 1637 return writeNewString((String) object, unshared); 1638 } 1639 1640 // Is it an Array ? 1641 if (objClass.isArray()) { 1642 return writeNewArray(object, objClass, clDesc, objClass 1643 .getComponentType(), unshared); 1644 } 1645 1646 if (object instanceof Enum) { 1647 return writeNewEnum(object, objClass, unshared); 1648 } 1649 1650 // Not a String or Class or Array. Default procedure. 1651 return writeNewObject(object, objClass, clDesc, unshared); 1652 } finally { 1653 nestedLevels--; 1654 } 1655 } 1656 1657 // write for Enum Class Desc only, which is different from other classes 1658 private ObjectStreamClass writeEnumDesc(ObjectStreamClass classDesc, boolean unshared) 1659 throws IOException { 1660 // write classDesc, classDesc for enum is different 1661 1662 // set flag for enum, the flag is (SC_SERIALIZABLE | SC_ENUM) 1663 classDesc.setFlags((byte) (SC_SERIALIZABLE | SC_ENUM)); 1664 int previousHandle = -1; 1665 if (unshared) { 1666 previousHandle = objectsWritten.get(classDesc); 1667 } 1668 int handle = -1; 1669 if (!unshared) { 1670 handle = dumpCycle(classDesc); 1671 } 1672 if (handle == -1) { 1673 Class<?> classToWrite = classDesc.forClass(); 1674 // If we got here, it is a new (non-null) classDesc that will have 1675 // to be registered as well 1676 registerObjectWritten(classDesc); 1677 1678 output.writeByte(TC_CLASSDESC); 1679 if (protocolVersion == PROTOCOL_VERSION_1) { 1680 writeNewClassDesc(classDesc); 1681 } else { 1682 // So write...() methods can be used by 1683 // subclasses during writeClassDescriptor() 1684 primitiveTypes = output; 1685 writeClassDescriptor(classDesc); 1686 primitiveTypes = null; 1687 } 1688 // Extra class info (optional) 1689 annotateClass(classToWrite); 1690 drain(); // flush primitive types in the annotation 1691 output.writeByte(TC_ENDBLOCKDATA); 1692 // write super class 1693 ObjectStreamClass superClassDesc = classDesc.getSuperclass(); 1694 if (superClassDesc != null) { 1695 // super class is also enum 1696 superClassDesc.setFlags((byte) (SC_SERIALIZABLE | SC_ENUM)); 1697 writeEnumDesc(superClassDesc, unshared); 1698 } else { 1699 output.writeByte(TC_NULL); 1700 } 1701 if (unshared) { 1702 // remove reference to unshared object 1703 removeUnsharedReference(classDesc, previousHandle); 1704 } 1705 } 1706 return classDesc; 1707 } 1708 1709 private int writeNewEnum(Object object, Class<?> theClass, boolean unshared) throws IOException { 1710 // write new Enum 1711 EmulatedFieldsForDumping originalCurrentPutField = currentPutField; // save 1712 // null it, to make sure one will be computed if needed 1713 currentPutField = null; 1714 1715 output.writeByte(TC_ENUM); 1716 while (theClass != null && !theClass.isEnum()) { 1717 // write enum only 1718 theClass = theClass.getSuperclass(); 1719 } 1720 ObjectStreamClass classDesc = ObjectStreamClass.lookup(theClass); 1721 writeEnumDesc(classDesc, unshared); 1722 1723 int previousHandle = -1; 1724 if (unshared) { 1725 previousHandle = objectsWritten.get(object); 1726 } 1727 int handle = registerObjectWritten(object); 1728 1729 ObjectStreamField[] fields = classDesc.getSuperclass().fields(); 1730 // Only write field "name" for enum class, which is the second field of 1731 // enum, that is fields[1]. Ignore all non-fields and fields.length < 2 1732 if (fields != null && fields.length > 1) { 1733 Field field = classDesc.getSuperclass().checkAndGetReflectionField(fields[1]); 1734 if (field == null) { 1735 throw new NoSuchFieldError(); 1736 } 1737 try { 1738 String str = (String) field.get(object); 1739 int strHandle = -1; 1740 if (!unshared) { 1741 strHandle = dumpCycle(str); 1742 } 1743 if (strHandle == -1) { 1744 writeNewString(str, unshared); 1745 } 1746 } catch (IllegalAccessException iae) { 1747 throw new AssertionError(iae); 1748 } 1749 } 1750 1751 if (unshared) { 1752 // remove reference to unshared object 1753 removeUnsharedReference(object, previousHandle); 1754 } 1755 currentPutField = originalCurrentPutField; 1756 return handle; 1757 } 1758 1759 /** 1760 * Method to be overridden by subclasses to write {@code object} to the 1761 * target stream. 1762 * 1763 * @param object 1764 * the object to write to the target stream. 1765 * @throws IOException 1766 * if an error occurs while writing to the target stream. 1767 */ 1768 protected void writeObjectOverride(Object object) throws IOException { 1769 if (!subclassOverridingImplementation) { 1770 // Subclasses must override. 1771 throw new IOException(); 1772 } 1773 } 1774 1775 /** 1776 * Writes a short (16 bit) to the target stream. 1777 * 1778 * @param value 1779 * the short to write to the target stream. 1780 * @throws IOException 1781 * if an error occurs while writing to the target stream. 1782 */ 1783 public void writeShort(int value) throws IOException { 1784 checkWritePrimitiveTypes(); 1785 primitiveTypes.writeShort(value); 1786 } 1787 1788 /** 1789 * Writes the {@link ObjectOutputStream} header to the target stream. 1790 * 1791 * @throws IOException 1792 * if an error occurs while writing to the target stream. 1793 */ 1794 protected void writeStreamHeader() throws IOException { 1795 output.writeShort(STREAM_MAGIC); 1796 output.writeShort(STREAM_VERSION); 1797 } 1798 1799 /** 1800 * Writes a string encoded with {@link DataInput modified UTF-8} to the 1801 * target stream. 1802 * 1803 * @param value 1804 * the string to write to the target stream. 1805 * @throws IOException 1806 * if an error occurs while writing to the target stream. 1807 */ 1808 public void writeUTF(String value) throws IOException { 1809 checkWritePrimitiveTypes(); 1810 primitiveTypes.writeUTF(value); 1811 } 1812} 1813