1: /* ObjectOutputStream.java -- Class used to write serialized objects 2: Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 3: Free Software Foundation, Inc. 4: 5: This file is part of GNU Classpath. 6: 7: GNU Classpath is free software; you can redistribute it and/or modify 8: it under the terms of the GNU General Public License as published by 9: the Free Software Foundation; either version 2, or (at your option) 10: any later version. 11: 12: GNU Classpath is distributed in the hope that it will be useful, but 13: WITHOUT ANY WARRANTY; without even the implied warranty of 14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15: General Public License for more details. 16: 17: You should have received a copy of the GNU General Public License 18: along with GNU Classpath; see the file COPYING. If not, write to the 19: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 20: 02110-1301 USA. 21: 22: Linking this library statically or dynamically with other modules is 23: making a combined work based on this library. Thus, the terms and 24: conditions of the GNU General Public License cover the whole 25: combination. 26: 27: As a special exception, the copyright holders of this library give you 28: permission to link this library with independent modules to produce an 29: executable, regardless of the license terms of these independent 30: modules, and to copy and distribute the resulting executable under 31: terms of your choice, provided that you also meet, for each linked 32: independent module, the terms and conditions of the license of that 33: module. An independent module is a module which is not derived from 34: or based on this library. If you modify this library, you may extend 35: this exception to your version of the library, but you are not 36: obligated to do so. If you do not wish to do so, delete this 37: exception statement from your version. */ 38: 39: 40: package java.io; 41: 42: import gnu.java.io.ObjectIdentityMap2Int; 43: import gnu.java.lang.reflect.TypeSignature; 44: import gnu.java.security.action.SetAccessibleAction; 45: 46: import java.lang.reflect.Array; 47: import java.lang.reflect.Field; 48: import java.lang.reflect.InvocationTargetException; 49: import java.lang.reflect.Method; 50: 51: 52: /** 53: * An <code>ObjectOutputStream</code> can be used to write objects 54: * as well as primitive data in a platform-independent manner to an 55: * <code>OutputStream</code>. 56: * 57: * The data produced by an <code>ObjectOutputStream</code> can be read 58: * and reconstituted by an <code>ObjectInputStream</code>. 59: * 60: * <code>writeObject (Object)</code> is used to write Objects, the 61: * <code>write<type></code> methods are used to write primitive 62: * data (as in <code>DataOutputStream</code>). Strings can be written 63: * as objects or as primitive data. 64: * 65: * Not all objects can be written out using an 66: * <code>ObjectOutputStream</code>. Only those objects that are an 67: * instance of <code>java.io.Serializable</code> can be written. 68: * 69: * Using default serialization, information about the class of an 70: * object is written, all of the non-transient, non-static fields of 71: * the object are written, if any of these fields are objects, they are 72: * written out in the same manner. 73: * 74: * An object is only written out the first time it is encountered. If 75: * the object is encountered later, a reference to it is written to 76: * the underlying stream. Thus writing circular object graphs 77: * does not present a problem, nor are relationships between objects 78: * in a graph lost. 79: * 80: * Example usage: 81: * <pre> 82: * Hashtable map = new Hashtable (); 83: * map.put ("one", new Integer (1)); 84: * map.put ("two", new Integer (2)); 85: * 86: * ObjectOutputStream oos = 87: * new ObjectOutputStream (new FileOutputStream ("numbers")); 88: * oos.writeObject (map); 89: * oos.close (); 90: * 91: * ObjectInputStream ois = 92: * new ObjectInputStream (new FileInputStream ("numbers")); 93: * Hashtable newmap = (Hashtable)ois.readObject (); 94: * 95: * System.out.println (newmap); 96: * </pre> 97: * 98: * The default serialization can be overriden in two ways. 99: * 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: // writes out FIELDS of OBJECT for the specified ObjectStreamClass. 1283: // FIELDS are already in canonical order. 1284: private void writeFields(Object obj, ObjectStreamClass osc) 1285: throws IOException 1286: { 1287: ObjectStreamField[] fields = osc.fields; 1288: boolean oldmode = setBlockDataMode(false); 1289: 1290: try 1291: { 1292: writeFields(obj,fields); 1293: } 1294: catch (IllegalArgumentException _) 1295: { 1296: InvalidClassException e = new InvalidClassException 1297: ("writing fields of class " + osc.forClass().getName()); 1298: e.initCause(_); 1299: throw e; 1300: } 1301: catch (IOException e) 1302: { 1303: throw e; 1304: } 1305: catch (Exception _) 1306: { 1307: IOException e = new IOException("Unexpected exception " + _); 1308: e.initCause(_); 1309: throw(e); 1310: } 1311: 1312: setBlockDataMode(oldmode); 1313: } 1314: 1315: 1316: /** 1317: * Helper function for writeFields(Object,ObjectStreamClass): write 1318: * fields from given fields array. Pass exception on. 1319: * 1320: * @param obj the object to be written 1321: * 1322: * @param fields the fields of obj to be written. 1323: */ 1324: private void writeFields(Object obj, ObjectStreamField[] fields) 1325: throws 1326: IllegalArgumentException, IllegalAccessException, IOException 1327: { 1328: for (int i = 0; i < fields.length; i++) 1329: { 1330: ObjectStreamField osf = fields[i]; 1331: Field field = osf.field; 1332: 1333: if (DEBUG && dump) 1334: dumpElementln ("WRITE FIELD: " + osf.getName() + " type=" + osf.getType()); 1335: 1336: switch (osf.getTypeCode()) 1337: { 1338: case 'Z': realOutput.writeBoolean(field.getBoolean(obj)); break; 1339: case 'B': realOutput.writeByte (field.getByte (obj)); break; 1340: case 'S': realOutput.writeShort (field.getShort (obj)); break; 1341: case 'C': realOutput.writeChar (field.getChar (obj)); break; 1342: case 'I': realOutput.writeInt (field.getInt (obj)); break; 1343: case 'F': realOutput.writeFloat (field.getFloat (obj)); break; 1344: case 'J': realOutput.writeLong (field.getLong (obj)); break; 1345: case 'D': realOutput.writeDouble (field.getDouble (obj)); break; 1346: case 'L': 1347: case '[': writeObject (field.get (obj)); break; 1348: default: 1349: throw new IOException("Unexpected type code " + osf.getTypeCode()); 1350: } 1351: } 1352: } 1353: 1354: 1355: // Toggles writing primitive data to block-data buffer. 1356: // Package-private to avoid a trampoline constructor. 1357: boolean setBlockDataMode(boolean on) throws IOException 1358: { 1359: if (on == writeDataAsBlocks) 1360: return on; 1361: 1362: drain(); 1363: boolean oldmode = writeDataAsBlocks; 1364: writeDataAsBlocks = on; 1365: 1366: if (on) 1367: dataOutput = blockDataOutput; 1368: else 1369: dataOutput = realOutput; 1370: 1371: return oldmode; 1372: } 1373: 1374: 1375: private void callWriteMethod(Object obj, ObjectStreamClass osc) 1376: throws IOException 1377: { 1378: currentPutField = null; 1379: try 1380: { 1381: Object args[] = {this}; 1382: osc.writeObjectMethod.invoke(obj, args); 1383: } 1384: catch (InvocationTargetException x) 1385: { 1386: /* Rethrow if possible. */ 1387: Throwable exception = x.getTargetException(); 1388: if (exception instanceof RuntimeException) 1389: throw (RuntimeException) exception; 1390: if (exception instanceof IOException) 1391: throw (IOException) exception; 1392: 1393: IOException ioe 1394: = new IOException("Exception thrown from writeObject() on " + 1395: osc.forClass().getName() + ": " + 1396: exception.getClass().getName()); 1397: ioe.initCause(exception); 1398: throw ioe; 1399: } 1400: catch (Exception x) 1401: { 1402: IOException ioe 1403: = new IOException("Failure invoking writeObject() on " + 1404: osc.forClass().getName() + ": " + 1405: x.getClass().getName()); 1406: ioe.initCause(x); 1407: throw ioe; 1408: } 1409: } 1410: 1411: private void dumpElementln (String msg) 1412: { 1413: for (int i = 0; i < depth; i++) 1414: System.out.print (" "); 1415: System.out.print (Thread.currentThread() + ": "); 1416: System.out.println(msg); 1417: } 1418: 1419: // this value comes from 1.2 spec, but is used in 1.1 as well 1420: private static final int BUFFER_SIZE = 1024; 1421: 1422: private static int defaultProtocolVersion = PROTOCOL_VERSION_2; 1423: 1424: private DataOutputStream dataOutput; 1425: private boolean writeDataAsBlocks; 1426: private DataOutputStream realOutput; 1427: private DataOutputStream blockDataOutput; 1428: private byte[] blockData; 1429: private int blockDataCount; 1430: private Object currentObject; 1431: // Package-private to avoid a trampoline. 1432: ObjectStreamClass currentObjectStreamClass; 1433: private PutField currentPutField; 1434: private boolean fieldsAlreadyWritten; 1435: private boolean replacementEnabled; 1436: private boolean isSerializing; 1437: private int nextOID; 1438: private ObjectIdentityMap2Int OIDLookupTable; 1439: private int protocolVersion; 1440: private boolean useSubclassMethod; 1441: private SetAccessibleAction setAccessible = new SetAccessibleAction(); 1442: 1443: // The nesting depth for debugging output 1444: private int depth = 0; 1445: 1446: // Set if we're generating debugging dumps 1447: private boolean dump = false; 1448: 1449: private static final boolean DEBUG = false; 1450: }