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