001/* ObjectOutputStream.java -- Class used to write serialized objects 002 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2008 003 Free Software Foundation, Inc. 004 005This file is part of GNU Classpath. 006 007GNU Classpath is free software; you can redistribute it and/or modify 008it under the terms of the GNU General Public License as published by 009the Free Software Foundation; either version 2, or (at your option) 010any later version. 011 012GNU Classpath is distributed in the hope that it will be useful, but 013WITHOUT ANY WARRANTY; without even the implied warranty of 014MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015General Public License for more details. 016 017You should have received a copy of the GNU General Public License 018along with GNU Classpath; see the file COPYING. If not, write to the 019Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02002110-1301 USA. 021 022Linking this library statically or dynamically with other modules is 023making a combined work based on this library. Thus, the terms and 024conditions of the GNU General Public License cover the whole 025combination. 026 027As a special exception, the copyright holders of this library give you 028permission to link this library with independent modules to produce an 029executable, regardless of the license terms of these independent 030modules, and to copy and distribute the resulting executable under 031terms of your choice, provided that you also meet, for each linked 032independent module, the terms and conditions of the license of that 033module. An independent module is a module which is not derived from 034or based on this library. If you modify this library, you may extend 035this exception to your version of the library, but you are not 036obligated to do so. If you do not wish to do so, delete this 037exception statement from your version. */ 038 039 040package java.io; 041 042import gnu.java.io.ObjectIdentityMap2Int; 043import gnu.java.lang.reflect.TypeSignature; 044import gnu.java.security.action.SetAccessibleAction; 045 046import java.lang.reflect.Array; 047import java.lang.reflect.Field; 048import java.lang.reflect.InvocationTargetException; 049import 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 */ 123public 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 String s = (String)obj; 367 long l = realOutput.getUTFlength(s, 0, 0); 368 if (l <= 65535) 369 { 370 realOutput.writeByte(TC_STRING); 371 if (shared) 372 assignNewHandle(obj); 373 realOutput.writeUTFShort(s, (int)l); 374 } 375 else 376 { 377 realOutput.writeByte(TC_LONGSTRING); 378 if (shared) 379 assignNewHandle(obj); 380 realOutput.writeUTFLong(s, l); 381 } 382 break; 383 } 384 385 if (clazz.isArray ()) 386 { 387 realOutput.writeByte(TC_ARRAY); 388 writeObject(osc); 389 if (shared) 390 assignNewHandle(obj); 391 writeArraySizeAndElements(obj, clazz.getComponentType()); 392 break; 393 } 394 395 realOutput.writeByte(TC_OBJECT); 396 writeObject(osc); 397 398 if (shared) 399 if (replaceDone) 400 assignNewHandle(replacedObject); 401 else 402 assignNewHandle(obj); 403 404 if (obj instanceof Externalizable) 405 { 406 if (protocolVersion == PROTOCOL_VERSION_2) 407 setBlockDataMode(true); 408 409 ((Externalizable)obj).writeExternal(this); 410 411 if (protocolVersion == PROTOCOL_VERSION_2) 412 { 413 setBlockDataMode(false); 414 realOutput.writeByte(TC_ENDBLOCKDATA); 415 } 416 417 break; 418 } 419 420 if (obj instanceof Serializable) 421 { 422 Object prevObject = this.currentObject; 423 ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass; 424 currentObject = obj; 425 ObjectStreamClass[] hierarchy = osc.hierarchy(); 426 427 for (int i = 0; i < hierarchy.length; i++) 428 { 429 currentObjectStreamClass = hierarchy[i]; 430 431 fieldsAlreadyWritten = false; 432 if (currentObjectStreamClass.hasWriteMethod()) 433 { 434 if (dump) 435 dumpElementln ("WRITE METHOD CALLED FOR: ", obj); 436 setBlockDataMode(true); 437 callWriteMethod(obj, currentObjectStreamClass); 438 setBlockDataMode(false); 439 realOutput.writeByte(TC_ENDBLOCKDATA); 440 if (dump) 441 dumpElementln ("WRITE ENDBLOCKDATA FOR: ", obj); 442 } 443 else 444 { 445 if (dump) 446 dumpElementln ("WRITE FIELDS CALLED FOR: ", obj); 447 writeFields(obj, currentObjectStreamClass); 448 } 449 } 450 451 this.currentObject = prevObject; 452 this.currentObjectStreamClass = prevObjectStreamClass; 453 currentPutField = null; 454 break; 455 } 456 457 throw new NotSerializableException(clazz.getName() 458 + " in " 459 + obj.getClass()); 460 } // end pseudo-loop 461 } 462 catch (ObjectStreamException ose) 463 { 464 // Rethrow these are fatal. 465 throw ose; 466 } 467 catch (IOException e) 468 { 469 realOutput.writeByte(TC_EXCEPTION); 470 reset(true); 471 472 setBlockDataMode(false); 473 try 474 { 475 if (DEBUG) 476 { 477 e.printStackTrace(System.out); 478 } 479 writeObject(e); 480 } 481 catch (IOException ioe) 482 { 483 StreamCorruptedException ex = 484 new StreamCorruptedException 485 (ioe + " thrown while exception was being written to stream."); 486 if (DEBUG) 487 { 488 ex.printStackTrace(System.out); 489 } 490 throw ex; 491 } 492 493 reset (true); 494 495 } 496 finally 497 { 498 isSerializing = was_serializing; 499 setBlockDataMode(old_mode); 500 depth -= 2; 501 502 if (dump) 503 dumpElementln ("END: ", obj); 504 } 505 } 506 507 protected void writeClassDescriptor(ObjectStreamClass osc) throws IOException 508 { 509 if (osc.isProxyClass) 510 { 511 realOutput.writeByte(TC_PROXYCLASSDESC); 512 Class[] intfs = osc.forClass().getInterfaces(); 513 realOutput.writeInt(intfs.length); 514 for (int i = 0; i < intfs.length; i++) 515 realOutput.writeUTF(intfs[i].getName()); 516 517 assignNewHandle(osc); 518 519 boolean oldmode = setBlockDataMode(true); 520 annotateProxyClass(osc.forClass()); 521 setBlockDataMode(oldmode); 522 realOutput.writeByte(TC_ENDBLOCKDATA); 523 } 524 else 525 { 526 realOutput.writeByte(TC_CLASSDESC); 527 realOutput.writeUTF(osc.getName()); 528 if (osc.isEnum()) 529 realOutput.writeLong(0L); 530 else 531 realOutput.writeLong(osc.getSerialVersionUID()); 532 assignNewHandle(osc); 533 534 int flags = osc.getFlags(); 535 536 if (protocolVersion == PROTOCOL_VERSION_2 537 && osc.isExternalizable()) 538 flags |= SC_BLOCK_DATA; 539 540 realOutput.writeByte(flags); 541 542 ObjectStreamField[] fields = osc.fields; 543 544 if (fields == ObjectStreamClass.INVALID_FIELDS) 545 throw new InvalidClassException 546 (osc.getName(), "serialPersistentFields is invalid"); 547 548 realOutput.writeShort(fields.length); 549 550 ObjectStreamField field; 551 for (int i = 0; i < fields.length; i++) 552 { 553 field = fields[i]; 554 realOutput.writeByte(field.getTypeCode ()); 555 realOutput.writeUTF(field.getName ()); 556 557 if (! field.isPrimitive()) 558 writeObject(field.getTypeString()); 559 } 560 561 boolean oldmode = setBlockDataMode(true); 562 annotateClass(osc.forClass()); 563 setBlockDataMode(oldmode); 564 realOutput.writeByte(TC_ENDBLOCKDATA); 565 } 566 567 if (osc.isSerializable() || osc.isExternalizable()) 568 writeObject(osc.getSuper()); 569 else 570 writeObject(null); 571 } 572 573 /** 574 * Writes the current objects non-transient, non-static fields from 575 * the current class to the underlying output stream. 576 * 577 * This method is intended to be called from within a object's 578 * <code>private void writeObject (ObjectOutputStream)</code> 579 * method. 580 * 581 * @exception NotActiveException This method was called from a 582 * context other than from the current object's and current class's 583 * <code>private void writeObject (ObjectOutputStream)</code> 584 * method. 585 * 586 * @exception IOException Exception from underlying 587 * <code>OutputStream</code>. 588 */ 589 public void defaultWriteObject() 590 throws IOException, NotActiveException 591 { 592 markFieldsWritten(); 593 writeFields(currentObject, currentObjectStreamClass); 594 } 595 596 597 private void markFieldsWritten() throws IOException 598 { 599 if (currentObject == null || currentObjectStreamClass == null) 600 throw new NotActiveException 601 ("defaultWriteObject called by non-active class and/or object"); 602 603 if (fieldsAlreadyWritten) 604 throw new IOException 605 ("Only one of writeFields and defaultWriteObject may be called, and it may only be called once"); 606 607 fieldsAlreadyWritten = true; 608 } 609 610 /** 611 * Resets stream to state equivalent to the state just after it was 612 * constructed. 613 * 614 * Causes all objects previously written to the stream to be 615 * forgotten. A notification of this reset is also written to the 616 * underlying stream. 617 * 618 * @exception IOException Exception from underlying 619 * <code>OutputStream</code> or reset called while serialization is 620 * in progress. 621 */ 622 public void reset() throws IOException 623 { 624 reset(false); 625 } 626 627 628 private void reset(boolean internal) throws IOException 629 { 630 if (!internal) 631 { 632 if (isSerializing) 633 throw new IOException("Reset called while serialization in progress"); 634 635 realOutput.writeByte(TC_RESET); 636 } 637 638 clearHandles(); 639 } 640 641 642 /** 643 * Informs this <code>ObjectOutputStream</code> to write data 644 * according to the specified protocol. There are currently two 645 * different protocols, specified by <code>PROTOCOL_VERSION_1</code> 646 * and <code>PROTOCOL_VERSION_2</code>. This implementation writes 647 * data using <code>PROTOCOL_VERSION_2</code> by default, as is done 648 * since the JDK 1.2. 649 * <p> 650 * For an explanation of the differences between the two protocols 651 * see the Java Object Serialization Specification. 652 * </p> 653 * 654 * @param version the version to use. 655 * 656 * @throws IllegalArgumentException if <code>version</code> is not a valid 657 * protocol. 658 * @throws IllegalStateException if called after the first the first object 659 * was serialized. 660 * @throws IOException if an I/O error occurs. 661 * 662 * @see ObjectStreamConstants#PROTOCOL_VERSION_1 663 * @see ObjectStreamConstants#PROTOCOL_VERSION_2 664 * 665 * @since 1.2 666 */ 667 public void useProtocolVersion(int version) throws IOException 668 { 669 if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2) 670 throw new IllegalArgumentException("Invalid protocol version requested."); 671 672 if (nextOID != baseWireHandle) 673 throw new IllegalStateException("Protocol version cannot be changed " 674 + "after serialization started."); 675 676 protocolVersion = version; 677 } 678 679 /** 680 * An empty hook that allows subclasses to write extra information 681 * about classes to the stream. This method is called the first 682 * time each class is seen, and after all of the standard 683 * information about the class has been written. 684 * 685 * @exception IOException Exception from underlying 686 * <code>OutputStream</code>. 687 * 688 * @see ObjectInputStream#resolveClass(java.io.ObjectStreamClass) 689 */ 690 protected void annotateClass(Class<?> cl) throws IOException 691 { 692 } 693 694 protected void annotateProxyClass(Class<?> cl) throws IOException 695 { 696 } 697 698 /** 699 * Allows subclasses to replace objects that are written to the 700 * stream with other objects to be written in their place. This 701 * method is called the first time each object is encountered 702 * (modulo reseting of the stream). 703 * 704 * This method must be enabled before it will be called in the 705 * serialization process. 706 * 707 * @exception IOException Exception from underlying 708 * <code>OutputStream</code>. 709 * 710 * @see #enableReplaceObject(boolean) 711 */ 712 protected Object replaceObject(Object obj) throws IOException 713 { 714 return obj; 715 } 716 717 718 /** 719 * If <code>enable</code> is <code>true</code> and this object is 720 * trusted, then <code>replaceObject (Object)</code> will be called 721 * in subsequent calls to <code>writeObject (Object)</code>. 722 * Otherwise, <code>replaceObject (Object)</code> will not be called. 723 * 724 * @exception SecurityException This class is not trusted. 725 */ 726 protected boolean enableReplaceObject(boolean enable) 727 throws SecurityException 728 { 729 if (enable) 730 { 731 SecurityManager sm = System.getSecurityManager(); 732 if (sm != null) 733 sm.checkPermission(new SerializablePermission("enableSubstitution")); 734 } 735 736 boolean old_val = replacementEnabled; 737 replacementEnabled = enable; 738 return old_val; 739 } 740 741 742 /** 743 * Writes stream magic and stream version information to the 744 * underlying stream. 745 * 746 * @exception IOException Exception from underlying 747 * <code>OutputStream</code>. 748 */ 749 protected void writeStreamHeader() throws IOException 750 { 751 realOutput.writeShort(STREAM_MAGIC); 752 realOutput.writeShort(STREAM_VERSION); 753 } 754 755 /** 756 * Protected constructor that allows subclasses to override 757 * serialization. This constructor should be called by subclasses 758 * that wish to override <code>writeObject (Object)</code>. This 759 * method does a security check <i>NOTE: currently not 760 * implemented</i>, then sets a flag that informs 761 * <code>writeObject (Object)</code> to call the subclasses 762 * <code>writeObjectOverride (Object)</code> method. 763 * 764 * @see #writeObjectOverride(Object) 765 */ 766 protected ObjectOutputStream() throws IOException, SecurityException 767 { 768 SecurityManager sec_man = System.getSecurityManager (); 769 if (sec_man != null) 770 sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); 771 useSubclassMethod = true; 772 } 773 774 775 /** 776 * This method allows subclasses to override the default 777 * serialization mechanism provided by 778 * <code>ObjectOutputStream</code>. To make this method be used for 779 * writing objects, subclasses must invoke the 0-argument 780 * constructor on this class from there constructor. 781 * 782 * @see #ObjectOutputStream() 783 * 784 * @exception NotActiveException Subclass has arranged for this 785 * method to be called, but did not implement this method. 786 */ 787 protected void writeObjectOverride(Object obj) throws NotActiveException, 788 IOException 789 { 790 throw new NotActiveException 791 ("Subclass of ObjectOutputStream must implement writeObjectOverride"); 792 } 793 794 795 /** 796 * @see DataOutputStream#write(int) 797 */ 798 public void write (int data) throws IOException 799 { 800 if (writeDataAsBlocks) 801 { 802 if (blockDataCount == BUFFER_SIZE) 803 drain(); 804 805 blockData[ blockDataCount++ ] = (byte)data; 806 } 807 else 808 realOutput.write(data); 809 } 810 811 812 /** 813 * @see DataOutputStream#write(byte[]) 814 */ 815 public void write(byte[] b) throws IOException 816 { 817 write(b, 0, b.length); 818 } 819 820 821 /** 822 * @see DataOutputStream#write(byte[],int,int) 823 */ 824 public void write(byte[] b, int off, int len) throws IOException 825 { 826 if (writeDataAsBlocks) 827 { 828 if (len < 0) 829 throw new IndexOutOfBoundsException(); 830 831 if (blockDataCount + len < BUFFER_SIZE) 832 { 833 System.arraycopy(b, off, blockData, blockDataCount, len); 834 blockDataCount += len; 835 } 836 else 837 { 838 drain(); 839 writeBlockDataHeader(len); 840 realOutput.write(b, off, len); 841 } 842 } 843 else 844 realOutput.write(b, off, len); 845 } 846 847 848 /** 849 * @see DataOutputStream#flush() 850 */ 851 public void flush () throws IOException 852 { 853 drain(); 854 realOutput.flush(); 855 } 856 857 858 /** 859 * Causes the block-data buffer to be written to the underlying 860 * stream, but does not flush underlying stream. 861 * 862 * @exception IOException Exception from underlying 863 * <code>OutputStream</code>. 864 */ 865 protected void drain() throws IOException 866 { 867 if (blockDataCount == 0) 868 return; 869 870 if (writeDataAsBlocks) 871 writeBlockDataHeader(blockDataCount); 872 realOutput.write(blockData, 0, blockDataCount); 873 blockDataCount = 0; 874 } 875 876 877 /** 878 * @see java.io.DataOutputStream#close () 879 */ 880 public void close() throws IOException 881 { 882 flush(); 883 realOutput.close(); 884 } 885 886 887 /** 888 * @see java.io.DataOutputStream#writeBoolean (boolean) 889 */ 890 public void writeBoolean(boolean data) throws IOException 891 { 892 blockDataOutput.writeBoolean(data); 893 } 894 895 896 /** 897 * @see java.io.DataOutputStream#writeByte (int) 898 */ 899 public void writeByte(int data) throws IOException 900 { 901 blockDataOutput.writeByte(data); 902 } 903 904 905 /** 906 * @see java.io.DataOutputStream#writeShort (int) 907 */ 908 public void writeShort (int data) throws IOException 909 { 910 blockDataOutput.writeShort(data); 911 } 912 913 914 /** 915 * @see java.io.DataOutputStream#writeChar (int) 916 */ 917 public void writeChar(int data) throws IOException 918 { 919 blockDataOutput.writeChar(data); 920 } 921 922 923 /** 924 * @see java.io.DataOutputStream#writeInt (int) 925 */ 926 public void writeInt(int data) throws IOException 927 { 928 blockDataOutput.writeInt(data); 929 } 930 931 932 /** 933 * @see java.io.DataOutputStream#writeLong (long) 934 */ 935 public void writeLong(long data) throws IOException 936 { 937 blockDataOutput.writeLong(data); 938 } 939 940 941 /** 942 * @see java.io.DataOutputStream#writeFloat (float) 943 */ 944 public void writeFloat(float data) throws IOException 945 { 946 blockDataOutput.writeFloat(data); 947 } 948 949 950 /** 951 * @see java.io.DataOutputStream#writeDouble (double) 952 */ 953 public void writeDouble(double data) throws IOException 954 { 955 blockDataOutput.writeDouble(data); 956 } 957 958 959 /** 960 * @see java.io.DataOutputStream#writeBytes (java.lang.String) 961 */ 962 public void writeBytes(String data) throws IOException 963 { 964 blockDataOutput.writeBytes(data); 965 } 966 967 968 /** 969 * @see java.io.DataOutputStream#writeChars (java.lang.String) 970 */ 971 public void writeChars(String data) throws IOException 972 { 973 dataOutput.writeChars(data); 974 } 975 976 977 /** 978 * @see java.io.DataOutputStream#writeUTF (java.lang.String) 979 */ 980 public void writeUTF(String data) throws IOException 981 { 982 dataOutput.writeUTF(data); 983 } 984 985 986 /** 987 * This class allows a class to specify exactly which fields should 988 * be written, and what values should be written for these fields. 989 * 990 * XXX: finish up comments 991 */ 992 public abstract static class PutField 993 { 994 public abstract void put (String name, boolean value); 995 public abstract void put (String name, byte value); 996 public abstract void put (String name, char value); 997 public abstract void put (String name, double value); 998 public abstract void put (String name, float value); 999 public abstract void put (String name, int value); 1000 public abstract void put (String name, long value); 1001 public abstract void put (String name, short value); 1002 public abstract void put (String name, Object value); 1003 1004 /** 1005 * @deprecated 1006 */ 1007 public abstract void write (ObjectOutput out) throws IOException; 1008 } 1009 1010 public PutField putFields() throws IOException 1011 { 1012 if (currentPutField != null) 1013 return currentPutField; 1014 1015 currentPutField = new PutField() 1016 { 1017 private byte[] prim_field_data 1018 = new byte[currentObjectStreamClass.primFieldSize]; 1019 private Object[] objs 1020 = new Object[currentObjectStreamClass.objectFieldCount]; 1021 1022 private ObjectStreamField getField (String name) 1023 { 1024 ObjectStreamField field 1025 = currentObjectStreamClass.getField(name); 1026 1027 if (field == null) 1028 throw new IllegalArgumentException("no such serializable field " + name); 1029 1030 return field; 1031 } 1032 1033 public void put(String name, boolean value) 1034 { 1035 ObjectStreamField field = getField(name); 1036 1037 checkType(field, 'Z'); 1038 prim_field_data[field.getOffset ()] = (byte)(value ? 1 : 0); 1039 } 1040 1041 public void put(String name, byte value) 1042 { 1043 ObjectStreamField field = getField(name); 1044 1045 checkType(field, 'B'); 1046 prim_field_data[field.getOffset()] = value; 1047 } 1048 1049 public void put(String name, char value) 1050 { 1051 ObjectStreamField field = getField(name); 1052 1053 checkType(field, 'C'); 1054 int off = field.getOffset(); 1055 prim_field_data[off++] = (byte)(value >>> 8); 1056 prim_field_data[off] = (byte)value; 1057 } 1058 1059 public void put(String name, double value) 1060 { 1061 ObjectStreamField field = getField (name); 1062 1063 checkType(field, 'D'); 1064 int off = field.getOffset(); 1065 long l_value = Double.doubleToLongBits (value); 1066 prim_field_data[off++] = (byte)(l_value >>> 52); 1067 prim_field_data[off++] = (byte)(l_value >>> 48); 1068 prim_field_data[off++] = (byte)(l_value >>> 40); 1069 prim_field_data[off++] = (byte)(l_value >>> 32); 1070 prim_field_data[off++] = (byte)(l_value >>> 24); 1071 prim_field_data[off++] = (byte)(l_value >>> 16); 1072 prim_field_data[off++] = (byte)(l_value >>> 8); 1073 prim_field_data[off] = (byte)l_value; 1074 } 1075 1076 public void put(String name, float value) 1077 { 1078 ObjectStreamField field = getField(name); 1079 1080 checkType(field, 'F'); 1081 int off = field.getOffset(); 1082 int i_value = Float.floatToIntBits(value); 1083 prim_field_data[off++] = (byte)(i_value >>> 24); 1084 prim_field_data[off++] = (byte)(i_value >>> 16); 1085 prim_field_data[off++] = (byte)(i_value >>> 8); 1086 prim_field_data[off] = (byte)i_value; 1087 } 1088 1089 public void put(String name, int value) 1090 { 1091 ObjectStreamField field = getField(name); 1092 checkType(field, 'I'); 1093 int off = field.getOffset(); 1094 prim_field_data[off++] = (byte)(value >>> 24); 1095 prim_field_data[off++] = (byte)(value >>> 16); 1096 prim_field_data[off++] = (byte)(value >>> 8); 1097 prim_field_data[off] = (byte)value; 1098 } 1099 1100 public void put(String name, long value) 1101 { 1102 ObjectStreamField field = getField(name); 1103 checkType(field, 'J'); 1104 int off = field.getOffset(); 1105 prim_field_data[off++] = (byte)(value >>> 52); 1106 prim_field_data[off++] = (byte)(value >>> 48); 1107 prim_field_data[off++] = (byte)(value >>> 40); 1108 prim_field_data[off++] = (byte)(value >>> 32); 1109 prim_field_data[off++] = (byte)(value >>> 24); 1110 prim_field_data[off++] = (byte)(value >>> 16); 1111 prim_field_data[off++] = (byte)(value >>> 8); 1112 prim_field_data[off] = (byte)value; 1113 } 1114 1115 public void put(String name, short value) 1116 { 1117 ObjectStreamField field = getField(name); 1118 checkType(field, 'S'); 1119 int off = field.getOffset(); 1120 prim_field_data[off++] = (byte)(value >>> 8); 1121 prim_field_data[off] = (byte)value; 1122 } 1123 1124 public void put(String name, Object value) 1125 { 1126 ObjectStreamField field = getField(name); 1127 1128 if (value != null && 1129 ! field.getType().isAssignableFrom(value.getClass ())) 1130 throw new IllegalArgumentException("Class " + value.getClass() + 1131 " cannot be cast to " + field.getType()); 1132 objs[field.getOffset()] = value; 1133 } 1134 1135 public void write(ObjectOutput out) throws IOException 1136 { 1137 // Apparently Block data is not used with PutField as per 1138 // empirical evidence against JDK 1.2. Also see Mauve test 1139 // java.io.ObjectInputOutput.Test.GetPutField. 1140 boolean oldmode = setBlockDataMode(false); 1141 out.write(prim_field_data); 1142 for (int i = 0; i < objs.length; ++ i) 1143 out.writeObject(objs[i]); 1144 setBlockDataMode(oldmode); 1145 } 1146 1147 private void checkType(ObjectStreamField field, char type) 1148 throws IllegalArgumentException 1149 { 1150 if (TypeSignature.getEncodingOfClass(field.getType()).charAt(0) 1151 != type) 1152 throw new IllegalArgumentException(); 1153 } 1154 }; 1155 // end PutFieldImpl 1156 1157 return currentPutField; 1158 } 1159 1160 1161 public void writeFields() throws IOException 1162 { 1163 if (currentPutField == null) 1164 throw new NotActiveException("writeFields can only be called after putFields has been called"); 1165 1166 markFieldsWritten(); 1167 currentPutField.write(this); 1168 } 1169 1170 1171 // write out the block-data buffer, picking the correct header 1172 // depending on the size of the buffer 1173 private void writeBlockDataHeader(int size) throws IOException 1174 { 1175 if (size < 256) 1176 { 1177 realOutput.writeByte(TC_BLOCKDATA); 1178 realOutput.write(size); 1179 } 1180 else 1181 { 1182 realOutput.writeByte(TC_BLOCKDATALONG); 1183 realOutput.writeInt(size); 1184 } 1185 } 1186 1187 1188 // lookup the handle for OBJ, return null if OBJ doesn't have a 1189 // handle yet 1190 private int findHandle(Object obj) 1191 { 1192 return OIDLookupTable.get(obj); 1193 } 1194 1195 1196 // assigns the next availible handle to OBJ 1197 private int assignNewHandle(Object obj) 1198 { 1199 OIDLookupTable.put(obj, nextOID); 1200 return nextOID++; 1201 } 1202 1203 1204 // resets mapping from objects to handles 1205 private void clearHandles() 1206 { 1207 nextOID = baseWireHandle; 1208 OIDLookupTable.clear(); 1209 } 1210 1211 1212 // write out array size followed by each element of the array 1213 private void writeArraySizeAndElements(Object array, Class clazz) 1214 throws IOException 1215 { 1216 int length = Array.getLength(array); 1217 1218 if (clazz.isPrimitive()) 1219 { 1220 if (clazz == Boolean.TYPE) 1221 { 1222 boolean[] cast_array = (boolean[])array; 1223 realOutput.writeInt (length); 1224 for (int i = 0; i < length; i++) 1225 realOutput.writeBoolean(cast_array[i]); 1226 return; 1227 } 1228 if (clazz == Byte.TYPE) 1229 { 1230 byte[] cast_array = (byte[])array; 1231 realOutput.writeInt(length); 1232 realOutput.write(cast_array, 0, length); 1233 return; 1234 } 1235 if (clazz == Character.TYPE) 1236 { 1237 char[] cast_array = (char[])array; 1238 realOutput.writeInt(length); 1239 for (int i = 0; i < length; i++) 1240 realOutput.writeChar(cast_array[i]); 1241 return; 1242 } 1243 if (clazz == Double.TYPE) 1244 { 1245 double[] cast_array = (double[])array; 1246 realOutput.writeInt(length); 1247 for (int i = 0; i < length; i++) 1248 realOutput.writeDouble(cast_array[i]); 1249 return; 1250 } 1251 if (clazz == Float.TYPE) 1252 { 1253 float[] cast_array = (float[])array; 1254 realOutput.writeInt(length); 1255 for (int i = 0; i < length; i++) 1256 realOutput.writeFloat(cast_array[i]); 1257 return; 1258 } 1259 if (clazz == Integer.TYPE) 1260 { 1261 int[] cast_array = (int[])array; 1262 realOutput.writeInt(length); 1263 for (int i = 0; i < length; i++) 1264 realOutput.writeInt(cast_array[i]); 1265 return; 1266 } 1267 if (clazz == Long.TYPE) 1268 { 1269 long[] cast_array = (long[])array; 1270 realOutput.writeInt (length); 1271 for (int i = 0; i < length; i++) 1272 realOutput.writeLong(cast_array[i]); 1273 return; 1274 } 1275 if (clazz == Short.TYPE) 1276 { 1277 short[] cast_array = (short[])array; 1278 realOutput.writeInt (length); 1279 for (int i = 0; i < length; i++) 1280 realOutput.writeShort(cast_array[i]); 1281 return; 1282 } 1283 } 1284 else 1285 { 1286 Object[] cast_array = (Object[])array; 1287 realOutput.writeInt(length); 1288 for (int i = 0; i < length; i++) 1289 writeObject(cast_array[i]); 1290 } 1291 } 1292 1293 1294/* GCJ LOCAL */ 1295 // writes out FIELDS of OBJECT for the specified ObjectStreamClass. 1296 // FIELDS are already supposed already to be in canonical order, but 1297 // under some circumstances (to do with Proxies) this isn't the 1298 // case, so we call ensureFieldsSet(). 1299 private void writeFields(Object obj, ObjectStreamClass osc) 1300 throws IOException 1301 { 1302 osc.ensureFieldsSet(osc.forClass()); 1303/* END GCJ LOCAL */ 1304 1305 ObjectStreamField[] fields = osc.fields; 1306 boolean oldmode = setBlockDataMode(false); 1307 1308 try 1309 { 1310 writeFields(obj,fields); 1311 } 1312 catch (IllegalArgumentException _) 1313 { 1314 InvalidClassException e = new InvalidClassException 1315 ("writing fields of class " + osc.forClass().getName()); 1316 e.initCause(_); 1317 throw e; 1318 } 1319 catch (IOException e) 1320 { 1321 throw e; 1322 } 1323 catch (Exception _) 1324 { 1325 IOException e = new IOException("Unexpected exception " + _); 1326 e.initCause(_); 1327 throw(e); 1328 } 1329 1330 setBlockDataMode(oldmode); 1331 } 1332 1333 1334 /** 1335 * Helper function for writeFields(Object,ObjectStreamClass): write 1336 * fields from given fields array. Pass exception on. 1337 * 1338 * @param obj the object to be written 1339 * 1340 * @param fields the fields of obj to be written. 1341 */ 1342 private void writeFields(Object obj, ObjectStreamField[] fields) 1343 throws 1344 IllegalArgumentException, IllegalAccessException, IOException 1345 { 1346 for (int i = 0; i < fields.length; i++) 1347 { 1348 ObjectStreamField osf = fields[i]; 1349 Field field = osf.field; 1350 1351 if (DEBUG && dump) 1352 dumpElementln ("WRITE FIELD: " + osf.getName() + " type=" + osf.getType()); 1353 1354 switch (osf.getTypeCode()) 1355 { 1356 case 'Z': realOutput.writeBoolean(field.getBoolean(obj)); break; 1357 case 'B': realOutput.writeByte (field.getByte (obj)); break; 1358 case 'S': realOutput.writeShort (field.getShort (obj)); break; 1359 case 'C': realOutput.writeChar (field.getChar (obj)); break; 1360 case 'I': realOutput.writeInt (field.getInt (obj)); break; 1361 case 'F': realOutput.writeFloat (field.getFloat (obj)); break; 1362 case 'J': realOutput.writeLong (field.getLong (obj)); break; 1363 case 'D': realOutput.writeDouble (field.getDouble (obj)); break; 1364 case 'L': 1365 case '[': writeObject (field.get (obj)); break; 1366 default: 1367 throw new IOException("Unexpected type code " + osf.getTypeCode()); 1368 } 1369 } 1370 } 1371 1372 1373 // Toggles writing primitive data to block-data buffer. 1374 // Package-private to avoid a trampoline constructor. 1375 boolean setBlockDataMode(boolean on) throws IOException 1376 { 1377 if (on == writeDataAsBlocks) 1378 return on; 1379 1380 drain(); 1381 boolean oldmode = writeDataAsBlocks; 1382 writeDataAsBlocks = on; 1383 1384 if (on) 1385 dataOutput = blockDataOutput; 1386 else 1387 dataOutput = realOutput; 1388 1389 return oldmode; 1390 } 1391 1392 1393 private void callWriteMethod(Object obj, ObjectStreamClass osc) 1394 throws IOException 1395 { 1396 currentPutField = null; 1397 try 1398 { 1399 Object args[] = {this}; 1400 osc.writeObjectMethod.invoke(obj, args); 1401 } 1402 catch (InvocationTargetException x) 1403 { 1404 /* Rethrow if possible. */ 1405 Throwable exception = x.getTargetException(); 1406 if (exception instanceof RuntimeException) 1407 throw (RuntimeException) exception; 1408 if (exception instanceof IOException) 1409 throw (IOException) exception; 1410 1411 IOException ioe 1412 = new IOException("Exception thrown from writeObject() on " + 1413 osc.forClass().getName() + ": " + 1414 exception.getClass().getName()); 1415 ioe.initCause(exception); 1416 throw ioe; 1417 } 1418 catch (Exception x) 1419 { 1420 IOException ioe 1421 = new IOException("Failure invoking writeObject() on " + 1422 osc.forClass().getName() + ": " + 1423 x.getClass().getName()); 1424 ioe.initCause(x); 1425 throw ioe; 1426 } 1427 } 1428 1429 private void dumpElementln (String msg, Object obj) 1430 { 1431 try 1432 { 1433 for (int i = 0; i < depth; i++) 1434 System.out.print (" "); 1435 System.out.print (Thread.currentThread() + ": "); 1436 System.out.print (msg); 1437 if (java.lang.reflect.Proxy.isProxyClass(obj.getClass())) 1438 System.out.print (obj.getClass()); 1439 else 1440 System.out.print (obj); 1441 } 1442 catch (Exception _) 1443 { 1444 } 1445 finally 1446 { 1447 System.out.println (); 1448 } 1449 } 1450 1451 private void dumpElementln (String msg) 1452 { 1453 for (int i = 0; i < depth; i++) 1454 System.out.print (" "); 1455 System.out.print (Thread.currentThread() + ": "); 1456 System.out.println(msg); 1457 } 1458 1459 // this value comes from 1.2 spec, but is used in 1.1 as well 1460 private static final int BUFFER_SIZE = 1024; 1461 1462 private static int defaultProtocolVersion = PROTOCOL_VERSION_2; 1463 1464 private DataOutputStream dataOutput; 1465 private boolean writeDataAsBlocks; 1466 private DataOutputStream realOutput; 1467 private DataOutputStream blockDataOutput; 1468 private byte[] blockData; 1469 private int blockDataCount; 1470 private Object currentObject; 1471 // Package-private to avoid a trampoline. 1472 ObjectStreamClass currentObjectStreamClass; 1473 private PutField currentPutField; 1474 private boolean fieldsAlreadyWritten; 1475 private boolean replacementEnabled; 1476 private boolean isSerializing; 1477 private int nextOID; 1478 private ObjectIdentityMap2Int OIDLookupTable; 1479 private int protocolVersion; 1480 private boolean useSubclassMethod; 1481 private SetAccessibleAction setAccessible = new SetAccessibleAction(); 1482 1483 // The nesting depth for debugging output 1484 private int depth = 0; 1485 1486 // Set if we're generating debugging dumps 1487 private boolean dump = false; 1488 1489 private static final boolean DEBUG = false; 1490}