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