001 /* ObjectOutputStream.java -- Class used to write serialized objects 002 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 003 Free Software Foundation, Inc. 004 005 This file is part of GNU Classpath. 006 007 GNU Classpath is free software; you can redistribute it and/or modify 008 it under the terms of the GNU General Public License as published by 009 the Free Software Foundation; either version 2, or (at your option) 010 any later version. 011 012 GNU Classpath is distributed in the hope that it will be useful, but 013 WITHOUT ANY WARRANTY; without even the implied warranty of 014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 General Public License for more details. 016 017 You should have received a copy of the GNU General Public License 018 along with GNU Classpath; see the file COPYING. If not, write to the 019 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 020 02110-1301 USA. 021 022 Linking this library statically or dynamically with other modules is 023 making a combined work based on this library. Thus, the terms and 024 conditions of the GNU General Public License cover the whole 025 combination. 026 027 As a special exception, the copyright holders of this library give you 028 permission to link this library with independent modules to produce an 029 executable, regardless of the license terms of these independent 030 modules, and to copy and distribute the resulting executable under 031 terms of your choice, provided that you also meet, for each linked 032 independent module, the terms and conditions of the license of that 033 module. An independent module is a module which is not derived from 034 or based on this library. If you modify this library, you may extend 035 this exception to your version of the library, but you are not 036 obligated to do so. If you do not wish to do so, delete this 037 exception statement from your version. */ 038 039 040 package java.io; 041 042 import gnu.java.io.ObjectIdentityMap2Int; 043 import gnu.java.lang.reflect.TypeSignature; 044 import gnu.java.security.action.SetAccessibleAction; 045 046 import java.lang.reflect.Array; 047 import java.lang.reflect.Field; 048 import java.lang.reflect.InvocationTargetException; 049 import java.lang.reflect.Method; 050 051 052 /** 053 * An <code>ObjectOutputStream</code> can be used to write objects 054 * as well as primitive data in a platform-independent manner to an 055 * <code>OutputStream</code>. 056 * 057 * The data produced by an <code>ObjectOutputStream</code> can be read 058 * and reconstituted by an <code>ObjectInputStream</code>. 059 * 060 * <code>writeObject (Object)</code> is used to write Objects, the 061 * <code>write<type></code> methods are used to write primitive 062 * data (as in <code>DataOutputStream</code>). Strings can be written 063 * as objects or as primitive data. 064 * 065 * Not all objects can be written out using an 066 * <code>ObjectOutputStream</code>. Only those objects that are an 067 * instance of <code>java.io.Serializable</code> can be written. 068 * 069 * Using default serialization, information about the class of an 070 * object is written, all of the non-transient, non-static fields of 071 * the object are written, if any of these fields are objects, they are 072 * written out in the same manner. 073 * 074 * An object is only written out the first time it is encountered. If 075 * the object is encountered later, a reference to it is written to 076 * the underlying stream. Thus writing circular object graphs 077 * does not present a problem, nor are relationships between objects 078 * in a graph lost. 079 * 080 * Example usage: 081 * <pre> 082 * Hashtable map = new Hashtable (); 083 * map.put ("one", new Integer (1)); 084 * map.put ("two", new Integer (2)); 085 * 086 * ObjectOutputStream oos = 087 * new ObjectOutputStream (new FileOutputStream ("numbers")); 088 * oos.writeObject (map); 089 * oos.close (); 090 * 091 * ObjectInputStream ois = 092 * new ObjectInputStream (new FileInputStream ("numbers")); 093 * Hashtable newmap = (Hashtable)ois.readObject (); 094 * 095 * System.out.println (newmap); 096 * </pre> 097 * 098 * The default serialization can be overriden in two ways. 099 * 100 * By defining a method <code>private void 101 * writeObject (ObjectOutputStream)</code>, a class can dictate exactly 102 * how information about itself is written. 103 * <code>defaultWriteObject ()</code> may be called from this method to 104 * carry out default serialization. This method is not 105 * responsible for dealing with fields of super-classes or subclasses. 106 * 107 * By implementing <code>java.io.Externalizable</code>. This gives 108 * the class complete control over the way it is written to the 109 * stream. If this approach is used the burden of writing superclass 110 * and subclass data is transfered to the class implementing 111 * <code>java.io.Externalizable</code>. 112 * 113 * @see java.io.DataOutputStream 114 * @see java.io.Externalizable 115 * @see java.io.ObjectInputStream 116 * @see java.io.Serializable 117 * @author Tom Tromey (tromey@redhat.com) 118 * @author Jeroen Frijters (jeroen@frijters.net) 119 * @author Guilhem Lavaux (guilhem@kaffe.org) 120 * @author Michael Koch (konqueror@gmx.de) 121 * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 122 */ 123 public class ObjectOutputStream extends OutputStream 124 implements ObjectOutput, ObjectStreamConstants 125 { 126 /** 127 * Creates a new <code>ObjectOutputStream</code> that will do all of 128 * its writing onto <code>out</code>. This method also initializes 129 * the stream by writing the header information (stream magic number 130 * and stream version). 131 * 132 * @exception IOException Writing stream header to underlying 133 * stream cannot be completed. 134 * 135 * @see #writeStreamHeader() 136 */ 137 public ObjectOutputStream (OutputStream out) throws IOException 138 { 139 realOutput = new DataOutputStream(out); 140 blockData = new byte[ BUFFER_SIZE ]; 141 blockDataCount = 0; 142 blockDataOutput = new DataOutputStream(this); 143 setBlockDataMode(true); 144 replacementEnabled = false; 145 isSerializing = false; 146 nextOID = baseWireHandle; 147 OIDLookupTable = new ObjectIdentityMap2Int(); 148 protocolVersion = defaultProtocolVersion; 149 useSubclassMethod = false; 150 writeStreamHeader(); 151 152 if (DEBUG) 153 { 154 String val = System.getProperty("gcj.dumpobjects"); 155 if (val != null && !val.equals("")) 156 dump = true; 157 } 158 } 159 160 /** 161 * Writes a representation of <code>obj</code> to the underlying 162 * output stream by writing out information about its class, then 163 * writing out each of the objects non-transient, non-static 164 * fields. If any of these fields are other objects, 165 * they are written out in the same manner. 166 * 167 * This method can be overriden by a class by implementing 168 * <code>private void writeObject (ObjectOutputStream)</code>. 169 * 170 * If an exception is thrown from this method, the stream is left in 171 * an undefined state. 172 * 173 * @param obj the object to serialize. 174 * @exception NotSerializableException An attempt was made to 175 * serialize an <code>Object</code> that is not serializable. 176 * 177 * @exception InvalidClassException Somebody tried to serialize 178 * an object which is wrongly formatted. 179 * 180 * @exception IOException Exception from underlying 181 * <code>OutputStream</code>. 182 * @see #writeUnshared(Object) 183 */ 184 public final void writeObject(Object obj) throws IOException 185 { 186 writeObject(obj, true); 187 } 188 189 /** 190 * Writes an object to the stream in the same manner as 191 * {@link #writeObject(Object)}, but without the use of 192 * references. As a result, the object is always written 193 * to the stream in full. Likewise, if an object is written 194 * by this method and is then later written again by 195 * {@link #writeObject(Object)}, both calls will write out 196 * the object in full, as the later call to 197 * {@link #writeObject(Object)} will know nothing of the 198 * earlier use of {@link #writeUnshared(Object)}. 199 * 200 * @param obj the object to serialize. 201 * @throws NotSerializableException if the object being 202 * serialized does not implement 203 * {@link Serializable}. 204 * @throws InvalidClassException if a problem occurs with 205 * the class of the object being 206 * serialized. 207 * @throws IOException if an I/O error occurs on the underlying 208 * <code>OutputStream</code>. 209 * @since 1.4 210 * @see #writeObject(Object) 211 */ 212 public void writeUnshared(Object obj) 213 throws IOException 214 { 215 writeObject(obj, false); 216 } 217 218 /** 219 * Writes a representation of <code>obj</code> to the underlying 220 * output stream by writing out information about its class, then 221 * writing out each of the objects non-transient, non-static 222 * fields. If any of these fields are other objects, 223 * they are written out in the same manner. 224 * 225 * This method can be overriden by a class by implementing 226 * <code>private void writeObject (ObjectOutputStream)</code>. 227 * 228 * If an exception is thrown from this method, the stream is left in 229 * an undefined state. 230 * 231 * @param obj the object to serialize. 232 * @param shared true if the serialized object should be 233 * shared with later calls. 234 * @exception NotSerializableException An attempt was made to 235 * serialize an <code>Object</code> that is not serializable. 236 * 237 * @exception InvalidClassException Somebody tried to serialize 238 * an object which is wrongly formatted. 239 * 240 * @exception IOException Exception from underlying 241 * <code>OutputStream</code>. 242 * @see #writeUnshared(Object) 243 */ 244 private final void writeObject(Object obj, boolean shared) 245 throws IOException 246 { 247 if (useSubclassMethod) 248 { 249 if (dump) 250 dumpElementln ("WRITE OVERRIDE: " + obj); 251 252 writeObjectOverride(obj); 253 return; 254 } 255 256 if (dump) 257 dumpElementln ("WRITE: ", obj); 258 259 depth += 2; 260 261 boolean was_serializing = isSerializing; 262 boolean old_mode = setBlockDataMode(false); 263 try 264 { 265 isSerializing = true; 266 boolean replaceDone = false; 267 Object replacedObject = null; 268 269 while (true) 270 { 271 if (obj == null) 272 { 273 realOutput.writeByte(TC_NULL); 274 break; 275 } 276 277 int handle = findHandle(obj); 278 if (handle >= 0 && shared) 279 { 280 realOutput.writeByte(TC_REFERENCE); 281 realOutput.writeInt(handle); 282 break; 283 } 284 285 if (obj instanceof Class) 286 { 287 Class cl = (Class)obj; 288 ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(cl); 289 realOutput.writeByte(TC_CLASS); 290 if (!osc.isProxyClass) 291 { 292 writeObject (osc); 293 } 294 else 295 {System.err.println("1"); 296 realOutput.writeByte(TC_PROXYCLASSDESC); 297 Class[] intfs = cl.getInterfaces(); 298 realOutput.writeInt(intfs.length); 299 for (int i = 0; i < intfs.length; i++) 300 realOutput.writeUTF(intfs[i].getName()); 301 302 boolean oldmode = setBlockDataMode(true); 303 annotateProxyClass(cl); 304 setBlockDataMode(oldmode); 305 realOutput.writeByte(TC_ENDBLOCKDATA); 306 307 writeObject(osc.getSuper()); 308 } 309 if (shared) 310 assignNewHandle(obj); 311 break; 312 } 313 314 if (obj instanceof ObjectStreamClass) 315 { 316 writeClassDescriptor((ObjectStreamClass) obj); 317 break; 318 } 319 320 Class clazz = obj.getClass(); 321 ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject(clazz); 322 if (osc == null) 323 throw new NotSerializableException(clazz.getName()); 324 325 if (osc.isEnum()) 326 { 327 /* TC_ENUM classDesc newHandle enumConstantName */ 328 realOutput.writeByte(TC_ENUM); 329 writeObject(osc); 330 if (shared) 331 assignNewHandle(obj); 332 writeObject(((Enum) obj).name()); 333 break; 334 } 335 336 if ((replacementEnabled || obj instanceof Serializable) 337 && ! replaceDone) 338 { 339 replacedObject = obj; 340 341 if (obj instanceof Serializable) 342 { 343 try 344 { 345 Method m = osc.writeReplaceMethod; 346 if (m != null) 347 obj = m.invoke(obj, new Object[0]); 348 } 349 catch (IllegalAccessException ignore) 350 { 351 } 352 catch (InvocationTargetException ignore) 353 { 354 } 355 } 356 357 if (replacementEnabled) 358 obj = replaceObject(obj); 359 360 replaceDone = true; 361 continue; 362 } 363 364 if (obj instanceof String) 365 { 366 realOutput.writeByte(TC_STRING); 367 if (shared) 368 assignNewHandle(obj); 369 realOutput.writeUTF((String)obj); 370 break; 371 } 372 373 if (clazz.isArray ()) 374 { 375 realOutput.writeByte(TC_ARRAY); 376 writeObject(osc); 377 if (shared) 378 assignNewHandle(obj); 379 writeArraySizeAndElements(obj, clazz.getComponentType()); 380 break; 381 } 382 383 realOutput.writeByte(TC_OBJECT); 384 writeObject(osc); 385 386 if (shared) 387 if (replaceDone) 388 assignNewHandle(replacedObject); 389 else 390 assignNewHandle(obj); 391 392 if (obj instanceof Externalizable) 393 { 394 if (protocolVersion == PROTOCOL_VERSION_2) 395 setBlockDataMode(true); 396 397 ((Externalizable)obj).writeExternal(this); 398 399 if (protocolVersion == PROTOCOL_VERSION_2) 400 { 401 setBlockDataMode(false); 402 realOutput.writeByte(TC_ENDBLOCKDATA); 403 } 404 405 break; 406 } 407 408 if (obj instanceof Serializable) 409 { 410 Object prevObject = this.currentObject; 411 ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass; 412 currentObject = obj; 413 ObjectStreamClass[] hierarchy = osc.hierarchy(); 414 415 for (int i = 0; i < hierarchy.length; i++) 416 { 417 currentObjectStreamClass = hierarchy[i]; 418 419 fieldsAlreadyWritten = false; 420 if (currentObjectStreamClass.hasWriteMethod()) 421 { 422 if (dump) 423 dumpElementln ("WRITE METHOD CALLED FOR: ", obj); 424 setBlockDataMode(true); 425 callWriteMethod(obj, currentObjectStreamClass); 426 setBlockDataMode(false); 427 realOutput.writeByte(TC_ENDBLOCKDATA); 428 if (dump) 429 dumpElementln ("WRITE ENDBLOCKDATA FOR: ", obj); 430 } 431 else 432 { 433 if (dump) 434 dumpElementln ("WRITE FIELDS CALLED FOR: ", obj); 435 writeFields(obj, currentObjectStreamClass); 436 } 437 } 438 439 this.currentObject = prevObject; 440 this.currentObjectStreamClass = prevObjectStreamClass; 441 currentPutField = null; 442 break; 443 } 444 445 throw new NotSerializableException(clazz.getName() 446 + " in " 447 + obj.getClass()); 448 } // end pseudo-loop 449 } 450 catch (ObjectStreamException ose) 451 { 452 // Rethrow these are fatal. 453 throw ose; 454 } 455 catch (IOException e) 456 { 457 realOutput.writeByte(TC_EXCEPTION); 458 reset(true); 459 460 setBlockDataMode(false); 461 try 462 { 463 if (DEBUG) 464 { 465 e.printStackTrace(System.out); 466 } 467 writeObject(e); 468 } 469 catch (IOException ioe) 470 { 471 StreamCorruptedException ex = 472 new StreamCorruptedException 473 (ioe + " thrown while exception was being written to stream."); 474 if (DEBUG) 475 { 476 ex.printStackTrace(System.out); 477 } 478 throw ex; 479 } 480 481 reset (true); 482 483 } 484 finally 485 { 486 isSerializing = was_serializing; 487 setBlockDataMode(old_mode); 488 depth -= 2; 489 490 if (dump) 491 dumpElementln ("END: ", obj); 492 } 493 } 494 495 protected void writeClassDescriptor(ObjectStreamClass osc) throws IOException 496 { 497 if (osc.isProxyClass) 498 { 499 realOutput.writeByte(TC_PROXYCLASSDESC); 500 Class[] intfs = osc.forClass().getInterfaces(); 501 realOutput.writeInt(intfs.length); 502 for (int i = 0; i < intfs.length; i++) 503 realOutput.writeUTF(intfs[i].getName()); 504 505 assignNewHandle(osc); 506 507 boolean oldmode = setBlockDataMode(true); 508 annotateProxyClass(osc.forClass()); 509 setBlockDataMode(oldmode); 510 realOutput.writeByte(TC_ENDBLOCKDATA); 511 } 512 else 513 { 514 realOutput.writeByte(TC_CLASSDESC); 515 realOutput.writeUTF(osc.getName()); 516 if (osc.isEnum()) 517 realOutput.writeLong(0L); 518 else 519 realOutput.writeLong(osc.getSerialVersionUID()); 520 assignNewHandle(osc); 521 522 int flags = osc.getFlags(); 523 524 if (protocolVersion == PROTOCOL_VERSION_2 525 && osc.isExternalizable()) 526 flags |= SC_BLOCK_DATA; 527 528 realOutput.writeByte(flags); 529 530 ObjectStreamField[] fields = osc.fields; 531 532 if (fields == ObjectStreamClass.INVALID_FIELDS) 533 throw new InvalidClassException 534 (osc.getName(), "serialPersistentFields is invalid"); 535 536 realOutput.writeShort(fields.length); 537 538 ObjectStreamField field; 539 for (int i = 0; i < fields.length; i++) 540 { 541 field = fields[i]; 542 realOutput.writeByte(field.getTypeCode ()); 543 realOutput.writeUTF(field.getName ()); 544 545 if (! field.isPrimitive()) 546 writeObject(field.getTypeString()); 547 } 548 549 boolean oldmode = setBlockDataMode(true); 550 annotateClass(osc.forClass()); 551 setBlockDataMode(oldmode); 552 realOutput.writeByte(TC_ENDBLOCKDATA); 553 } 554 555 if (osc.isSerializable() || osc.isExternalizable()) 556 writeObject(osc.getSuper()); 557 else 558 writeObject(null); 559 } 560 561 /** 562 * Writes the current objects non-transient, non-static fields from 563 * the current class to the underlying output stream. 564 * 565 * This method is intended to be called from within a object's 566 * <code>private void writeObject (ObjectOutputStream)</code> 567 * method. 568 * 569 * @exception NotActiveException This method was called from a 570 * context other than from the current object's and current class's 571 * <code>private void writeObject (ObjectOutputStream)</code> 572 * method. 573 * 574 * @exception IOException Exception from underlying 575 * <code>OutputStream</code>. 576 */ 577 public void defaultWriteObject() 578 throws IOException, NotActiveException 579 { 580 markFieldsWritten(); 581 writeFields(currentObject, currentObjectStreamClass); 582 } 583 584 585 private void markFieldsWritten() throws IOException 586 { 587 if (currentObject == null || currentObjectStreamClass == null) 588 throw new NotActiveException 589 ("defaultWriteObject called by non-active class and/or object"); 590 591 if (fieldsAlreadyWritten) 592 throw new IOException 593 ("Only one of writeFields and defaultWriteObject may be called, and it may only be called once"); 594 595 fieldsAlreadyWritten = true; 596 } 597 598 /** 599 * Resets stream to state equivalent to the state just after it was 600 * constructed. 601 * 602 * Causes all objects previously written to the stream to be 603 * forgotten. A notification of this reset is also written to the 604 * underlying stream. 605 * 606 * @exception IOException Exception from underlying 607 * <code>OutputStream</code> or reset called while serialization is 608 * in progress. 609 */ 610 public void reset() throws IOException 611 { 612 reset(false); 613 } 614 615 616 private void reset(boolean internal) throws IOException 617 { 618 if (!internal) 619 { 620 if (isSerializing) 621 throw new IOException("Reset called while serialization in progress"); 622 623 realOutput.writeByte(TC_RESET); 624 } 625 626 clearHandles(); 627 } 628 629 630 /** 631 * Informs this <code>ObjectOutputStream</code> to write data 632 * according to the specified protocol. There are currently two 633 * different protocols, specified by <code>PROTOCOL_VERSION_1</code> 634 * and <code>PROTOCOL_VERSION_2</code>. This implementation writes 635 * data using <code>PROTOCOL_VERSION_2</code> by default, as is done 636 * since the JDK 1.2. 637 * <p> 638 * For an explanation of the differences between the two protocols 639 * see the Java Object Serialization Specification. 640 * </p> 641 * 642 * @param version the version to use. 643 * 644 * @throws IllegalArgumentException if <code>version</code> is not a valid 645 * protocol. 646 * @throws IllegalStateException if called after the first the first object 647 * was serialized. 648 * @throws IOException if an I/O error occurs. 649 * 650 * @see ObjectStreamConstants#PROTOCOL_VERSION_1 651 * @see ObjectStreamConstants#PROTOCOL_VERSION_2 652 * 653 * @since 1.2 654 */ 655 public void useProtocolVersion(int version) throws IOException 656 { 657 if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2) 658 throw new IllegalArgumentException("Invalid protocol version requested."); 659 660 if (nextOID != baseWireHandle) 661 throw new IllegalStateException("Protocol version cannot be changed " 662 + "after serialization started."); 663 664 protocolVersion = version; 665 } 666 667 /** 668 * An empty hook that allows subclasses to write extra information 669 * about classes to the stream. This method is called the first 670 * time each class is seen, and after all of the standard 671 * information about the class has been written. 672 * 673 * @exception IOException Exception from underlying 674 * <code>OutputStream</code>. 675 * 676 * @see ObjectInputStream#resolveClass(java.io.ObjectStreamClass) 677 */ 678 protected void annotateClass(Class<?> cl) throws IOException 679 { 680 } 681 682 protected void annotateProxyClass(Class<?> cl) throws IOException 683 { 684 } 685 686 /** 687 * Allows subclasses to replace objects that are written to the 688 * stream with other objects to be written in their place. This 689 * method is called the first time each object is encountered 690 * (modulo reseting of the stream). 691 * 692 * This method must be enabled before it will be called in the 693 * serialization process. 694 * 695 * @exception IOException Exception from underlying 696 * <code>OutputStream</code>. 697 * 698 * @see #enableReplaceObject(boolean) 699 */ 700 protected Object replaceObject(Object obj) throws IOException 701 { 702 return obj; 703 } 704 705 706 /** 707 * If <code>enable</code> is <code>true</code> and this object is 708 * trusted, then <code>replaceObject (Object)</code> will be called 709 * in subsequent calls to <code>writeObject (Object)</code>. 710 * Otherwise, <code>replaceObject (Object)</code> will not be called. 711 * 712 * @exception SecurityException This class is not trusted. 713 */ 714 protected boolean enableReplaceObject(boolean enable) 715 throws SecurityException 716 { 717 if (enable) 718 { 719 SecurityManager sm = System.getSecurityManager(); 720 if (sm != null) 721 sm.checkPermission(new SerializablePermission("enableSubstitution")); 722 } 723 724 boolean old_val = replacementEnabled; 725 replacementEnabled = enable; 726 return old_val; 727 } 728 729 730 /** 731 * Writes stream magic and stream version information to the 732 * underlying stream. 733 * 734 * @exception IOException Exception from underlying 735 * <code>OutputStream</code>. 736 */ 737 protected void writeStreamHeader() throws IOException 738 { 739 realOutput.writeShort(STREAM_MAGIC); 740 realOutput.writeShort(STREAM_VERSION); 741 } 742 743 /** 744 * Protected constructor that allows subclasses to override 745 * serialization. This constructor should be called by subclasses 746 * that wish to override <code>writeObject (Object)</code>. This 747 * method does a security check <i>NOTE: currently not 748 * implemented</i>, then sets a flag that informs 749 * <code>writeObject (Object)</code> to call the subclasses 750 * <code>writeObjectOverride (Object)</code> method. 751 * 752 * @see #writeObjectOverride(Object) 753 */ 754 protected ObjectOutputStream() throws IOException, SecurityException 755 { 756 SecurityManager sec_man = System.getSecurityManager (); 757 if (sec_man != null) 758 sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); 759 useSubclassMethod = true; 760 } 761 762 763 /** 764 * This method allows subclasses to override the default 765 * serialization mechanism provided by 766 * <code>ObjectOutputStream</code>. To make this method be used for 767 * writing objects, subclasses must invoke the 0-argument 768 * constructor on this class from there constructor. 769 * 770 * @see #ObjectOutputStream() 771 * 772 * @exception NotActiveException Subclass has arranged for this 773 * method to be called, but did not implement this method. 774 */ 775 protected void writeObjectOverride(Object obj) throws NotActiveException, 776 IOException 777 { 778 throw new NotActiveException 779 ("Subclass of ObjectOutputStream must implement writeObjectOverride"); 780 } 781 782 783 /** 784 * @see DataOutputStream#write(int) 785 */ 786 public void write (int data) throws IOException 787 { 788 if (writeDataAsBlocks) 789 { 790 if (blockDataCount == BUFFER_SIZE) 791 drain(); 792 793 blockData[ blockDataCount++ ] = (byte)data; 794 } 795 else 796 realOutput.write(data); 797 } 798 799 800 /** 801 * @see DataOutputStream#write(byte[]) 802 */ 803 public void write(byte[] b) throws IOException 804 { 805 write(b, 0, b.length); 806 } 807 808 809 /** 810 * @see DataOutputStream#write(byte[],int,int) 811 */ 812 public void write(byte[] b, int off, int len) throws IOException 813 { 814 if (writeDataAsBlocks) 815 { 816 if (len < 0) 817 throw new IndexOutOfBoundsException(); 818 819 if (blockDataCount + len < BUFFER_SIZE) 820 { 821 System.arraycopy(b, off, blockData, blockDataCount, len); 822 blockDataCount += len; 823 } 824 else 825 { 826 drain(); 827 writeBlockDataHeader(len); 828 realOutput.write(b, off, len); 829 } 830 } 831 else 832 realOutput.write(b, off, len); 833 } 834 835 836 /** 837 * @see DataOutputStream#flush() 838 */ 839 public void flush () throws IOException 840 { 841 drain(); 842 realOutput.flush(); 843 } 844 845 846 /** 847 * Causes the block-data buffer to be written to the underlying 848 * stream, but does not flush underlying stream. 849 * 850 * @exception IOException Exception from underlying 851 * <code>OutputStream</code>. 852 */ 853 protected void drain() throws IOException 854 { 855 if (blockDataCount == 0) 856 return; 857 858 if (writeDataAsBlocks) 859 writeBlockDataHeader(blockDataCount); 860 realOutput.write(blockData, 0, blockDataCount); 861 blockDataCount = 0; 862 } 863 864 865 /** 866 * @see java.io.DataOutputStream#close () 867 */ 868 public void close() throws IOException 869 { 870 flush(); 871 realOutput.close(); 872 } 873 874 875 /** 876 * @see java.io.DataOutputStream#writeBoolean (boolean) 877 */ 878 public void writeBoolean(boolean data) throws IOException 879 { 880 blockDataOutput.writeBoolean(data); 881 } 882 883 884 /** 885 * @see java.io.DataOutputStream#writeByte (int) 886 */ 887 public void writeByte(int data) throws IOException 888 { 889 blockDataOutput.writeByte(data); 890 } 891 892 893 /** 894 * @see java.io.DataOutputStream#writeShort (int) 895 */ 896 public void writeShort (int data) throws IOException 897 { 898 blockDataOutput.writeShort(data); 899 } 900 901 902 /** 903 * @see java.io.DataOutputStream#writeChar (int) 904 */ 905 public void writeChar(int data) throws IOException 906 { 907 blockDataOutput.writeChar(data); 908 } 909 910 911 /** 912 * @see java.io.DataOutputStream#writeInt (int) 913 */ 914 public void writeInt(int data) throws IOException 915 { 916 blockDataOutput.writeInt(data); 917 } 918 919 920 /** 921 * @see java.io.DataOutputStream#writeLong (long) 922 */ 923 public void writeLong(long data) throws IOException 924 { 925 blockDataOutput.writeLong(data); 926 } 927 928 929 /** 930 * @see java.io.DataOutputStream#writeFloat (float) 931 */ 932 public void writeFloat(float data) throws IOException 933 { 934 blockDataOutput.writeFloat(data); 935 } 936 937 938 /** 939 * @see java.io.DataOutputStream#writeDouble (double) 940 */ 941 public void writeDouble(double data) throws IOException 942 { 943 blockDataOutput.writeDouble(data); 944 } 945 946 947 /** 948 * @see java.io.DataOutputStream#writeBytes (java.lang.String) 949 */ 950 public void writeBytes(String data) throws IOException 951 { 952 blockDataOutput.writeBytes(data); 953 } 954 955 956 /** 957 * @see java.io.DataOutputStream#writeChars (java.lang.String) 958 */ 959 public void writeChars(String data) throws IOException 960 { 961 dataOutput.writeChars(data); 962 } 963 964 965 /** 966 * @see java.io.DataOutputStream#writeUTF (java.lang.String) 967 */ 968 public void writeUTF(String data) throws IOException 969 { 970 dataOutput.writeUTF(data); 971 } 972 973 974 /** 975 * This class allows a class to specify exactly which fields should 976 * be written, and what values should be written for these fields. 977 * 978 * XXX: finish up comments 979 */ 980 public abstract static class PutField 981 { 982 public abstract void put (String name, boolean value); 983 public abstract void put (String name, byte value); 984 public abstract void put (String name, char value); 985 public abstract void put (String name, double value); 986 public abstract void put (String name, float value); 987 public abstract void put (String name, int value); 988 public abstract void put (String name, long value); 989 public abstract void put (String name, short value); 990 public abstract void put (String name, Object value); 991 992 /** 993 * @deprecated 994 */ 995 public abstract void write (ObjectOutput out) throws IOException; 996 } 997 998 public PutField putFields() throws IOException 999 { 1000 if (currentPutField != null) 1001 return currentPutField; 1002 1003 currentPutField = new PutField() 1004 { 1005 private byte[] prim_field_data 1006 = new byte[currentObjectStreamClass.primFieldSize]; 1007 private Object[] objs 1008 = new Object[currentObjectStreamClass.objectFieldCount]; 1009 1010 private ObjectStreamField getField (String name) 1011 { 1012 ObjectStreamField field 1013 = currentObjectStreamClass.getField(name); 1014 1015 if (field == null) 1016 throw new IllegalArgumentException("no such serializable field " + name); 1017 1018 return field; 1019 } 1020 1021 public void put(String name, boolean value) 1022 { 1023 ObjectStreamField field = getField(name); 1024 1025 checkType(field, 'Z'); 1026 prim_field_data[field.getOffset ()] = (byte)(value ? 1 : 0); 1027 } 1028 1029 public void put(String name, byte value) 1030 { 1031 ObjectStreamField field = getField(name); 1032 1033 checkType(field, 'B'); 1034 prim_field_data[field.getOffset()] = value; 1035 } 1036 1037 public void put(String name, char value) 1038 { 1039 ObjectStreamField field = getField(name); 1040 1041 checkType(field, 'C'); 1042 int off = field.getOffset(); 1043 prim_field_data[off++] = (byte)(value >>> 8); 1044 prim_field_data[off] = (byte)value; 1045 } 1046 1047 public void put(String name, double value) 1048 { 1049 ObjectStreamField field = getField (name); 1050 1051 checkType(field, 'D'); 1052 int off = field.getOffset(); 1053 long l_value = Double.doubleToLongBits (value); 1054 prim_field_data[off++] = (byte)(l_value >>> 52); 1055 prim_field_data[off++] = (byte)(l_value >>> 48); 1056 prim_field_data[off++] = (byte)(l_value >>> 40); 1057 prim_field_data[off++] = (byte)(l_value >>> 32); 1058 prim_field_data[off++] = (byte)(l_value >>> 24); 1059 prim_field_data[off++] = (byte)(l_value >>> 16); 1060 prim_field_data[off++] = (byte)(l_value >>> 8); 1061 prim_field_data[off] = (byte)l_value; 1062 } 1063 1064 public void put(String name, float value) 1065 { 1066 ObjectStreamField field = getField(name); 1067 1068 checkType(field, 'F'); 1069 int off = field.getOffset(); 1070 int i_value = Float.floatToIntBits(value); 1071 prim_field_data[off++] = (byte)(i_value >>> 24); 1072 prim_field_data[off++] = (byte)(i_value >>> 16); 1073 prim_field_data[off++] = (byte)(i_value >>> 8); 1074 prim_field_data[off] = (byte)i_value; 1075 } 1076 1077 public void put(String name, int value) 1078 { 1079 ObjectStreamField field = getField(name); 1080 checkType(field, 'I'); 1081 int off = field.getOffset(); 1082 prim_field_data[off++] = (byte)(value >>> 24); 1083 prim_field_data[off++] = (byte)(value >>> 16); 1084 prim_field_data[off++] = (byte)(value >>> 8); 1085 prim_field_data[off] = (byte)value; 1086 } 1087 1088 public void put(String name, long value) 1089 { 1090 ObjectStreamField field = getField(name); 1091 checkType(field, 'J'); 1092 int off = field.getOffset(); 1093 prim_field_data[off++] = (byte)(value >>> 52); 1094 prim_field_data[off++] = (byte)(value >>> 48); 1095 prim_field_data[off++] = (byte)(value >>> 40); 1096 prim_field_data[off++] = (byte)(value >>> 32); 1097 prim_field_data[off++] = (byte)(value >>> 24); 1098 prim_field_data[off++] = (byte)(value >>> 16); 1099 prim_field_data[off++] = (byte)(value >>> 8); 1100 prim_field_data[off] = (byte)value; 1101 } 1102 1103 public void put(String name, short value) 1104 { 1105 ObjectStreamField field = getField(name); 1106 checkType(field, 'S'); 1107 int off = field.getOffset(); 1108 prim_field_data[off++] = (byte)(value >>> 8); 1109 prim_field_data[off] = (byte)value; 1110 } 1111 1112 public void put(String name, Object value) 1113 { 1114 ObjectStreamField field = getField(name); 1115 1116 if (value != null && 1117 ! field.getType().isAssignableFrom(value.getClass ())) 1118 throw new IllegalArgumentException("Class " + value.getClass() + 1119 " cannot be cast to " + field.getType()); 1120 objs[field.getOffset()] = value; 1121 } 1122 1123 public void write(ObjectOutput out) throws IOException 1124 { 1125 // Apparently Block data is not used with PutField as per 1126 // empirical evidence against JDK 1.2. Also see Mauve test 1127 // java.io.ObjectInputOutput.Test.GetPutField. 1128 boolean oldmode = setBlockDataMode(false); 1129 out.write(prim_field_data); 1130 for (int i = 0; i < objs.length; ++ i) 1131 out.writeObject(objs[i]); 1132 setBlockDataMode(oldmode); 1133 } 1134 1135 private void checkType(ObjectStreamField field, char type) 1136 throws IllegalArgumentException 1137 { 1138 if (TypeSignature.getEncodingOfClass(field.getType()).charAt(0) 1139 != type) 1140 throw new IllegalArgumentException(); 1141 } 1142 }; 1143 // end PutFieldImpl 1144 1145 return currentPutField; 1146 } 1147 1148 1149 public void writeFields() throws IOException 1150 { 1151 if (currentPutField == null) 1152 throw new NotActiveException("writeFields can only be called after putFields has been called"); 1153 1154 markFieldsWritten(); 1155 currentPutField.write(this); 1156 } 1157 1158 1159 // write out the block-data buffer, picking the correct header 1160 // depending on the size of the buffer 1161 private void writeBlockDataHeader(int size) throws IOException 1162 { 1163 if (size < 256) 1164 { 1165 realOutput.writeByte(TC_BLOCKDATA); 1166 realOutput.write(size); 1167 } 1168 else 1169 { 1170 realOutput.writeByte(TC_BLOCKDATALONG); 1171 realOutput.writeInt(size); 1172 } 1173 } 1174 1175 1176 // lookup the handle for OBJ, return null if OBJ doesn't have a 1177 // handle yet 1178 private int findHandle(Object obj) 1179 { 1180 return OIDLookupTable.get(obj); 1181 } 1182 1183 1184 // assigns the next availible handle to OBJ 1185 private int assignNewHandle(Object obj) 1186 { 1187 OIDLookupTable.put(obj, nextOID); 1188 return nextOID++; 1189 } 1190 1191 1192 // resets mapping from objects to handles 1193 private void clearHandles() 1194 { 1195 nextOID = baseWireHandle; 1196 OIDLookupTable.clear(); 1197 } 1198 1199 1200 // write out array size followed by each element of the array 1201 private void writeArraySizeAndElements(Object array, Class clazz) 1202 throws IOException 1203 { 1204 int length = Array.getLength(array); 1205 1206 if (clazz.isPrimitive()) 1207 { 1208 if (clazz == Boolean.TYPE) 1209 { 1210 boolean[] cast_array = (boolean[])array; 1211 realOutput.writeInt (length); 1212 for (int i = 0; i < length; i++) 1213 realOutput.writeBoolean(cast_array[i]); 1214 return; 1215 } 1216 if (clazz == Byte.TYPE) 1217 { 1218 byte[] cast_array = (byte[])array; 1219 realOutput.writeInt(length); 1220 realOutput.write(cast_array, 0, length); 1221 return; 1222 } 1223 if (clazz == Character.TYPE) 1224 { 1225 char[] cast_array = (char[])array; 1226 realOutput.writeInt(length); 1227 for (int i = 0; i < length; i++) 1228 realOutput.writeChar(cast_array[i]); 1229 return; 1230 } 1231 if (clazz == Double.TYPE) 1232 { 1233 double[] cast_array = (double[])array; 1234 realOutput.writeInt(length); 1235 for (int i = 0; i < length; i++) 1236 realOutput.writeDouble(cast_array[i]); 1237 return; 1238 } 1239 if (clazz == Float.TYPE) 1240 { 1241 float[] cast_array = (float[])array; 1242 realOutput.writeInt(length); 1243 for (int i = 0; i < length; i++) 1244 realOutput.writeFloat(cast_array[i]); 1245 return; 1246 } 1247 if (clazz == Integer.TYPE) 1248 { 1249 int[] cast_array = (int[])array; 1250 realOutput.writeInt(length); 1251 for (int i = 0; i < length; i++) 1252 realOutput.writeInt(cast_array[i]); 1253 return; 1254 } 1255 if (clazz == Long.TYPE) 1256 { 1257 long[] cast_array = (long[])array; 1258 realOutput.writeInt (length); 1259 for (int i = 0; i < length; i++) 1260 realOutput.writeLong(cast_array[i]); 1261 return; 1262 } 1263 if (clazz == Short.TYPE) 1264 { 1265 short[] cast_array = (short[])array; 1266 realOutput.writeInt (length); 1267 for (int i = 0; i < length; i++) 1268 realOutput.writeShort(cast_array[i]); 1269 return; 1270 } 1271 } 1272 else 1273 { 1274 Object[] cast_array = (Object[])array; 1275 realOutput.writeInt(length); 1276 for (int i = 0; i < length; i++) 1277 writeObject(cast_array[i]); 1278 } 1279 } 1280 1281 1282 /* GCJ LOCAL */ 1283 // writes out FIELDS of OBJECT for the specified ObjectStreamClass. 1284 // FIELDS are already supposed already to be in canonical order, but 1285 // under some circumstances (to do with Proxies) this isn't the 1286 // case, so we call ensureFieldsSet(). 1287 private void writeFields(Object obj, ObjectStreamClass osc) 1288 throws IOException 1289 { 1290 osc.ensureFieldsSet(osc.forClass()); 1291 /* END GCJ LOCAL */ 1292 1293 ObjectStreamField[] fields = osc.fields; 1294 boolean oldmode = setBlockDataMode(false); 1295 1296 try 1297 { 1298 writeFields(obj,fields); 1299 } 1300 catch (IllegalArgumentException _) 1301 { 1302 InvalidClassException e = new InvalidClassException 1303 ("writing fields of class " + osc.forClass().getName()); 1304 e.initCause(_); 1305 throw e; 1306 } 1307 catch (IOException e) 1308 { 1309 throw e; 1310 } 1311 catch (Exception _) 1312 { 1313 IOException e = new IOException("Unexpected exception " + _); 1314 e.initCause(_); 1315 throw(e); 1316 } 1317 1318 setBlockDataMode(oldmode); 1319 } 1320 1321 1322 /** 1323 * Helper function for writeFields(Object,ObjectStreamClass): write 1324 * fields from given fields array. Pass exception on. 1325 * 1326 * @param obj the object to be written 1327 * 1328 * @param fields the fields of obj to be written. 1329 */ 1330 private void writeFields(Object obj, ObjectStreamField[] fields) 1331 throws 1332 IllegalArgumentException, IllegalAccessException, IOException 1333 { 1334 for (int i = 0; i < fields.length; i++) 1335 { 1336 ObjectStreamField osf = fields[i]; 1337 Field field = osf.field; 1338 1339 if (DEBUG && dump) 1340 dumpElementln ("WRITE FIELD: " + osf.getName() + " type=" + osf.getType()); 1341 1342 switch (osf.getTypeCode()) 1343 { 1344 case 'Z': realOutput.writeBoolean(field.getBoolean(obj)); break; 1345 case 'B': realOutput.writeByte (field.getByte (obj)); break; 1346 case 'S': realOutput.writeShort (field.getShort (obj)); break; 1347 case 'C': realOutput.writeChar (field.getChar (obj)); break; 1348 case 'I': realOutput.writeInt (field.getInt (obj)); break; 1349 case 'F': realOutput.writeFloat (field.getFloat (obj)); break; 1350 case 'J': realOutput.writeLong (field.getLong (obj)); break; 1351 case 'D': realOutput.writeDouble (field.getDouble (obj)); break; 1352 case 'L': 1353 case '[': writeObject (field.get (obj)); break; 1354 default: 1355 throw new IOException("Unexpected type code " + osf.getTypeCode()); 1356 } 1357 } 1358 } 1359 1360 1361 // Toggles writing primitive data to block-data buffer. 1362 // Package-private to avoid a trampoline constructor. 1363 boolean setBlockDataMode(boolean on) throws IOException 1364 { 1365 if (on == writeDataAsBlocks) 1366 return on; 1367 1368 drain(); 1369 boolean oldmode = writeDataAsBlocks; 1370 writeDataAsBlocks = on; 1371 1372 if (on) 1373 dataOutput = blockDataOutput; 1374 else 1375 dataOutput = realOutput; 1376 1377 return oldmode; 1378 } 1379 1380 1381 private void callWriteMethod(Object obj, ObjectStreamClass osc) 1382 throws IOException 1383 { 1384 currentPutField = null; 1385 try 1386 { 1387 Object args[] = {this}; 1388 osc.writeObjectMethod.invoke(obj, args); 1389 } 1390 catch (InvocationTargetException x) 1391 { 1392 /* Rethrow if possible. */ 1393 Throwable exception = x.getTargetException(); 1394 if (exception instanceof RuntimeException) 1395 throw (RuntimeException) exception; 1396 if (exception instanceof IOException) 1397 throw (IOException) exception; 1398 1399 IOException ioe 1400 = new IOException("Exception thrown from writeObject() on " + 1401 osc.forClass().getName() + ": " + 1402 exception.getClass().getName()); 1403 ioe.initCause(exception); 1404 throw ioe; 1405 } 1406 catch (Exception x) 1407 { 1408 IOException ioe 1409 = new IOException("Failure invoking writeObject() on " + 1410 osc.forClass().getName() + ": " + 1411 x.getClass().getName()); 1412 ioe.initCause(x); 1413 throw ioe; 1414 } 1415 } 1416 1417 private void dumpElementln (String msg, Object obj) 1418 { 1419 try 1420 { 1421 for (int i = 0; i < depth; i++) 1422 System.out.print (" "); 1423 System.out.print (Thread.currentThread() + ": "); 1424 System.out.print (msg); 1425 if (java.lang.reflect.Proxy.isProxyClass(obj.getClass())) 1426 System.out.print (obj.getClass()); 1427 else 1428 System.out.print (obj); 1429 } 1430 catch (Exception _) 1431 { 1432 } 1433 finally 1434 { 1435 System.out.println (); 1436 } 1437 } 1438 1439 private void dumpElementln (String msg) 1440 { 1441 for (int i = 0; i < depth; i++) 1442 System.out.print (" "); 1443 System.out.print (Thread.currentThread() + ": "); 1444 System.out.println(msg); 1445 } 1446 1447 // this value comes from 1.2 spec, but is used in 1.1 as well 1448 private static final int BUFFER_SIZE = 1024; 1449 1450 private static int defaultProtocolVersion = PROTOCOL_VERSION_2; 1451 1452 private DataOutputStream dataOutput; 1453 private boolean writeDataAsBlocks; 1454 private DataOutputStream realOutput; 1455 private DataOutputStream blockDataOutput; 1456 private byte[] blockData; 1457 private int blockDataCount; 1458 private Object currentObject; 1459 // Package-private to avoid a trampoline. 1460 ObjectStreamClass currentObjectStreamClass; 1461 private PutField currentPutField; 1462 private boolean fieldsAlreadyWritten; 1463 private boolean replacementEnabled; 1464 private boolean isSerializing; 1465 private int nextOID; 1466 private ObjectIdentityMap2Int OIDLookupTable; 1467 private int protocolVersion; 1468 private boolean useSubclassMethod; 1469 private SetAccessibleAction setAccessible = new SetAccessibleAction(); 1470 1471 // The nesting depth for debugging output 1472 private int depth = 0; 1473 1474 // Set if we're generating debugging dumps 1475 private boolean dump = false; 1476 1477 private static final boolean DEBUG = false; 1478 }