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