1 /* ObjectOutputStream.java -- Class used to write serialized objects
2 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004
3 Free Software Foundation, Inc.
5 This file is part of GNU Classpath.
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)
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.
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., 59 Temple Place, Suite 330, Boston, MA
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
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. */
42 import gnu
.classpath
.Configuration
;
43 import gnu
.java
.io
.ObjectIdentityWrapper
;
44 import gnu
.java
.lang
.reflect
.TypeSignature
;
45 import gnu
.java
.security
.action
.SetAccessibleAction
;
47 import java
.lang
.reflect
.Array
;
48 import java
.lang
.reflect
.Field
;
49 import java
.lang
.reflect
.InvocationTargetException
;
50 import java
.lang
.reflect
.Method
;
51 import java
.security
.AccessController
;
52 import java
.util
.Hashtable
;
55 * An <code>ObjectOutputStream</code> can be used to write objects
56 * as well as primitive data in a platform-independent manner to an
57 * <code>OutputStream</code>.
59 * The data produced by an <code>ObjectOutputStream</code> can be read
60 * and reconstituted by an <code>ObjectInputStream</code>.
62 * <code>writeObject (Object)</code> is used to write Objects, the
63 * <code>write<type></code> methods are used to write primitive
64 * data (as in <code>DataOutputStream</code>). Strings can be written
65 * as objects or as primitive data.
67 * Not all objects can be written out using an
68 * <code>ObjectOutputStream</code>. Only those objects that are an
69 * instance of <code>java.io.Serializable</code> can be written.
71 * Using default serialization, information about the class of an
72 * object is written, all of the non-transient, non-static fields of
73 * the object are written, if any of these fields are objects, they are
74 * written out in the same manner.
76 * An object is only written out the first time it is encountered. If
77 * the object is encountered later, a reference to it is written to
78 * the underlying stream. Thus writing circular object graphs
79 * does not present a problem, nor are relationships between objects
84 * Hashtable map = new Hashtable ();
85 * map.put ("one", new Integer (1));
86 * map.put ("two", new Integer (2));
88 * ObjectOutputStream oos =
89 * new ObjectOutputStream (new FileOutputStream ("numbers"));
90 * oos.writeObject (map);
93 * ObjectInputStream ois =
94 * new ObjectInputStream (new FileInputStream ("numbers"));
95 * Hashtable newmap = (Hashtable)ois.readObject ();
97 * System.out.println (newmap);
100 * The default serialization can be overriden in two ways.
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.
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>.
115 * @see java.io.DataOutputStream
116 * @see java.io.Externalizable
117 * @see java.io.ObjectInputStream
118 * @see java.io.Serializable
120 public class ObjectOutputStream
extends OutputStream
121 implements ObjectOutput
, ObjectStreamConstants
124 * Creates a new <code>ObjectOutputStream</code> that will do all of
125 * its writing onto <code>out</code>. This method also initializes
126 * the stream by writing the header information (stream magic number
127 * and stream version).
129 * @exception IOException Writing stream header to underlying
130 * stream cannot be completed.
132 * @see #writeStreamHeader()
134 public ObjectOutputStream (OutputStream out
) throws IOException
136 realOutput
= new DataOutputStream(out
);
137 blockData
= new byte[ BUFFER_SIZE
];
139 blockDataOutput
= new DataOutputStream(this);
140 setBlockDataMode(true);
141 replacementEnabled
= false;
142 isSerializing
= false;
143 nextOID
= baseWireHandle
;
144 OIDLookupTable
= new Hashtable();
145 protocolVersion
= defaultProtocolVersion
;
146 useSubclassMethod
= false;
149 if (Configuration
.DEBUG
)
151 String val
= System
.getProperty("gcj.dumpobjects");
152 if (val
!= null && !val
.equals(""))
158 * Writes a representation of <code>obj</code> to the underlying
159 * output stream by writing out information about its class, then
160 * writing out each of the objects non-transient, non-static
161 * fields. If any of these fields are other objects,
162 * they are written out in the same manner.
164 * This method can be overriden by a class by implementing
165 * <code>private void writeObject (ObjectOutputStream)</code>.
167 * If an exception is thrown from this method, the stream is left in
168 * an undefined state.
170 * @exception NotSerializableException An attempt was made to
171 * serialize an <code>Object</code> that is not serializable.
173 * @exception InvalidClassException Somebody tried to serialize
174 * an object which is wrongly formatted.
176 * @exception IOException Exception from underlying
177 * <code>OutputStream</code>.
179 public final void writeObject(Object obj
) throws IOException
181 if (useSubclassMethod
)
184 dumpElementln ("WRITE OVERRIDE: " + obj
);
186 writeObjectOverride(obj
);
191 dumpElementln ("WRITE: " + obj
);
195 boolean was_serializing
= isSerializing
;
196 boolean old_mode
= setBlockDataMode(false);
199 isSerializing
= true;
200 boolean replaceDone
= false;
201 Object replacedObject
= null;
207 realOutput
.writeByte(TC_NULL
);
211 Integer handle
= findHandle(obj
);
214 realOutput
.writeByte(TC_REFERENCE
);
215 realOutput
.writeInt(handle
.intValue());
219 if (obj
instanceof Class
)
221 Class cl
= (Class
)obj
;
222 ObjectStreamClass osc
= ObjectStreamClass
.lookupForClassObject(cl
);
223 realOutput
.writeByte(TC_CLASS
);
224 if (!osc
.isProxyClass
)
230 realOutput
.writeByte(TC_PROXYCLASSDESC
);
231 Class
[] intfs
= cl
.getInterfaces();
232 realOutput
.writeInt(intfs
.length
);
233 for (int i
= 0; i
< intfs
.length
; i
++)
234 realOutput
.writeUTF(intfs
[i
].getName());
236 boolean oldmode
= setBlockDataMode(true);
237 annotateProxyClass(cl
);
238 setBlockDataMode(oldmode
);
239 realOutput
.writeByte(TC_ENDBLOCKDATA
);
241 writeObject(osc
.getSuper());
243 assignNewHandle(obj
);
247 if (obj
instanceof ObjectStreamClass
)
249 writeClassDescriptor((ObjectStreamClass
) obj
);
253 if ((replacementEnabled
|| obj
instanceof Serializable
)
256 replacedObject
= obj
;
258 if (obj
instanceof Serializable
)
263 Class classArgs
[] = {};
264 m
= getMethod(obj
.getClass(), "writeReplace",
266 // m can't be null by definition since an
267 // exception would have been thrown so a check
268 // for null is not needed.
269 obj
= m
.invoke(obj
, new Object
[] {});
271 catch (NoSuchMethodException ignore
)
274 catch (IllegalAccessException ignore
)
277 catch (InvocationTargetException ignore
)
282 if (replacementEnabled
)
283 obj
= replaceObject(obj
);
289 if (obj
instanceof String
)
291 realOutput
.writeByte(TC_STRING
);
292 assignNewHandle(obj
);
293 realOutput
.writeUTF((String
)obj
);
297 Class clazz
= obj
.getClass();
298 ObjectStreamClass osc
= ObjectStreamClass
.lookupForClassObject(clazz
);
300 throw new NotSerializableException(clazz
.getName());
302 if (clazz
.isArray ())
304 realOutput
.writeByte(TC_ARRAY
);
306 assignNewHandle(obj
);
307 writeArraySizeAndElements(obj
, clazz
.getComponentType());
311 realOutput
.writeByte(TC_OBJECT
);
315 assignNewHandle(replacedObject
);
317 assignNewHandle(obj
);
319 if (obj
instanceof Externalizable
)
321 if (protocolVersion
== PROTOCOL_VERSION_2
)
322 setBlockDataMode(true);
324 ((Externalizable
)obj
).writeExternal(this);
326 if (protocolVersion
== PROTOCOL_VERSION_2
)
328 setBlockDataMode(false);
329 realOutput
.writeByte(TC_ENDBLOCKDATA
);
335 if (obj
instanceof Serializable
)
337 Object prevObject
= this.currentObject
;
338 ObjectStreamClass prevObjectStreamClass
= this.currentObjectStreamClass
;
340 ObjectStreamClass
[] hierarchy
=
341 ObjectStreamClass
.getObjectStreamClasses(clazz
);
343 for (int i
= 0; i
< hierarchy
.length
; i
++)
345 currentObjectStreamClass
= hierarchy
[i
];
347 fieldsAlreadyWritten
= false;
348 if (currentObjectStreamClass
.hasWriteMethod())
351 dumpElementln ("WRITE METHOD CALLED FOR: " + obj
);
352 setBlockDataMode(true);
353 callWriteMethod(obj
, currentObjectStreamClass
);
354 setBlockDataMode(false);
355 realOutput
.writeByte(TC_ENDBLOCKDATA
);
357 dumpElementln ("WRITE ENDBLOCKDATA FOR: " + obj
);
362 dumpElementln ("WRITE FIELDS CALLED FOR: " + obj
);
363 writeFields(obj
, currentObjectStreamClass
);
367 this.currentObject
= prevObject
;
368 this.currentObjectStreamClass
= prevObjectStreamClass
;
369 currentPutField
= null;
373 throw new NotSerializableException(clazz
.getName ());
376 catch (ObjectStreamException ose
)
378 // Rethrow these are fatal.
381 catch (IOException e
)
383 realOutput
.writeByte(TC_EXCEPTION
);
386 setBlockDataMode(false);
389 if (Configuration
.DEBUG
)
391 e
.printStackTrace(System
.out
);
395 catch (IOException ioe
)
397 StreamCorruptedException ex
=
398 new StreamCorruptedException
399 (ioe
+ " thrown while exception was being written to stream.");
400 if (Configuration
.DEBUG
)
402 ex
.printStackTrace(System
.out
);
412 isSerializing
= was_serializing
;
413 setBlockDataMode(old_mode
);
417 dumpElementln ("END: " + obj
);
421 protected void writeClassDescriptor(ObjectStreamClass osc
) throws IOException
423 realOutput
.writeByte(TC_CLASSDESC
);
424 realOutput
.writeUTF(osc
.getName());
425 realOutput
.writeLong(osc
.getSerialVersionUID());
426 assignNewHandle(osc
);
428 int flags
= osc
.getFlags();
430 if (protocolVersion
== PROTOCOL_VERSION_2
431 && osc
.isExternalizable())
432 flags
|= SC_BLOCK_DATA
;
434 realOutput
.writeByte(flags
);
436 ObjectStreamField
[] fields
= osc
.fields
;
437 realOutput
.writeShort(fields
.length
);
439 ObjectStreamField field
;
440 for (int i
= 0; i
< fields
.length
; i
++)
443 realOutput
.writeByte(field
.getTypeCode ());
444 realOutput
.writeUTF(field
.getName ());
446 if (! field
.isPrimitive())
447 writeObject(field
.getTypeString());
450 boolean oldmode
= setBlockDataMode(true);
451 annotateClass(osc
.forClass());
452 setBlockDataMode(oldmode
);
453 realOutput
.writeByte(TC_ENDBLOCKDATA
);
455 if (osc
.isSerializable() || osc
.isExternalizable())
456 writeObject(osc
.getSuper());
462 * Writes the current objects non-transient, non-static fields from
463 * the current class to the underlying output stream.
465 * This method is intended to be called from within a object's
466 * <code>private void writeObject (ObjectOutputStream)</code>
469 * @exception NotActiveException This method was called from a
470 * context other than from the current object's and current class's
471 * <code>private void writeObject (ObjectOutputStream)</code>
474 * @exception IOException Exception from underlying
475 * <code>OutputStream</code>.
477 public void defaultWriteObject()
478 throws IOException
, NotActiveException
481 writeFields(currentObject
, currentObjectStreamClass
);
485 private void markFieldsWritten() throws IOException
487 if (currentObject
== null || currentObjectStreamClass
== null)
488 throw new NotActiveException
489 ("defaultWriteObject called by non-active class and/or object");
491 if (fieldsAlreadyWritten
)
492 throw new IOException
493 ("Only one of writeFields and defaultWriteObject may be called, and it may only be called once");
495 fieldsAlreadyWritten
= true;
499 * Resets stream to state equivalent to the state just after it was
502 * Causes all objects previously written to the stream to be
503 * forgotten. A notification of this reset is also written to the
506 * @exception IOException Exception from underlying
507 * <code>OutputStream</code> or reset called while serialization is
510 public void reset() throws IOException
516 private void reset(boolean internal
) throws IOException
521 throw new IOException("Reset called while serialization in progress");
523 realOutput
.writeByte(TC_RESET
);
531 * Informs this <code>ObjectOutputStream</code> to write data
532 * according to the specified protocol. There are currently two
533 * different protocols, specified by <code>PROTOCOL_VERSION_1</code>
534 * and <code>PROTOCOL_VERSION_2</code>. This implementation writes
535 * data using <code>PROTOCOL_VERSION_2</code> by default, as is done
538 * A non-portable method, <code>setDefaultProtocolVersion (int
539 * version)</code> is provided to change the default protocol
542 * For an explination of the differences beween the two protocols
543 * see XXX: the Java ObjectSerialization Specification.
545 * @exception IOException if <code>version</code> is not a valid
548 * @see #setDefaultProtocolVersion(int)
550 public void useProtocolVersion(int version
) throws IOException
552 if (version
!= PROTOCOL_VERSION_1
&& version
!= PROTOCOL_VERSION_2
)
553 throw new IOException("Invalid protocol version requested.");
555 protocolVersion
= version
;
560 * <em>GNU $classpath specific</em>
562 * Changes the default stream protocol used by all
563 * <code>ObjectOutputStream</code>s. There are currently two
564 * different protocols, specified by <code>PROTOCOL_VERSION_1</code>
565 * and <code>PROTOCOL_VERSION_2</code>. The default default is
566 * <code>PROTOCOL_VERSION_1</code>.
568 * @exception IOException if <code>version</code> is not a valid
571 * @see #useProtocolVersion(int)
573 public static void setDefaultProtocolVersion(int version
)
576 if (version
!= PROTOCOL_VERSION_1
&& version
!= PROTOCOL_VERSION_2
)
577 throw new IOException("Invalid protocol version requested.");
579 defaultProtocolVersion
= version
;
584 * An empty hook that allows subclasses to write extra information
585 * about classes to the stream. This method is called the first
586 * time each class is seen, and after all of the standard
587 * information about the class has been written.
589 * @exception IOException Exception from underlying
590 * <code>OutputStream</code>.
592 * @see ObjectInputStream#resolveClass(java.io.ObjectStreamClass)
594 protected void annotateClass(Class cl
) throws IOException
598 protected void annotateProxyClass(Class cl
) throws IOException
603 * Allows subclasses to replace objects that are written to the
604 * stream with other objects to be written in their place. This
605 * method is called the first time each object is encountered
606 * (modulo reseting of the stream).
608 * This method must be enabled before it will be called in the
609 * serialization process.
611 * @exception IOException Exception from underlying
612 * <code>OutputStream</code>.
614 * @see #enableReplaceObject(boolean)
616 protected Object
replaceObject(Object obj
) throws IOException
623 * If <code>enable</code> is <code>true</code> and this object is
624 * trusted, then <code>replaceObject (Object)</code> will be called
625 * in subsequent calls to <code>writeObject (Object)</code>.
626 * Otherwise, <code>replaceObject (Object)</code> will not be called.
628 * @exception SecurityException This class is not trusted.
630 protected boolean enableReplaceObject(boolean enable
)
631 throws SecurityException
635 SecurityManager sm
= System
.getSecurityManager();
637 sm
.checkPermission(new SerializablePermission("enableSubstitution"));
640 boolean old_val
= replacementEnabled
;
641 replacementEnabled
= enable
;
647 * Writes stream magic and stream version information to the
650 * @exception IOException Exception from underlying
651 * <code>OutputStream</code>.
653 protected void writeStreamHeader() throws IOException
655 realOutput
.writeShort(STREAM_MAGIC
);
656 realOutput
.writeShort(STREAM_VERSION
);
660 * Protected constructor that allows subclasses to override
661 * serialization. This constructor should be called by subclasses
662 * that wish to override <code>writeObject (Object)</code>. This
663 * method does a security check <i>NOTE: currently not
664 * implemented</i>, then sets a flag that informs
665 * <code>writeObject (Object)</code> to call the subclasses
666 * <code>writeObjectOverride (Object)</code> method.
668 * @see #writeObjectOverride(Object)
670 protected ObjectOutputStream() throws IOException
, SecurityException
672 SecurityManager sec_man
= System
.getSecurityManager ();
674 sec_man
.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION
);
675 useSubclassMethod
= true;
680 * This method allows subclasses to override the default
681 * serialization mechanism provided by
682 * <code>ObjectOutputStream</code>. To make this method be used for
683 * writing objects, subclasses must invoke the 0-argument
684 * constructor on this class from there constructor.
686 * @see #ObjectOutputStream()
688 * @exception NotActiveException Subclass has arranged for this
689 * method to be called, but did not implement this method.
691 protected void writeObjectOverride(Object obj
) throws NotActiveException
,
694 throw new NotActiveException
695 ("Subclass of ObjectOutputStream must implement writeObjectOverride");
700 * @see DataOutputStream#write(int)
702 public void write (int data
) throws IOException
704 if (writeDataAsBlocks
)
706 if (blockDataCount
== BUFFER_SIZE
)
709 blockData
[ blockDataCount
++ ] = (byte)data
;
712 realOutput
.write(data
);
717 * @see DataOutputStream#write(byte[])
719 public void write(byte[] b
) throws IOException
721 write(b
, 0, b
.length
);
726 * @see DataOutputStream#write(byte[],int,int)
728 public void write(byte[] b
, int off
, int len
) throws IOException
730 if (writeDataAsBlocks
)
733 throw new IndexOutOfBoundsException();
735 if (blockDataCount
+ len
< BUFFER_SIZE
)
737 System
.arraycopy(b
, off
, blockData
, blockDataCount
, len
);
738 blockDataCount
+= len
;
743 writeBlockDataHeader(len
);
744 realOutput
.write(b
, off
, len
);
748 realOutput
.write(b
, off
, len
);
753 * @see DataOutputStream#flush()
755 public void flush () throws IOException
763 * Causes the block-data buffer to be written to the underlying
764 * stream, but does not flush underlying stream.
766 * @exception IOException Exception from underlying
767 * <code>OutputStream</code>.
769 protected void drain() throws IOException
771 if (blockDataCount
== 0)
774 if (writeDataAsBlocks
)
775 writeBlockDataHeader(blockDataCount
);
776 realOutput
.write(blockData
, 0, blockDataCount
);
782 * @see java.io.DataOutputStream#close ()
784 public void close() throws IOException
792 * @see java.io.DataOutputStream#writeBoolean (boolean)
794 public void writeBoolean(boolean data
) throws IOException
796 blockDataOutput
.writeBoolean(data
);
801 * @see java.io.DataOutputStream#writeByte (int)
803 public void writeByte(int data
) throws IOException
805 blockDataOutput
.writeByte(data
);
810 * @see java.io.DataOutputStream#writeShort (int)
812 public void writeShort (int data
) throws IOException
814 blockDataOutput
.writeShort(data
);
819 * @see java.io.DataOutputStream#writeChar (int)
821 public void writeChar(int data
) throws IOException
823 blockDataOutput
.writeChar(data
);
828 * @see java.io.DataOutputStream#writeInt (int)
830 public void writeInt(int data
) throws IOException
832 blockDataOutput
.writeInt(data
);
837 * @see java.io.DataOutputStream#writeLong (long)
839 public void writeLong(long data
) throws IOException
841 blockDataOutput
.writeLong(data
);
846 * @see java.io.DataOutputStream#writeFloat (float)
848 public void writeFloat(float data
) throws IOException
850 blockDataOutput
.writeFloat(data
);
855 * @see java.io.DataOutputStream#writeDouble (double)
857 public void writeDouble(double data
) throws IOException
859 blockDataOutput
.writeDouble(data
);
864 * @see java.io.DataOutputStream#writeBytes (java.lang.String)
866 public void writeBytes(String data
) throws IOException
868 blockDataOutput
.writeBytes(data
);
873 * @see java.io.DataOutputStream#writeChars (java.lang.String)
875 public void writeChars(String data
) throws IOException
877 dataOutput
.writeChars(data
);
882 * @see java.io.DataOutputStream#writeUTF (java.lang.String)
884 public void writeUTF(String data
) throws IOException
886 dataOutput
.writeUTF(data
);
891 * This class allows a class to specify exactly which fields should
892 * be written, and what values should be written for these fields.
894 * XXX: finish up comments
896 public abstract static class PutField
898 public abstract void put (String name
, boolean value
);
899 public abstract void put (String name
, byte value
);
900 public abstract void put (String name
, char value
);
901 public abstract void put (String name
, double value
);
902 public abstract void put (String name
, float value
);
903 public abstract void put (String name
, int value
);
904 public abstract void put (String name
, long value
);
905 public abstract void put (String name
, short value
);
906 public abstract void put (String name
, Object value
);
911 public abstract void write (ObjectOutput out
) throws IOException
;
914 public PutField
putFields() throws IOException
916 if (currentPutField
!= null)
917 return currentPutField
;
919 currentPutField
= new PutField()
921 private byte[] prim_field_data
922 = new byte[currentObjectStreamClass
.primFieldSize
];
923 private Object
[] objs
924 = new Object
[currentObjectStreamClass
.objectFieldCount
];
926 private ObjectStreamField
getField (String name
)
928 ObjectStreamField field
929 = currentObjectStreamClass
.getField(name
);
932 throw new IllegalArgumentException("no such serializable field " + name
);
937 public void put(String name
, boolean value
)
939 ObjectStreamField field
= getField(name
);
941 checkType(field
, 'Z');
942 prim_field_data
[field
.getOffset ()] = (byte)(value ?
1 : 0);
945 public void put(String name
, byte value
)
947 ObjectStreamField field
= getField(name
);
949 checkType(field
, 'B');
950 prim_field_data
[field
.getOffset()] = value
;
953 public void put(String name
, char value
)
955 ObjectStreamField field
= getField(name
);
957 checkType(field
, 'C');
958 int off
= field
.getOffset();
959 prim_field_data
[off
++] = (byte)(value
>>> 8);
960 prim_field_data
[off
] = (byte)value
;
963 public void put(String name
, double value
)
965 ObjectStreamField field
= getField (name
);
967 checkType(field
, 'D');
968 int off
= field
.getOffset();
969 long l_value
= Double
.doubleToLongBits (value
);
970 prim_field_data
[off
++] = (byte)(l_value
>>> 52);
971 prim_field_data
[off
++] = (byte)(l_value
>>> 48);
972 prim_field_data
[off
++] = (byte)(l_value
>>> 40);
973 prim_field_data
[off
++] = (byte)(l_value
>>> 32);
974 prim_field_data
[off
++] = (byte)(l_value
>>> 24);
975 prim_field_data
[off
++] = (byte)(l_value
>>> 16);
976 prim_field_data
[off
++] = (byte)(l_value
>>> 8);
977 prim_field_data
[off
] = (byte)l_value
;
980 public void put(String name
, float value
)
982 ObjectStreamField field
= getField(name
);
984 checkType(field
, 'F');
985 int off
= field
.getOffset();
986 int i_value
= Float
.floatToIntBits(value
);
987 prim_field_data
[off
++] = (byte)(i_value
>>> 24);
988 prim_field_data
[off
++] = (byte)(i_value
>>> 16);
989 prim_field_data
[off
++] = (byte)(i_value
>>> 8);
990 prim_field_data
[off
] = (byte)i_value
;
993 public void put(String name
, int value
)
995 ObjectStreamField field
= getField(name
);
996 checkType(field
, 'I');
997 int off
= field
.getOffset();
998 prim_field_data
[off
++] = (byte)(value
>>> 24);
999 prim_field_data
[off
++] = (byte)(value
>>> 16);
1000 prim_field_data
[off
++] = (byte)(value
>>> 8);
1001 prim_field_data
[off
] = (byte)value
;
1004 public void put(String name
, long value
)
1006 ObjectStreamField field
= getField(name
);
1007 checkType(field
, 'J');
1008 int off
= field
.getOffset();
1009 prim_field_data
[off
++] = (byte)(value
>>> 52);
1010 prim_field_data
[off
++] = (byte)(value
>>> 48);
1011 prim_field_data
[off
++] = (byte)(value
>>> 40);
1012 prim_field_data
[off
++] = (byte)(value
>>> 32);
1013 prim_field_data
[off
++] = (byte)(value
>>> 24);
1014 prim_field_data
[off
++] = (byte)(value
>>> 16);
1015 prim_field_data
[off
++] = (byte)(value
>>> 8);
1016 prim_field_data
[off
] = (byte)value
;
1019 public void put(String name
, short value
)
1021 ObjectStreamField field
= getField(name
);
1022 checkType(field
, 'S');
1023 int off
= field
.getOffset();
1024 prim_field_data
[off
++] = (byte)(value
>>> 8);
1025 prim_field_data
[off
] = (byte)value
;
1028 public void put(String name
, Object value
)
1030 ObjectStreamField field
= getField(name
);
1032 if (value
!= null &&
1033 ! field
.getType().isAssignableFrom(value
.getClass ()))
1034 throw new IllegalArgumentException("Class " + value
.getClass() +
1035 " cannot be cast to " + field
.getType());
1036 objs
[field
.getOffset()] = value
;
1039 public void write(ObjectOutput out
) throws IOException
1041 // Apparently Block data is not used with PutField as per
1042 // empirical evidence against JDK 1.2. Also see Mauve test
1043 // java.io.ObjectInputOutput.Test.GetPutField.
1044 boolean oldmode
= setBlockDataMode(false);
1045 out
.write(prim_field_data
);
1046 for (int i
= 0; i
< objs
.length
; ++ i
)
1047 out
.writeObject(objs
[i
]);
1048 setBlockDataMode(oldmode
);
1051 private void checkType(ObjectStreamField field
, char type
)
1052 throws IllegalArgumentException
1054 if (TypeSignature
.getEncodingOfClass(field
.getType()).charAt(0)
1056 throw new IllegalArgumentException();
1061 return currentPutField
;
1065 public void writeFields() throws IOException
1067 if (currentPutField
== null)
1068 throw new NotActiveException("writeFields can only be called after putFields has been called");
1070 markFieldsWritten();
1071 currentPutField
.write(this);
1075 // write out the block-data buffer, picking the correct header
1076 // depending on the size of the buffer
1077 private void writeBlockDataHeader(int size
) throws IOException
1081 realOutput
.writeByte(TC_BLOCKDATA
);
1082 realOutput
.write(size
);
1086 realOutput
.writeByte(TC_BLOCKDATALONG
);
1087 realOutput
.writeInt(size
);
1092 // lookup the handle for OBJ, return null if OBJ doesn't have a
1094 private Integer
findHandle(Object obj
)
1096 return (Integer
)OIDLookupTable
.get(new ObjectIdentityWrapper(obj
));
1100 // assigns the next availible handle to OBJ
1101 private int assignNewHandle(Object obj
)
1103 OIDLookupTable
.put(new ObjectIdentityWrapper(obj
),
1104 new Integer(nextOID
));
1109 // resets mapping from objects to handles
1110 private void clearHandles()
1112 nextOID
= baseWireHandle
;
1113 OIDLookupTable
.clear();
1117 // write out array size followed by each element of the array
1118 private void writeArraySizeAndElements(Object array
, Class clazz
)
1121 int length
= Array
.getLength(array
);
1123 if (clazz
.isPrimitive())
1125 if (clazz
== Boolean
.TYPE
)
1127 boolean[] cast_array
= (boolean[])array
;
1128 realOutput
.writeInt (length
);
1129 for (int i
= 0; i
< length
; i
++)
1130 realOutput
.writeBoolean(cast_array
[i
]);
1133 if (clazz
== Byte
.TYPE
)
1135 byte[] cast_array
= (byte[])array
;
1136 realOutput
.writeInt(length
);
1137 realOutput
.write(cast_array
, 0, length
);
1140 if (clazz
== Character
.TYPE
)
1142 char[] cast_array
= (char[])array
;
1143 realOutput
.writeInt(length
);
1144 for (int i
= 0; i
< length
; i
++)
1145 realOutput
.writeChar(cast_array
[i
]);
1148 if (clazz
== Double
.TYPE
)
1150 double[] cast_array
= (double[])array
;
1151 realOutput
.writeInt(length
);
1152 for (int i
= 0; i
< length
; i
++)
1153 realOutput
.writeDouble(cast_array
[i
]);
1156 if (clazz
== Float
.TYPE
)
1158 float[] cast_array
= (float[])array
;
1159 realOutput
.writeInt(length
);
1160 for (int i
= 0; i
< length
; i
++)
1161 realOutput
.writeFloat(cast_array
[i
]);
1164 if (clazz
== Integer
.TYPE
)
1166 int[] cast_array
= (int[])array
;
1167 realOutput
.writeInt(length
);
1168 for (int i
= 0; i
< length
; i
++)
1169 realOutput
.writeInt(cast_array
[i
]);
1172 if (clazz
== Long
.TYPE
)
1174 long[] cast_array
= (long[])array
;
1175 realOutput
.writeInt (length
);
1176 for (int i
= 0; i
< length
; i
++)
1177 realOutput
.writeLong(cast_array
[i
]);
1180 if (clazz
== Short
.TYPE
)
1182 short[] cast_array
= (short[])array
;
1183 realOutput
.writeInt (length
);
1184 for (int i
= 0; i
< length
; i
++)
1185 realOutput
.writeShort(cast_array
[i
]);
1191 Object
[] cast_array
= (Object
[])array
;
1192 realOutput
.writeInt(length
);
1193 for (int i
= 0; i
< length
; i
++)
1194 writeObject(cast_array
[i
]);
1199 // writes out FIELDS of OBJECT for the specified ObjectStreamClass.
1200 // FIELDS are already in canonical order.
1201 private void writeFields(Object obj
, ObjectStreamClass osc
)
1204 ObjectStreamField
[] fields
= osc
.fields
;
1205 boolean oldmode
= setBlockDataMode(false);
1209 for (int i
= 0; i
< fields
.length
; i
++)
1211 field_name
= fields
[i
].getName();
1212 type
= fields
[i
].getType();
1215 dumpElementln ("WRITE FIELD: " + field_name
+ " type=" + type
);
1217 if (type
== Boolean
.TYPE
)
1218 realOutput
.writeBoolean(getBooleanField(obj
, osc
.forClass(), field_name
));
1219 else if (type
== Byte
.TYPE
)
1220 realOutput
.writeByte(getByteField(obj
, osc
.forClass(), field_name
));
1221 else if (type
== Character
.TYPE
)
1222 realOutput
.writeChar(getCharField(obj
, osc
.forClass(), field_name
));
1223 else if (type
== Double
.TYPE
)
1224 realOutput
.writeDouble(getDoubleField(obj
, osc
.forClass(), field_name
));
1225 else if (type
== Float
.TYPE
)
1226 realOutput
.writeFloat(getFloatField(obj
, osc
.forClass(), field_name
));
1227 else if (type
== Integer
.TYPE
)
1228 realOutput
.writeInt(getIntField(obj
, osc
.forClass(), field_name
));
1229 else if (type
== Long
.TYPE
)
1230 realOutput
.writeLong(getLongField(obj
, osc
.forClass(), field_name
));
1231 else if (type
== Short
.TYPE
)
1232 realOutput
.writeShort(getShortField(obj
, osc
.forClass(), field_name
));
1234 writeObject(getObjectField(obj
, osc
.forClass(), field_name
,
1235 fields
[i
].getTypeString ()));
1237 setBlockDataMode(oldmode
);
1241 // Toggles writing primitive data to block-data buffer.
1242 private boolean setBlockDataMode(boolean on
) throws IOException
1244 if (on
== writeDataAsBlocks
)
1248 boolean oldmode
= writeDataAsBlocks
;
1249 writeDataAsBlocks
= on
;
1252 dataOutput
= blockDataOutput
;
1254 dataOutput
= realOutput
;
1260 private void callWriteMethod(Object obj
, ObjectStreamClass osc
)
1263 Class klass
= osc
.forClass();
1264 currentPutField
= null;
1267 Class classArgs
[] = {ObjectOutputStream
.class};
1268 Method m
= getMethod(klass
, "writeObject", classArgs
);
1269 Object args
[] = {this};
1270 m
.invoke(obj
, args
);
1272 catch (NoSuchMethodException nsme
)
1276 catch (InvocationTargetException x
)
1278 /* Rethrow if possible. */
1279 Throwable exception
= x
.getTargetException();
1280 if (exception
instanceof RuntimeException
)
1281 throw (RuntimeException
) exception
;
1282 if (exception
instanceof IOException
)
1283 throw (IOException
) exception
;
1286 = new IOException("Exception thrown from writeObject() on " +
1287 klass
+ ": " + exception
.getClass().getName());
1288 ioe
.initCause(exception
);
1294 = new IOException("Failure invoking writeObject() on " +
1295 klass
+ ": " + x
.getClass().getName());
1301 private boolean getBooleanField(Object obj
, Class klass
, String field_name
)
1306 Field f
= getField(klass
, field_name
);
1307 boolean b
= f
.getBoolean(obj
);
1310 catch (IllegalArgumentException _
)
1312 throw new InvalidClassException
1313 ("invalid requested type for field " + field_name
+ " in class " + klass
.getName());
1315 catch (IOException e
)
1321 throw new IOException("Unexpected exception " + _
);
1325 private byte getByteField (Object obj
, Class klass
, String field_name
)
1330 Field f
= getField (klass
, field_name
);
1331 byte b
= f
.getByte (obj
);
1334 catch (IllegalArgumentException _
)
1336 throw new InvalidClassException
1337 ("invalid requested type for field " + field_name
+ " in class " + klass
.getName());
1339 catch (IOException e
)
1345 throw new IOException("Unexpected exception " + _
);
1349 private char getCharField (Object obj
, Class klass
, String field_name
)
1354 Field f
= getField (klass
, field_name
);
1355 char b
= f
.getChar (obj
);
1358 catch (IllegalArgumentException _
)
1360 throw new InvalidClassException
1361 ("invalid requested type for field " + field_name
+ " in class " + klass
.getName());
1363 catch (IOException e
)
1369 throw new IOException("Unexpected exception " + _
);
1373 private double getDoubleField (Object obj
, Class klass
, String field_name
)
1378 Field f
= getField (klass
, field_name
);
1379 double b
= f
.getDouble (obj
);
1382 catch (IllegalArgumentException _
)
1384 throw new InvalidClassException
1385 ("invalid requested type for field " + field_name
+ " in class " + klass
.getName());
1387 catch (IOException e
)
1393 throw new IOException("Unexpected exception " + _
);
1397 private float getFloatField (Object obj
, Class klass
, String field_name
)
1402 Field f
= getField (klass
, field_name
);
1403 float b
= f
.getFloat (obj
);
1406 catch (IllegalArgumentException _
)
1408 throw new InvalidClassException
1409 ("invalid requested type for field " + field_name
+ " in class " + klass
.getName());
1411 catch (IOException e
)
1417 throw new IOException("Unexpected exception " + _
);
1421 private int getIntField (Object obj
, Class klass
, String field_name
)
1426 Field f
= getField (klass
, field_name
);
1427 int b
= f
.getInt (obj
);
1430 catch (IllegalArgumentException _
)
1432 throw new InvalidClassException
1433 ("invalid requested type for field " + field_name
+ " in class " + klass
.getName());
1435 catch (IOException e
)
1441 throw new IOException("Unexpected exception " + _
);
1445 private long getLongField (Object obj
, Class klass
, String field_name
)
1450 Field f
= getField (klass
, field_name
);
1451 long b
= f
.getLong (obj
);
1454 catch (IllegalArgumentException _
)
1456 throw new InvalidClassException
1457 ("invalid requested type for field " + field_name
+ " in class " + klass
.getName());
1459 catch (IOException e
)
1465 throw new IOException("Unexpected exception " + _
);
1469 private short getShortField (Object obj
, Class klass
, String field_name
)
1474 Field f
= getField (klass
, field_name
);
1475 short b
= f
.getShort (obj
);
1478 catch (IllegalArgumentException _
)
1480 throw new InvalidClassException
1481 ("invalid requested type for field " + field_name
+ " in class " + klass
.getName());
1483 catch (IOException e
)
1489 throw new IOException("Unexpected exception " + _
);
1493 private Object
getObjectField (Object obj
, Class klass
, String field_name
,
1494 String type_code
) throws IOException
1498 Field f
= getField (klass
, field_name
);
1499 ObjectStreamField of
= new ObjectStreamField(f
.getName(), f
.getType());
1501 if (of
.getTypeString() == null ||
1502 !of
.getTypeString().equals(type_code
))
1503 throw new InvalidClassException
1504 ("invalid type code for " + field_name
+ " in class " + klass
.getName());
1506 Object o
= f
.get (obj
);
1507 // FIXME: We should check the type_code here
1510 catch (IOException e
)
1516 throw new IOException ();
1520 private Field
getField (Class klass
, String name
)
1521 throws java
.io
.InvalidClassException
1525 final Field f
= klass
.getDeclaredField(name
);
1526 setAccessible
.setMember(f
);
1527 AccessController
.doPrivileged(setAccessible
);
1530 catch (java
.lang
.NoSuchFieldException e
)
1532 throw new InvalidClassException
1533 ("no field called " + name
+ " in class " + klass
.getName());
1537 private Method
getMethod (Class klass
, String name
, Class
[] args
)
1538 throws java
.lang
.NoSuchMethodException
1540 final Method m
= klass
.getDeclaredMethod(name
, args
);
1541 setAccessible
.setMember(m
);
1542 AccessController
.doPrivileged(setAccessible
);
1546 private void dumpElementln (String msg
)
1548 for (int i
= 0; i
< depth
; i
++)
1549 System
.out
.print (" ");
1550 System
.out
.print (Thread
.currentThread() + ": ");
1551 System
.out
.println(msg
);
1554 // this value comes from 1.2 spec, but is used in 1.1 as well
1555 private static final int BUFFER_SIZE
= 1024;
1557 private static int defaultProtocolVersion
= PROTOCOL_VERSION_2
;
1559 private DataOutputStream dataOutput
;
1560 private boolean writeDataAsBlocks
;
1561 private DataOutputStream realOutput
;
1562 private DataOutputStream blockDataOutput
;
1563 private byte[] blockData
;
1564 private int blockDataCount
;
1565 private Object currentObject
;
1566 private ObjectStreamClass currentObjectStreamClass
;
1567 private PutField currentPutField
;
1568 private boolean fieldsAlreadyWritten
;
1569 private boolean replacementEnabled
;
1570 private boolean isSerializing
;
1571 private int nextOID
;
1572 private Hashtable OIDLookupTable
;
1573 private int protocolVersion
;
1574 private boolean useSubclassMethod
;
1575 private SetAccessibleAction setAccessible
= new SetAccessibleAction();
1577 // The nesting depth for debugging output
1578 private int depth
= 0;
1580 // Set if we're generating debugging dumps
1581 private boolean dump
= false;
1585 if (Configuration
.INIT_LOAD_LIBRARY
)
1587 System
.loadLibrary("javaio");