1 /* ObjectInputStream.java -- Class used to read serialized objects
2 Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21 As a special exception, if you link this library with other files to
22 produce an executable, this library does not by itself cause the
23 resulting executable to be covered by the GNU General Public License.
24 This exception does not however invalidate any other reasons why the
25 executable file might be covered by the GNU General Public License. */
30 import java.lang.reflect.Array;
31 import java.lang.reflect.Modifier;
32 import java.util.Arrays;
33 import java.util.Hashtable;
34 import java.util.Vector;
36 import gnu.java.io.ObjectIdentityWrapper;
37 import gnu.java.lang.reflect.TypeSignature;
38 import java.lang.reflect.Field;
39 import java.lang.reflect.Method;
40 import java.lang.reflect.InvocationTargetException;
44 public class ObjectInputStream extends InputStream
45 implements ObjectInput, ObjectStreamConstants
48 Creates a new <code>ObjectInputStream</code> that will do all of
49 its reading from <code>in</code>. This method also checks
50 the stream by reading the header information (stream magic number
53 @exception IOException Reading stream header from underlying
54 stream cannot be completed.
56 @exception StreamCorruptedException An invalid stream magic
57 number or stream version was read from the stream.
59 @see readStreamHeader ()
61 public ObjectInputStream (InputStream in)
62 throws IOException, StreamCorruptedException
64 this.resolveEnabled = false;
65 this.isDeserializing = false;
66 this.blockDataPosition = 0;
67 this.blockDataBytes = 0;
68 this.blockData = new byte[BUFFER_SIZE];
69 this.blockDataInput = new DataInputStream (this);
70 this.realInputStream = new DataInputStream (in);
71 this.nextOID = baseWireHandle;
72 this.objectLookupTable = new Hashtable ();
73 this.validators = new Vector ();
74 setBlockDataMode (true);
80 Returns the next deserialized object read from the underlying stream.
82 This method can be overriden by a class by implementing
83 <code>private void readObject (ObjectInputStream)</code>.
85 If an exception is thrown from this method, the stream is left in
88 @exception ClassNotFoundException The class that an object being
89 read in belongs to cannot be found.
91 @exception IOException Exception from underlying
92 <code>InputStream</code>.
94 public final Object readObject () throws ClassNotFoundException, IOException
96 if (this.useSubclassMethod)
97 return readObjectOverride ();
99 boolean was_deserializing;
102 was_deserializing = this.isDeserializing;
104 if (! was_deserializing)
105 setBlockDataMode (false);
107 this.isDeserializing = true;
109 byte marker = this.realInputStream.readByte ();
110 dumpElement ("MARKER: 0x" + Integer.toHexString(marker) + " ");
115 case TC_BLOCKDATALONG:
116 if (marker == TC_BLOCKDATALONG)
117 dumpElementln ("BLOCKDATALONG");
119 dumpElementln ("BLOCKDATA");
120 readNextBlock (marker);
121 throw new BlockDataException (this.blockDataBytes);
124 dumpElementln ("NULL");
130 dumpElement ("REFERENCE ");
131 Integer oid = new Integer (this.realInputStream.readInt ());
132 dumpElementln (Integer.toHexString(oid.intValue()));
133 ret_val = ((ObjectIdentityWrapper)
134 this.objectLookupTable.get (oid)).object;
140 dumpElementln ("CLASS");
141 ObjectStreamClass osc = (ObjectStreamClass)readObject ();
142 Class clazz = osc.forClass ();
143 assignNewHandle (clazz);
150 dumpElement ("CLASSDESC NAME=");
151 String name = this.realInputStream.readUTF ();
152 dumpElement (name + "; UID=");
153 long uid = this.realInputStream.readLong ();
154 dumpElement (Long.toHexString(uid) + "; FLAGS=");
155 byte flags = this.realInputStream.readByte ();
156 dumpElement (Integer.toHexString(flags) + "; FIELD COUNT=");
157 short field_count = this.realInputStream.readShort ();
158 dumpElementln (Short.toString(field_count));
159 ObjectStreamField[] fields = new ObjectStreamField[field_count];
161 ObjectStreamClass osc = new ObjectStreamClass (name, uid,
163 assignNewHandle (osc);
165 for (int i=0; i < field_count; i++)
167 dumpElement (" TYPE CODE=");
168 char type_code = (char)this.realInputStream.readByte ();
169 dumpElement (type_code + "; FIELD NAME=");
170 String field_name = this.realInputStream.readUTF ();
171 dumpElementln (field_name);
174 if (type_code == 'L' || type_code == '[')
175 class_name = (String)readObject ();
177 class_name = String.valueOf (type_code);
180 new ObjectStreamField (field_name,
181 TypeSignature.getClassForEncoding
185 setBlockDataMode (true);
186 osc.setClass (resolveClass (osc));
187 setBlockDataMode (false);
189 if (this.realInputStream.readByte () != TC_ENDBLOCKDATA)
190 throw new IOException ("Data annotated to class was not consumed.");
191 dumpElementln ("ENDBLOCKDATA ");
193 osc.setSuperclass ((ObjectStreamClass)readObject ());
200 dumpElement ("STRING=");
201 String s = this.realInputStream.readUTF ();
203 ret_val = processResolution (s, assignNewHandle (s));
209 dumpElementln ("ARRAY");
210 ObjectStreamClass osc = (ObjectStreamClass)readObject ();
211 Class componenetType = osc.forClass ().getComponentType ();
212 dumpElement ("ARRAY LENGTH=");
213 int length = this.realInputStream.readInt ();
214 dumpElementln (length + "; COMPONENT TYPE=" + componenetType);
215 Object array = Array.newInstance (componenetType, length);
216 int handle = assignNewHandle (array);
217 readArrayElements (array, componenetType);
218 for (int i=0, len=Array.getLength(array); i < len; i++)
219 dumpElementln (" ELEMENT[" + i + "]=" + Array.get(array, i).toString());
220 ret_val = processResolution (array, handle);
226 dumpElementln ("OBJECT");
227 ObjectStreamClass osc = (ObjectStreamClass)readObject ();
228 Class clazz = osc.forClass ();
230 if (!Serializable.class.isAssignableFrom (clazz))
231 throw new NotSerializableException (clazz + " is not Serializable, and thus cannot be deserialized.");
233 if (Externalizable.class.isAssignableFrom (clazz))
235 Externalizable obj = null;
239 obj = (Externalizable)clazz.newInstance ();
241 catch (InstantiationException e)
243 throw new ClassNotFoundException ("Instance of " + clazz
244 + " could not be created");
246 catch (IllegalAccessException e)
248 throw new ClassNotFoundException ("Instance of " + clazz
249 + " could not be created because class or zero-argument constructor is not accessible");
251 catch (NoSuchMethodError e)
253 throw new ClassNotFoundException ("Instance of " + clazz
254 + " could not be created because zero-argument constructor is not defined");
257 int handle = assignNewHandle (obj);
259 boolean read_from_blocks = ((osc.getFlags () & SC_BLOCK_DATA) != 0);
261 if (read_from_blocks)
262 setBlockDataMode (true);
264 obj.readExternal (this);
266 if (read_from_blocks)
267 setBlockDataMode (false);
269 ret_val = processResolution (obj, handle);
271 } // end if (Externalizable.class.isAssignableFrom (clazz))
273 // find the first non-serializable, non-abstract
274 // class in clazz's inheritance hierarchy
275 Class first_nonserial = clazz.getSuperclass ();
276 while (Serializable.class.isAssignableFrom (first_nonserial)
277 || Modifier.isAbstract (first_nonserial.getModifiers ()))
278 first_nonserial = first_nonserial.getSuperclass ();
280 // DEBUGln ("Using " + first_nonserial
281 // + " as starting point for constructing " + clazz);
284 obj = newObject (clazz, first_nonserial);
287 throw new ClassNotFoundException ("Instance of " + clazz +
288 " could not be created");
290 int handle = assignNewHandle (obj);
291 this.currentObject = obj;
292 ObjectStreamClass[] hierarchy =
293 ObjectStreamClass.getObjectStreamClasses (clazz);
295 // DEBUGln ("Got class hierarchy of depth " + hierarchy.length);
298 for (int i=0; i < hierarchy.length; i++)
300 this.currentObjectStreamClass = hierarchy[i];
302 dumpElementln ("Reading fields of "
303 + this.currentObjectStreamClass.getName ());
309 this.currentObjectStreamClass.forClass ().
310 getDeclaredMethod ("readObject", readObjectParams);
312 catch (NoSuchMethodException e)
317 // XXX: should initialize fields in classes in the hierarchy
318 // that aren't in the stream
319 // should skip over classes in the stream that aren't in the
320 // real classes hierarchy
321 readFields (obj, this.currentObjectStreamClass.fields,
322 has_read, this.currentObjectStreamClass);
326 dumpElement ("ENDBLOCKDATA? ");
329 // FIXME: XXX: This try block is to catch EOF which is
330 // thrown for some objects. That indicates a bug in the logic.
331 if (this.realInputStream.readByte () != TC_ENDBLOCKDATA)
332 throw new IOException ("No end of block data seen for class with readObject (ObjectInputStream) method.");
333 dumpElementln ("yes");
335 catch (EOFException e)
337 dumpElementln ("no, got EOFException");
339 catch (IOException e)
341 dumpElementln ("no, got IOException");
346 this.currentObject = null;
347 this.currentObjectStreamClass = null;
348 ret_val = processResolution (obj, handle);
353 dumpElementln ("RESET");
355 ret_val = readObject ();
360 dumpElement ("EXCEPTION=");
361 Exception e = (Exception)readObject ();
362 dumpElementln (e.toString());
364 throw new WriteAbortedException ("Exception thrown during writing of stream", e);
368 throw new IOException ("Unknown marker on stream");
371 this.isDeserializing = was_deserializing;
373 if (! was_deserializing)
375 setBlockDataMode (true);
377 if (validators.size () > 0)
386 Reads the current objects non-transient, non-static fields from
387 the current class from the underlying output stream.
389 This method is intended to be called from within a object's
390 <code>private void readObject (ObjectInputStream)</code>
393 @exception ClassNotFoundException The class that an object being
394 read in belongs to cannot be found.
396 @exception NotActiveException This method was called from a
397 context other than from the current object's and current class's
398 <code>private void readObject (ObjectInputStream)</code>
401 @exception IOException Exception from underlying
402 <code>OutputStream</code>.
404 public void defaultReadObject ()
405 throws ClassNotFoundException, IOException, NotActiveException
407 if (this.currentObject == null || this.currentObjectStreamClass == null)
408 throw new NotActiveException ("defaultReadObject called by non-active class and/or object");
410 if (fieldsAlreadyRead)
411 throw new NotActiveException ("defaultReadObject called but fields already read from stream (by defaultReadObject or readFields)");
413 readFields (this.currentObject,
414 this.currentObjectStreamClass.fields,
415 false, this.currentObjectStreamClass);
417 fieldsAlreadyRead = true;
422 Registers a <code>ObjectInputValidation</code> to be carried out
423 on the object graph currently being deserialized before it is
424 returned to the original caller of <code>readObject ()</code>.
425 The order of validation for multiple
426 <code>ObjectInputValidation</code>s can be controled using
427 <code>priority</code>. Validators with higher priorities are
430 @see java.io.ObjectInputValidation
432 @exception InvalidObjectException <code>validator</code> is
435 @exception NotActiveException an attempt was made to add a
436 validator outside of the <code>readObject</code> method of the
437 object currently being deserialized
439 public void registerValidation (ObjectInputValidation validator,
441 throws InvalidObjectException, NotActiveException
443 if (this.currentObject == null || this.currentObjectStreamClass == null)
444 throw new NotActiveException ("registerValidation called by non-active class and/or object");
446 if (validator == null)
447 throw new InvalidObjectException ("attempt to add a null ObjectInputValidation object");
449 this.validators.addElement (new ValidatorAndPriority (validator,
455 Called when a class is being deserialized. This is a hook to
456 allow subclasses to read in information written by the
457 <code>annotateClass (Class)</code> method of an
458 <code>ObjectOutputStream</code>.
460 This implementation looks up the active call stack for a
461 <code>ClassLoader</code>; if a <code>ClassLoader</code> is found,
462 it is used to load the class associated with <code>osc</code>,
463 otherwise, the default system <code>ClassLoader</code> is used.
465 @exception IOException Exception from underlying
466 <code>OutputStream</code>.
468 @see java.io.ObjectOutputStream#annotateClass (java.lang.Class)
470 protected Class resolveClass (ObjectStreamClass osc)
471 throws ClassNotFoundException, IOException
473 // DEBUGln ("Resolving " + osc);
475 SecurityManager sm = System.getSecurityManager ();
478 sm = new SecurityManager () {};
480 ClassLoader cl = currentClassLoader (sm);
484 // DEBUGln ("No class loader found");
485 return Class.forName (osc.getName ());
489 // DEBUGln ("Using " + cl);
490 return cl.loadClass (osc.getName ());
496 Allows subclasses to resolve objects that are read from the
497 stream with other objects to be returned in their place. This
498 method is called the first time each object is encountered.
500 This method must be enabled before it will be called in the
501 serialization process.
503 @exception IOException Exception from underlying
504 <code>OutputStream</code>.
506 @see enableResolveObject (boolean)
508 protected Object resolveObject (Object obj) throws IOException
515 If <code>enable</code> is <code>true</code> and this object is
516 trusted, then <code>resolveObject (Object)</code> will be called
517 in subsequent calls to <code>readObject (Object)</code>.
518 Otherwise, <code>resolveObject (Object)</code> will not be called.
520 @exception SecurityException This class is not trusted.
522 protected boolean enableResolveObject (boolean enable)
523 throws SecurityException
526 if (getClass ().getClassLoader () != null)
527 throw new SecurityException ("Untrusted ObjectInputStream subclass attempted to enable object resolution");
529 boolean old_val = this.resolveEnabled;
530 this.resolveEnabled = enable;
536 Reads stream magic and stream version information from the
539 @exception IOException Exception from underlying stream.
541 @exception StreamCorruptedException An invalid stream magic
542 number or stream version was read from the stream.
544 protected void readStreamHeader ()
545 throws IOException, StreamCorruptedException
547 dumpElement ("STREAM MAGIC ");
548 if (this.realInputStream.readShort () != STREAM_MAGIC)
549 throw new StreamCorruptedException ("Invalid stream magic number");
551 dumpElementln ("STREAM VERSION ");
552 if (this.realInputStream.readShort () != STREAM_VERSION)
553 throw new StreamCorruptedException ("Invalid stream version number");
557 public int read () throws IOException
559 if (this.readDataFromBlock)
561 if (this.blockDataPosition >= this.blockDataBytes)
563 return this.blockData[this.blockDataPosition++];
566 return this.realInputStream.read ();
569 public int read (byte data[], int offset, int length) throws IOException
571 if (this.readDataFromBlock)
573 if (this.blockDataPosition + length >= this.blockDataBytes)
576 System.arraycopy (this.blockData, this.blockDataPosition,
577 data, offset, length);
581 return this.realInputStream.read (data, offset, length);
584 public int available () throws IOException
586 if (this.readDataFromBlock)
588 if (this.blockDataPosition >= this.blockDataBytes)
591 return this.blockDataBytes - this.blockDataPosition;
594 return this.realInputStream.available ();
597 public void close () throws IOException
599 this.realInputStream.close ();
602 public boolean readBoolean () throws IOException
604 return this.dataInputStream.readBoolean ();
607 public byte readByte () throws IOException
609 return this.dataInputStream.readByte ();
612 public int readUnsignedByte () throws IOException
614 return this.dataInputStream.readUnsignedByte ();
617 public short readShort () throws IOException
619 return this.dataInputStream.readShort ();
622 public int readUnsignedShort () throws IOException
624 return this.dataInputStream.readUnsignedShort ();
627 public char readChar () throws IOException
629 return this.dataInputStream.readChar ();
632 public int readInt () throws IOException
634 return this.dataInputStream.readInt ();
637 public long readLong () throws IOException
639 return this.dataInputStream.readLong ();
642 public float readFloat () throws IOException
644 return this.dataInputStream.readFloat ();
647 public double readDouble () throws IOException
649 return this.dataInputStream.readDouble ();
652 public void readFully (byte data[]) throws IOException
654 this.dataInputStream.readFully (data);
657 public void readFully (byte data[], int offset, int size)
660 this.dataInputStream.readFully (data, offset, size);
663 public int skipBytes (int len) throws IOException
665 return this.dataInputStream.skipBytes (len);
670 @see java.io.DataInputStream#readLine ()
672 public String readLine () throws IOException
674 return this.dataInputStream.readLine ();
677 public String readUTF () throws IOException
679 return this.dataInputStream.readUTF ();
684 This class allows a class to specify exactly which fields should
685 be read, and what values should be read for these fields.
687 XXX: finish up comments
689 public static abstract class GetField
691 public abstract ObjectStreamClass getObjectStreamClass ();
693 public abstract boolean defaulted (String name)
694 throws IOException, IllegalArgumentException;
696 public abstract boolean get (String name, boolean defvalue)
697 throws IOException, IllegalArgumentException;
699 public abstract char get (String name, char defvalue)
700 throws IOException, IllegalArgumentException;
702 public abstract byte get (String name, byte defvalue)
703 throws IOException, IllegalArgumentException;
705 public abstract short get (String name, short defvalue)
706 throws IOException, IllegalArgumentException;
708 public abstract int get (String name, int defvalue)
709 throws IOException, IllegalArgumentException;
711 public abstract long get (String name, long defvalue)
712 throws IOException, IllegalArgumentException;
714 public abstract float get (String name, float defvalue)
715 throws IOException, IllegalArgumentException;
717 public abstract double get (String name, double defvalue)
718 throws IOException, IllegalArgumentException;
720 public abstract Object get (String name, Object defvalue)
721 throws IOException, IllegalArgumentException;
724 public GetField readFields ()
725 throws IOException, ClassNotFoundException, NotActiveException
727 if (this.currentObject == null || this.currentObjectStreamClass == null)
728 throw new NotActiveException ("readFields called by non-active class and/or object");
730 if (fieldsAlreadyRead)
731 throw new NotActiveException ("readFields called but fields already read from stream (by defaultReadObject or readFields)");
733 final ObjectStreamClass clazz = this.currentObjectStreamClass;
734 final byte[] prim_field_data = new byte[clazz.primFieldSize];
735 final Object[] objs = new Object[clazz.objectFieldCount];
737 // Apparently Block data is not used with GetField as per
738 // empirical evidence against JDK 1.2. Also see Mauve test
739 // java.io.ObjectInputOutput.Test.GetPutField.
740 setBlockDataMode (false);
741 readFully (prim_field_data);
742 for (int i = 0; i < objs.length; ++ i)
743 objs[i] = readObject ();
744 setBlockDataMode (true);
746 return new GetField ()
748 public ObjectStreamClass getObjectStreamClass ()
753 public boolean defaulted (String name)
754 throws IOException, IllegalArgumentException
756 return clazz.getField (name) == null;
759 public boolean get (String name, boolean defvalue)
760 throws IOException, IllegalArgumentException
762 ObjectStreamField field = getField (name, Boolean.TYPE);
767 return prim_field_data[field.getOffset ()] == 0 ? false : true;
770 public char get (String name, char defvalue)
771 throws IOException, IllegalArgumentException
773 ObjectStreamField field = getField (name, Character.TYPE);
778 int off = field.getOffset ();
780 return (char)(((prim_field_data[off++] & 0xFF) << 8)
781 | (prim_field_data[off] & 0xFF));
784 public byte get (String name, byte defvalue)
785 throws IOException, IllegalArgumentException
787 ObjectStreamField field = getField (name, Byte.TYPE);
792 return prim_field_data[field.getOffset ()];
795 public short get (String name, short defvalue)
796 throws IOException, IllegalArgumentException
798 ObjectStreamField field = getField (name, Short.TYPE);
803 int off = field.getOffset ();
805 return (short)(((prim_field_data[off++] & 0xFF) << 8)
806 | (prim_field_data[off] & 0xFF));
809 public int get (String name, int defvalue)
810 throws IOException, IllegalArgumentException
812 ObjectStreamField field = getField (name, Integer.TYPE);
817 int off = field.getOffset ();
819 return ((prim_field_data[off++] & 0xFF) << 24)
820 | ((prim_field_data[off++] & 0xFF) << 16)
821 | ((prim_field_data[off++] & 0xFF) << 8)
822 | (prim_field_data[off] & 0xFF);
825 public long get (String name, long defvalue)
826 throws IOException, IllegalArgumentException
828 ObjectStreamField field = getField (name, Long.TYPE);
833 int off = field.getOffset ();
835 return (long)(((prim_field_data[off++] & 0xFF) << 56)
836 | ((prim_field_data[off++] & 0xFF) << 48)
837 | ((prim_field_data[off++] & 0xFF) << 40)
838 | ((prim_field_data[off++] & 0xFF) << 32)
839 | ((prim_field_data[off++] & 0xFF) << 24)
840 | ((prim_field_data[off++] & 0xFF) << 16)
841 | ((prim_field_data[off++] & 0xFF) << 8)
842 | (prim_field_data[off] & 0xFF));
845 public float get (String name, float defvalue)
846 throws IOException, IllegalArgumentException
848 ObjectStreamField field = getField (name, Float.TYPE);
853 int off = field.getOffset ();
855 return Float.intBitsToFloat (((prim_field_data[off++] & 0xFF) << 24)
856 | ((prim_field_data[off++] & 0xFF) << 16)
857 | ((prim_field_data[off++] & 0xFF) << 8)
858 | (prim_field_data[off] & 0xFF));
861 public double get (String name, double defvalue)
862 throws IOException, IllegalArgumentException
864 ObjectStreamField field = getField (name, Double.TYPE);
869 int off = field.getOffset ();
871 return Double.longBitsToDouble (
872 (long)(((prim_field_data[off++] & 0xFF) << 56)
873 | ((prim_field_data[off++] & 0xFF) << 48)
874 | ((prim_field_data[off++] & 0xFF) << 40)
875 | ((prim_field_data[off++] & 0xFF) << 32)
876 | ((prim_field_data[off++] & 0xFF) << 24)
877 | ((prim_field_data[off++] & 0xFF) << 16)
878 | ((prim_field_data[off++] & 0xFF) << 8)
879 | (prim_field_data[off] & 0xFF)));
882 public Object get (String name, Object defvalue)
883 throws IOException, IllegalArgumentException
885 ObjectStreamField field =
886 getField (name, defvalue == null ? null : defvalue.getClass ());
891 return objs[field.getOffset ()];
894 private ObjectStreamField getField (String name, Class type)
895 throws IllegalArgumentException
897 ObjectStreamField field = clazz.getField (name);
902 Class field_type = field.getType ();
904 if (type == field_type ||
905 (type == null && ! field_type.isPrimitive ()))
908 throw new IllegalArgumentException ("Field requested is of type "
909 + field_type.getName ()
910 + ", but requested type was "
912 "Object" : type.getName ()));
920 Protected constructor that allows subclasses to override
921 deserialization. This constructor should be called by subclasses
922 that wish to override <code>readObject (Object)</code>. This
923 method does a security check <i>NOTE: currently not
924 implemented</i>, then sets a flag that informs
925 <code>readObject (Object)</code> to call the subclasses
926 <code>readObjectOverride (Object)</code> method.
928 @see readObjectOverride (Object)
930 protected ObjectInputStream ()
931 throws IOException, SecurityException
933 SecurityManager sec_man = System.getSecurityManager ();
935 sec_man.checkPermission (SUBCLASS_IMPLEMENTATION_PERMISSION);
936 this.useSubclassMethod = true;
941 This method allows subclasses to override the default
942 de serialization mechanism provided by
943 <code>ObjectInputStream</code>. To make this method be used for
944 writing objects, subclasses must invoke the 0-argument
945 constructor on this class from there constructor.
947 @see ObjectInputStream ()
949 protected Object readObjectOverride ()
950 throws ClassNotFoundException, IOException, OptionalDataException
952 throw new IOException ("Subclass of ObjectInputStream must implement readObjectOverride");
956 // assigns the next availible handle to OBJ
957 private int assignNewHandle (Object obj)
959 this.objectLookupTable.put (new Integer (this.nextOID),
960 new ObjectIdentityWrapper (obj));
964 // DEBUG ("Assigning handle " + this.nextOID);
965 // DEBUGln (" to " + obj);
967 // catch (Throwable t) {}
969 return this.nextOID++;
973 private Object processResolution (Object obj, int handle)
976 if (obj instanceof Serializable)
981 Class classArgs[] = {};
982 m = obj.getClass ().getDeclaredMethod ("readResolve", classArgs);
983 // m can't be null by definition since an exception would
984 // have been thrown so a check for null is not needed.
985 obj = m.invoke (obj, new Object[] {});
987 catch (NoSuchMethodException ignore)
990 catch (IllegalAccessException ignore)
993 catch (InvocationTargetException ignore)
998 if (this.resolveEnabled)
999 obj = resolveObject (obj);
1001 this.objectLookupTable.put (new Integer (handle),
1002 new ObjectIdentityWrapper (obj));
1008 private void clearHandles ()
1010 this.objectLookupTable.clear ();
1011 this.nextOID = baseWireHandle;
1015 private void readNextBlock () throws IOException
1017 // DEBUGln ("In readNextBlock ");
1018 readNextBlock (this.realInputStream.readByte ());
1022 private void readNextBlock (byte marker) throws IOException
1024 if (marker == TC_BLOCKDATA)
1026 dumpElement ("BLOCK DATA SIZE=");
1027 this.blockDataBytes = this.realInputStream.readUnsignedByte ();
1028 dumpElementln (Integer.toString(this.blockDataBytes));
1030 else if (marker == TC_BLOCKDATALONG)
1032 dumpElement ("BLOCK DATA LONG SIZE=");
1033 this.blockDataBytes = this.realInputStream.readInt ();
1034 dumpElementln (Integer.toString(this.blockDataBytes));
1038 throw new EOFException ("Attempt to read primitive data, but no data block is active.");
1041 if (this.blockData.length < this.blockDataBytes)
1042 this.blockData = new byte[this.blockDataBytes];
1044 this.realInputStream.readFully (this.blockData, 0, this.blockDataBytes);
1045 this.blockDataPosition = 0;
1049 private void readArrayElements (Object array, Class clazz)
1050 throws ClassNotFoundException, IOException
1052 if (clazz.isPrimitive ())
1054 if (clazz == Boolean.TYPE)
1056 boolean[] cast_array = (boolean[])array;
1057 for (int i=0; i < cast_array.length; i++)
1058 cast_array[i] = this.realInputStream.readBoolean ();
1061 if (clazz == Byte.TYPE)
1063 byte[] cast_array = (byte[])array;
1064 for (int i=0; i < cast_array.length; i++)
1065 cast_array[i] = this.realInputStream.readByte ();
1068 if (clazz == Character.TYPE)
1070 char[] cast_array = (char[])array;
1071 for (int i=0; i < cast_array.length; i++)
1072 cast_array[i] = this.realInputStream.readChar ();
1075 if (clazz == Double.TYPE)
1077 double[] cast_array = (double[])array;
1078 for (int i=0; i < cast_array.length; i++)
1079 cast_array[i] = this.realInputStream.readDouble ();
1082 if (clazz == Float.TYPE)
1084 float[] cast_array = (float[])array;
1085 for (int i=0; i < cast_array.length; i++)
1086 cast_array[i] = this.realInputStream.readFloat ();
1089 if (clazz == Integer.TYPE)
1091 int[] cast_array = (int[])array;
1092 for (int i=0; i < cast_array.length; i++)
1093 cast_array[i] = this.realInputStream.readInt ();
1096 if (clazz == Long.TYPE)
1098 long[] cast_array = (long[])array;
1099 for (int i=0; i < cast_array.length; i++)
1100 cast_array[i] = this.realInputStream.readLong ();
1103 if (clazz == Short.TYPE)
1105 short[] cast_array = (short[])array;
1106 for (int i=0; i < cast_array.length; i++)
1107 cast_array[i] = this.realInputStream.readShort ();
1113 Object[] cast_array = (Object[])array;
1114 for (int i=0; i < cast_array.length; i++)
1115 cast_array[i] = readObject ();
1120 private void readFields (Object obj, ObjectStreamField[] stream_fields,
1121 boolean call_read_method,
1122 ObjectStreamClass stream_osc)
1123 throws ClassNotFoundException, IOException
1125 // DEBUGln ("In readFields");
1126 if (call_read_method)
1128 // DEBUGln (" call_read_method is true");
1129 fieldsAlreadyRead = false;
1130 setBlockDataMode (true);
1131 callReadMethod (obj, stream_osc.forClass ());
1132 setBlockDataMode (false);
1136 ObjectStreamField[] real_fields =
1137 ObjectStreamClass.lookup (stream_osc.forClass ()).fields;
1139 boolean default_initialize, set_value;
1140 String field_name = null;
1142 ObjectStreamField stream_field = null;
1143 ObjectStreamField real_field = null;
1147 while (stream_idx < stream_fields.length
1148 && real_idx < real_fields.length)
1150 default_initialize = false;
1153 if (stream_idx == stream_fields.length)
1154 default_initialize = true;
1157 stream_field = stream_fields[stream_idx];
1158 type = stream_field.getType ();
1161 if (real_idx == real_fields.length)
1165 real_field = real_fields[real_idx];
1166 type = real_field.getType ();
1167 field_name = real_field.getName ();
1170 if (set_value && !default_initialize)
1173 real_field.compareTo (stream_field);
1177 default_initialize = true;
1180 else if (comp_val > 0)
1192 if (type == Boolean.TYPE)
1195 default_initialize ? false : this.realInputStream.readBoolean ();
1196 if (!default_initialize && set_value)
1197 dumpElementln (" " + field_name + ": " + value);
1199 setBooleanField (obj, field_name, value);
1201 else if (type == Byte.TYPE)
1204 default_initialize ? 0 : this.realInputStream.readByte ();
1205 if (!default_initialize && set_value)
1206 dumpElementln (" " + field_name + ": " + value);
1208 setByteField (obj, field_name, value);
1210 else if (type == Character.TYPE)
1213 default_initialize ? (char)0 : this.realInputStream.readChar ();
1214 if (!default_initialize && set_value)
1215 dumpElementln (" " + field_name + ": " + value);
1217 setCharField (obj, field_name, value);
1219 else if (type == Double.TYPE)
1222 default_initialize ? 0 : this.realInputStream.readDouble ();
1223 if (!default_initialize && set_value)
1224 dumpElementln (" " + field_name + ": " + value);
1226 setDoubleField (obj, field_name, value);
1228 else if (type == Float.TYPE)
1231 default_initialize ? 0 : this.realInputStream.readFloat ();
1232 if (!default_initialize && set_value)
1233 dumpElementln (" " + field_name + ": " + value);
1235 setFloatField (obj, field_name, value);
1237 else if (type == Integer.TYPE)
1240 default_initialize ? 0 : this.realInputStream.readInt ();
1241 if (!default_initialize && set_value)
1242 dumpElementln (" " + field_name + ": " + value);
1244 setIntField (obj, field_name, value);
1246 else if (type == Long.TYPE)
1249 default_initialize ? 0 : this.realInputStream.readLong ();
1250 if (!default_initialize && set_value)
1251 dumpElementln (" " + field_name + ": " + value);
1253 setLongField (obj, field_name, value);
1255 else if (type == Short.TYPE)
1258 default_initialize ? (short)0 : this.realInputStream.readShort ();
1259 if (!default_initialize && set_value)
1260 dumpElementln (" " + field_name + ": " + value);
1262 setShortField (obj, field_name, value);
1267 default_initialize ? null : readObject ();
1269 setObjectField (obj, field_name,
1270 real_field.getTypeString (), value);
1276 // Toggles writing primitive data to block-data buffer.
1277 private void setBlockDataMode (boolean on)
1279 // DEBUGln ("Setting block data mode to " + on);
1281 this.readDataFromBlock = on;
1284 this.dataInputStream = this.blockDataInput;
1286 this.dataInputStream = this.realInputStream;
1290 // returns a new instance of REAL_CLASS that has been constructed
1291 // only to th level of CONSTRUCTOR_CLASS (a super class of REAL_CLASS)
1292 private Object newObject (Class real_class, Class constructor_class)
1296 Object obj = allocateObject (real_class);
1297 callConstructor (constructor_class, obj);
1300 catch (InstantiationException e)
1307 // runs all registered ObjectInputValidations in prioritized order
1309 private void invokeValidators () throws InvalidObjectException
1311 Object[] validators = new Object[this.validators.size ()];
1312 this.validators.copyInto (validators);
1313 Arrays.sort (validators);
1317 for (int i=0; i < validators.length; i++)
1318 ((ObjectInputValidation)validators[i]).validateObject ();
1322 this.validators.removeAllElements ();
1327 // this native method is used to get access to the protected method
1328 // of the same name in SecurityManger
1329 private static ClassLoader currentClassLoader (SecurityManager sm)
1331 // FIXME: This is too simple.
1332 return ClassLoader.getSystemClassLoader ();
1335 private static native Field getField (Class klass, String name)
1336 throws java.lang.NoSuchFieldException;
1338 private static native Method getMethod (Class klass, String name, Class args[])
1339 throws java.lang.NoSuchMethodException;
1341 private void callReadMethod (Object obj, Class klass) throws IOException
1345 Class classArgs[] = {Class.forName ("java.io.ObjectInputStream")};
1346 Method m = getMethod (klass, "readObject", classArgs);
1349 Object args[] = {this};
1350 m.invoke (obj, args);
1354 throw new IOException ();
1358 private native Object allocateObject (Class clazz)
1359 throws InstantiationException;
1361 private native void callConstructor (Class clazz, Object obj);
1363 private void setBooleanField (Object obj, String field_name,
1368 Class klass = obj.getClass ();
1369 Field f = getField (klass, field_name);
1370 f.setBoolean (obj, val);
1377 private void setByteField (Object obj, String field_name,
1382 Class klass = obj.getClass ();
1383 Field f = getField (klass, field_name);
1384 f.setByte (obj, val);
1391 private void setCharField (Object obj, String field_name,
1396 Class klass = obj.getClass ();
1397 Field f = getField (klass, field_name);
1398 f.setChar (obj, val);
1405 private void setDoubleField (Object obj, String field_name,
1410 Class klass = obj.getClass ();
1411 Field f = getField (klass, field_name);
1412 f.setDouble (obj, val);
1419 private void setFloatField (Object obj, String field_name,
1424 Class klass = obj.getClass ();
1425 Field f = getField (klass, field_name);
1426 f.setFloat (obj, val);
1433 private void setIntField (Object obj, String field_name,
1438 Class klass = obj.getClass ();
1439 Field f = getField (klass, field_name);
1440 f.setInt (obj, val);
1448 private void setLongField (Object obj, String field_name,
1453 Class klass = obj.getClass ();
1454 Field f = getField (klass, field_name);
1455 f.setLong (obj, val);
1463 private void setShortField (Object obj, String field_name,
1468 Class klass = obj.getClass ();
1469 Field f = getField (klass, field_name);
1470 f.setShort (obj, val);
1478 private void setObjectField (Object obj, String field_name, String type_code,
1483 Class klass = obj.getClass ();
1484 Field f = getField (klass, field_name);
1485 // FIXME: We should check the type_code here
1493 private static final int BUFFER_SIZE = 1024;
1494 private static final Class[] readObjectParams = { ObjectInputStream.class };
1496 private DataInputStream realInputStream;
1497 private DataInputStream dataInputStream;
1498 private DataInputStream blockDataInput;
1499 private int blockDataPosition;
1500 private int blockDataBytes;
1501 private byte[] blockData;
1502 private boolean useSubclassMethod;
1503 private int nextOID;
1504 private boolean resolveEnabled;
1505 private Hashtable objectLookupTable;
1506 private Object currentObject;
1507 private ObjectStreamClass currentObjectStreamClass;
1508 private boolean readDataFromBlock;
1509 private boolean isDeserializing;
1510 private boolean fieldsAlreadyRead;
1511 private Vector validators;
1513 private static boolean dump;
1514 public native static void setDump (boolean dump);
1515 private native void dumpElement (String msg);
1516 private native void dumpElementln (String msg);
1519 /* FIXME: These 2 methods cause a build error on i686-pc-linux-gnu.
1520 private void DEBUG (String msg)
1522 System.out.print (msg);
1526 private void DEBUGln (String msg)
1528 System.out.println (msg);
1534 // used to keep a prioritized list of object validators
1535 class ValidatorAndPriority implements Comparable
1538 ObjectInputValidation validator;
1540 ValidatorAndPriority (ObjectInputValidation validator, int priority)
1542 this.priority = priority;
1543 this.validator = validator;
1546 public int compareTo (Object o)
1548 ValidatorAndPriority vap = (ValidatorAndPriority)o;
1549 return this.priority - vap.priority;