001 /* ObjectInputStream.java -- Class used to read serialized objects
002 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006
003 Free Software Foundation, Inc.
004
005 This file is part of GNU Classpath.
006
007 GNU Classpath is free software; you can redistribute it and/or modify
008 it under the terms of the GNU General Public License as published by
009 the Free Software Foundation; either version 2, or (at your option)
010 any later version.
011
012 GNU Classpath is distributed in the hope that it will be useful, but
013 WITHOUT ANY WARRANTY; without even the implied warranty of
014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 General Public License for more details.
016
017 You should have received a copy of the GNU General Public License
018 along with GNU Classpath; see the file COPYING. If not, write to the
019 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
020 02110-1301 USA.
021
022 Linking this library statically or dynamically with other modules is
023 making a combined work based on this library. Thus, the terms and
024 conditions of the GNU General Public License cover the whole
025 combination.
026
027 As a special exception, the copyright holders of this library give you
028 permission to link this library with independent modules to produce an
029 executable, regardless of the license terms of these independent
030 modules, and to copy and distribute the resulting executable under
031 terms of your choice, provided that you also meet, for each linked
032 independent module, the terms and conditions of the license of that
033 module. An independent module is a module which is not derived from
034 or based on this library. If you modify this library, you may extend
035 this exception to your version of the library, but you are not
036 obligated to do so. If you do not wish to do so, delete this
037 exception statement from your version. */
038
039
040 package java.io;
041
042 import gnu.classpath.Pair;
043 import gnu.classpath.VMStackWalker;
044
045 import java.lang.reflect.Array;
046 import java.lang.reflect.Constructor;
047 import java.lang.reflect.Field;
048 import java.lang.reflect.InvocationTargetException;
049 import java.lang.reflect.Method;
050 import java.lang.reflect.Modifier;
051 import java.lang.reflect.Proxy;
052 import java.security.AccessController;
053 import java.security.PrivilegedAction;
054 import java.util.HashMap;
055 import java.util.Hashtable;
056 import java.util.Iterator;
057 import java.util.Map;
058 import java.util.TreeSet;
059
060 /**
061 * @author Tom Tromey (tromey@redhat.com)
062 * @author Jeroen Frijters (jeroen@frijters.net)
063 * @author Guilhem Lavaux (guilhem@kaffe.org)
064 * @author Michael Koch (konqueror@gmx.de)
065 * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
066 */
067 public class ObjectInputStream extends InputStream
068 implements ObjectInput, ObjectStreamConstants
069 {
070 /**
071 * Creates a new <code>ObjectInputStream</code> that will do all of
072 * its reading from <code>in</code>. This method also checks
073 * the stream by reading the header information (stream magic number
074 * and stream version).
075 *
076 * @exception IOException Reading stream header from underlying
077 * stream cannot be completed.
078 *
079 * @exception StreamCorruptedException An invalid stream magic
080 * number or stream version was read from the stream.
081 *
082 * @see #readStreamHeader()
083 */
084 public ObjectInputStream(InputStream in)
085 throws IOException, StreamCorruptedException
086 {
087 if (DEBUG)
088 {
089 String val = System.getProperty("gcj.dumpobjects");
090 if (dump == false && val != null && !val.equals(""))
091 {
092 dump = true;
093 System.out.println ("Serialization debugging enabled");
094 }
095 else if (dump == true && (val == null || val.equals("")))
096 {
097 dump = false;
098 System.out.println ("Serialization debugging disabled");
099 }
100 }
101
102 this.resolveEnabled = false;
103 this.blockDataPosition = 0;
104 this.blockDataBytes = 0;
105 this.blockData = new byte[BUFFER_SIZE];
106 this.blockDataInput = new DataInputStream(this);
107 this.realInputStream = new DataInputStream(in);
108 this.nextOID = baseWireHandle;
109 handles = new HashMap<Integer,Pair<Boolean,Object>>();
110 this.classLookupTable = new Hashtable<Class,ObjectStreamClass>();
111 setBlockDataMode(true);
112 readStreamHeader();
113 }
114
115
116 /**
117 * Returns the next deserialized object read from the underlying stream.
118 *
119 * This method can be overriden by a class by implementing
120 * <code>private void readObject (ObjectInputStream)</code>.
121 *
122 * If an exception is thrown from this method, the stream is left in
123 * an undefined state. This method can also throw Errors and
124 * RuntimeExceptions if caused by existing readResolve() user code.
125 *
126 * @return The object read from the underlying stream.
127 *
128 * @exception ClassNotFoundException The class that an object being
129 * read in belongs to cannot be found.
130 *
131 * @exception IOException Exception from underlying
132 * <code>InputStream</code>.
133 */
134 public final Object readObject()
135 throws ClassNotFoundException, IOException
136 {
137 return readObject(true);
138 }
139
140 /**
141 * <p>
142 * Returns the next deserialized object read from the
143 * underlying stream in an unshared manner. Any object
144 * returned by this method will not be returned by
145 * subsequent calls to either this method or {@link #readObject()}.
146 * </p>
147 * <p>
148 * This behaviour is achieved by:
149 * </p>
150 * <ul>
151 * <li>Marking the handles created by successful calls to this
152 * method, so that future calls to {@link #readObject()} or
153 * {@link #readUnshared()} will throw an {@link ObjectStreamException}
154 * rather than returning the same object reference.</li>
155 * <li>Throwing an {@link ObjectStreamException} if the next
156 * element in the stream is a reference to an earlier object.</li>
157 * </ul>
158 *
159 * @return a reference to the deserialized object.
160 * @throws ClassNotFoundException if the class of the object being
161 * deserialized can not be found.
162 * @throws StreamCorruptedException if information in the stream
163 * is inconsistent.
164 * @throws ObjectStreamException if the next object has already been
165 * returned by an earlier call to this
166 * method or {@link #readObject()}.
167 * @throws OptionalDataException if primitive data occurs next in the stream.
168 * @throws IOException if an I/O error occurs from the stream.
169 * @since 1.4
170 * @see #readObject()
171 */
172 public Object readUnshared()
173 throws IOException, ClassNotFoundException
174 {
175 return readObject(false);
176 }
177
178 /**
179 * Returns the next deserialized object read from the underlying stream.
180 *
181 * This method can be overriden by a class by implementing
182 * <code>private void readObject (ObjectInputStream)</code>.
183 *
184 * If an exception is thrown from this method, the stream is left in
185 * an undefined state. This method can also throw Errors and
186 * RuntimeExceptions if caused by existing readResolve() user code.
187 *
188 * @param shared true if handles created by this call should be shared
189 * with later calls.
190 * @return The object read from the underlying stream.
191 *
192 * @exception ClassNotFoundException The class that an object being
193 * read in belongs to cannot be found.
194 *
195 * @exception IOException Exception from underlying
196 * <code>InputStream</code>.
197 */
198 private final Object readObject(boolean shared)
199 throws ClassNotFoundException, IOException
200 {
201 if (this.useSubclassMethod)
202 return readObjectOverride();
203
204 Object ret_val;
205 boolean old_mode = setBlockDataMode(false);
206 byte marker = this.realInputStream.readByte();
207
208 if (DEBUG)
209 depth += 2;
210
211 if(dump) dumpElement("MARKER: 0x" + Integer.toHexString(marker) + " ");
212
213 try
214 {
215 ret_val = parseContent(marker, shared);
216 }
217 finally
218 {
219 setBlockDataMode(old_mode);
220 if (DEBUG)
221 depth -= 2;
222 }
223
224 return ret_val;
225 }
226
227 /**
228 * Handles a content block within the stream, which begins with a marker
229 * byte indicating its type.
230 *
231 * @param marker the byte marker.
232 * @param shared true if handles created by this call should be shared
233 * with later calls.
234 * @return an object which represents the parsed content.
235 * @throws ClassNotFoundException if the class of an object being
236 * read in cannot be found.
237 * @throws IOException if invalid data occurs or one is thrown by the
238 * underlying <code>InputStream</code>.
239 */
240 private Object parseContent(byte marker, boolean shared)
241 throws ClassNotFoundException, IOException
242 {
243 Object ret_val;
244 boolean is_consumed = false;
245
246 switch (marker)
247 {
248 case TC_ENDBLOCKDATA:
249 {
250 ret_val = null;
251 is_consumed = true;
252 break;
253 }
254
255 case TC_BLOCKDATA:
256 case TC_BLOCKDATALONG:
257 {
258 if (marker == TC_BLOCKDATALONG)
259 { if(dump) dumpElementln("BLOCKDATALONG"); }
260 else
261 { if(dump) dumpElementln("BLOCKDATA"); }
262 readNextBlock(marker);
263 }
264
265 case TC_NULL:
266 {
267 if(dump) dumpElementln("NULL");
268 ret_val = null;
269 break;
270 }
271
272 case TC_REFERENCE:
273 {
274 if(dump) dumpElement("REFERENCE ");
275 int oid = realInputStream.readInt();
276 if(dump) dumpElementln(Integer.toHexString(oid));
277 ret_val = lookupHandle(oid);
278 if (!shared)
279 throw new
280 InvalidObjectException("References can not be read unshared.");
281 break;
282 }
283
284 case TC_CLASS:
285 {
286 if(dump) dumpElementln("CLASS");
287 ObjectStreamClass osc = (ObjectStreamClass)readObject();
288 Class clazz = osc.forClass();
289 assignNewHandle(clazz,shared);
290 ret_val = clazz;
291 break;
292 }
293
294 case TC_PROXYCLASSDESC:
295 {
296 if(dump) dumpElementln("PROXYCLASS");
297
298 /* GCJ LOCAL */
299 // The grammar at this point is
300 // TC_PROXYCLASSDESC newHandle proxyClassDescInfo
301 // i.e. we have to assign the handle immediately after
302 // reading the marker.
303 int handle = assignNewHandle("Dummy proxy",shared);
304 /* END GCJ LOCAL */
305
306 int n_intf = this.realInputStream.readInt();
307 String[] intfs = new String[n_intf];
308 for (int i = 0; i < n_intf; i++)
309 {
310 intfs[i] = this.realInputStream.readUTF();
311 }
312
313 boolean oldmode = setBlockDataMode(true);
314 Class cl = resolveProxyClass(intfs);
315 setBlockDataMode(oldmode);
316
317 ObjectStreamClass osc = lookupClass(cl);
318 if (osc.firstNonSerializableParentConstructor == null)
319 {
320 osc.realClassIsSerializable = true;
321 osc.fields = osc.fieldMapping = new ObjectStreamField[0];
322 try
323 {
324 osc.firstNonSerializableParentConstructor =
325 Object.class.getConstructor(new Class[0]);
326 }
327 catch (NoSuchMethodException x)
328 {
329 throw (InternalError)
330 new InternalError("Object ctor missing").initCause(x);
331 }
332 }
333 /* GCJ LOCAL */
334 rememberHandle(osc,shared,handle);
335 /* END GCJ LOCAL */
336
337 if (!is_consumed)
338 {
339 byte b = this.realInputStream.readByte();
340 if (b != TC_ENDBLOCKDATA)
341 throw new IOException("Data annotated to class was not consumed." + b);
342 }
343 else
344 is_consumed = false;
345 ObjectStreamClass superosc = (ObjectStreamClass)readObject();
346 osc.setSuperclass(superosc);
347 ret_val = osc;
348 break;
349 }
350
351 case TC_CLASSDESC:
352 {
353 ObjectStreamClass osc = readClassDescriptor();
354
355 if (!is_consumed)
356 {
357 byte b = this.realInputStream.readByte();
358 if (b != TC_ENDBLOCKDATA)
359 throw new IOException("Data annotated to class was not consumed." + b);
360 }
361 else
362 is_consumed = false;
363
364 osc.setSuperclass ((ObjectStreamClass)readObject());
365 ret_val = osc;
366 break;
367 }
368
369 case TC_STRING:
370 case TC_LONGSTRING:
371 {
372 if(dump) dumpElement("STRING=");
373 String s = this.realInputStream.readUTF();
374 if(dump) dumpElementln(s);
375 ret_val = processResolution(null, s, assignNewHandle(s,shared),
376 shared);
377 break;
378 }
379
380 case TC_ARRAY:
381 {
382 if(dump) dumpElementln("ARRAY");
383 ObjectStreamClass osc = (ObjectStreamClass)readObject();
384 Class componentType = osc.forClass().getComponentType();
385 if(dump) dumpElement("ARRAY LENGTH=");
386 int length = this.realInputStream.readInt();
387 if(dump) dumpElementln (length + "; COMPONENT TYPE=" + componentType);
388 Object array = Array.newInstance(componentType, length);
389 int handle = assignNewHandle(array,shared);
390 readArrayElements(array, componentType);
391 if(dump)
392 for (int i = 0, len = Array.getLength(array); i < len; i++)
393 dumpElementln(" ELEMENT[" + i + "]=", Array.get(array, i));
394 ret_val = processResolution(null, array, handle, shared);
395 break;
396 }
397
398 case TC_OBJECT:
399 {
400 if(dump) dumpElementln("OBJECT");
401 ObjectStreamClass osc = (ObjectStreamClass)readObject();
402 Class clazz = osc.forClass();
403
404 if (!osc.realClassIsSerializable)
405 throw new NotSerializableException
406 (clazz + " is not Serializable, and thus cannot be deserialized.");
407
408 if (osc.realClassIsExternalizable)
409 {
410 Externalizable obj = osc.newInstance();
411
412 int handle = assignNewHandle(obj,shared);
413
414 boolean read_from_blocks = ((osc.getFlags() & SC_BLOCK_DATA) != 0);
415
416 boolean oldmode = this.readDataFromBlock;
417 if (read_from_blocks)
418 setBlockDataMode(true);
419
420 obj.readExternal(this);
421
422 if (read_from_blocks)
423 {
424 setBlockDataMode(oldmode);
425 if (!oldmode)
426 if (this.realInputStream.readByte() != TC_ENDBLOCKDATA)
427 throw new IOException("No end of block data seen for class with readExternal (ObjectInputStream) method.");
428 }
429
430 ret_val = processResolution(osc, obj, handle,shared);
431 break;
432
433 } // end if (osc.realClassIsExternalizable)
434
435 Object obj = newObject(clazz, osc.firstNonSerializableParentConstructor);
436
437 int handle = assignNewHandle(obj,shared);
438 Object prevObject = this.currentObject;
439 ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass;
440 TreeSet<ValidatorAndPriority> prevObjectValidators =
441 this.currentObjectValidators;
442
443 this.currentObject = obj;
444 this.currentObjectValidators = null;
445 ObjectStreamClass[] hierarchy = hierarchy(clazz);
446
447 for (int i = 0; i < hierarchy.length; i++)
448 {
449 this.currentObjectStreamClass = hierarchy[i];
450 if(dump) dumpElementln("Reading fields of " + this.currentObjectStreamClass.getName ());
451
452 // XXX: should initialize fields in classes in the hierarchy
453 // that aren't in the stream
454 // should skip over classes in the stream that aren't in the
455 // real classes hierarchy
456
457 Method readObjectMethod = this.currentObjectStreamClass.readObjectMethod;
458 if (readObjectMethod != null)
459 {
460 fieldsAlreadyRead = false;
461 boolean oldmode = setBlockDataMode(true);
462 callReadMethod(readObjectMethod, this.currentObjectStreamClass.forClass(), obj);
463 setBlockDataMode(oldmode);
464 }
465 else
466 {
467 readFields(obj, currentObjectStreamClass);
468 }
469
470 if (this.currentObjectStreamClass.hasWriteMethod())
471 {
472 if(dump) dumpElement("ENDBLOCKDATA? ");
473 try
474 {
475 /* Read blocks until an end marker */
476 byte writeMarker = this.realInputStream.readByte();
477 while (writeMarker != TC_ENDBLOCKDATA)
478 {
479 parseContent(writeMarker, shared);
480 writeMarker = this.realInputStream.readByte();
481 }
482 if(dump) dumpElementln("yes");
483 }
484 catch (EOFException e)
485 {
486 throw (IOException) new IOException
487 ("No end of block data seen for class with readObject (ObjectInputStream) method.").initCause(e);
488 }
489 }
490 }
491
492 this.currentObject = prevObject;
493 this.currentObjectStreamClass = prevObjectStreamClass;
494 ret_val = processResolution(osc, obj, handle, shared);
495 if (currentObjectValidators != null)
496 invokeValidators();
497 this.currentObjectValidators = prevObjectValidators;
498
499 break;
500 }
501
502 case TC_RESET:
503 if(dump) dumpElementln("RESET");
504 clearHandles();
505 ret_val = readObject();
506 break;
507
508 case TC_EXCEPTION:
509 {
510 if(dump) dumpElement("EXCEPTION=");
511 Exception e = (Exception)readObject();
512 if(dump) dumpElementln(e.toString());
513 clearHandles();
514 throw new WriteAbortedException("Exception thrown during writing of stream", e);
515 }
516
517 case TC_ENUM:
518 {
519 /* TC_ENUM classDesc newHandle enumConstantName */
520 if (dump)
521 dumpElementln("ENUM=");
522 ObjectStreamClass osc = (ObjectStreamClass) readObject();
523 String constantName = (String) readObject();
524 if (dump)
525 dumpElementln("CONSTANT NAME = " + constantName);
526 Class clazz = osc.forClass();
527 Enum instance = Enum.valueOf(clazz, constantName);
528 assignNewHandle(instance,shared);
529 ret_val = instance;
530 break;
531 }
532
533 default:
534 throw new IOException("Unknown marker on stream: " + marker);
535 }
536 return ret_val;
537 }
538
539 /**
540 * This method makes a partial check of types for the fields
541 * contained given in arguments. It checks primitive types of
542 * fields1 against non primitive types of fields2. This method
543 * assumes the two lists has already been sorted according to
544 * the Java specification.
545 *
546 * @param name Name of the class owning the given fields.
547 * @param fields1 First list to check.
548 * @param fields2 Second list to check.
549 * @throws InvalidClassException if a field in fields1, which has a primitive type, is a present
550 * in the non primitive part in fields2.
551 */
552 private void checkTypeConsistency(String name, ObjectStreamField[] fields1, ObjectStreamField[] fields2)
553 throws InvalidClassException
554 {
555 int nonPrimitive = 0;
556
557 for (nonPrimitive = 0;
558 nonPrimitive < fields1.length
559 && fields1[nonPrimitive].isPrimitive(); nonPrimitive++)
560 {
561 }
562
563 if (nonPrimitive == fields1.length)
564 return;
565
566 int i = 0;
567 ObjectStreamField f1;
568 ObjectStreamField f2;
569
570 while (i < fields2.length
571 && nonPrimitive < fields1.length)
572 {
573 f1 = fields1[nonPrimitive];
574 f2 = fields2[i];
575
576 if (!f2.isPrimitive())
577 break;
578
579 int compVal = f1.getName().compareTo (f2.getName());
580
581 if (compVal < 0)
582 {
583 nonPrimitive++;
584 }
585 else if (compVal > 0)
586 {
587 i++;
588 }
589 else
590 {
591 throw new InvalidClassException
592 ("invalid field type for " + f2.getName() +
593 " in class " + name);
594 }
595 }
596 }
597
598 /**
599 * This method reads a class descriptor from the real input stream
600 * and use these data to create a new instance of ObjectStreamClass.
601 * Fields are sorted and ordered for the real read which occurs for
602 * each instance of the described class. Be aware that if you call that
603 * method you must ensure that the stream is synchronized, in the other
604 * case it may be completely desynchronized.
605 *
606 * @return A new instance of ObjectStreamClass containing the freshly
607 * created descriptor.
608 * @throws ClassNotFoundException if the required class to build the
609 * descriptor has not been found in the system.
610 * @throws IOException An input/output error occured.
611 * @throws InvalidClassException If there was a compatibility problem
612 * between the class present in the system and the serialized class.
613 */
614 protected ObjectStreamClass readClassDescriptor()
615 throws ClassNotFoundException, IOException
616 {
617 if(dump) dumpElement("CLASSDESC NAME=");
618 String name = this.realInputStream.readUTF();
619 if(dump) dumpElement(name + "; UID=");
620 long uid = this.realInputStream.readLong ();
621 if(dump) dumpElement(Long.toHexString(uid) + "; FLAGS=");
622 byte flags = this.realInputStream.readByte ();
623 if(dump) dumpElement(Integer.toHexString(flags) + "; FIELD COUNT=");
624 short field_count = this.realInputStream.readShort();
625 if(dump) dumpElementln(Short.toString(field_count));
626 ObjectStreamField[] fields = new ObjectStreamField[field_count];
627 ObjectStreamClass osc = new ObjectStreamClass(name, uid,
628 flags, fields);
629 assignNewHandle(osc,true);
630
631 for (int i = 0; i < field_count; i++)
632 {
633 if(dump) dumpElement(" TYPE CODE=");
634 char type_code = (char)this.realInputStream.readByte();
635 if(dump) dumpElement(type_code + "; FIELD NAME=");
636 String field_name = this.realInputStream.readUTF();
637 if(dump) dumpElementln(field_name);
638 String class_name;
639
640 // If the type code is an array or an object we must
641 // decode a String here. In the other case we convert
642 // the type code and pass it to ObjectStreamField.
643 // Type codes are decoded by gnu.java.lang.reflect.TypeSignature.
644 if (type_code == 'L' || type_code == '[')
645 class_name = (String)readObject();
646 else
647 class_name = String.valueOf(type_code);
648
649 fields[i] =
650 new ObjectStreamField(field_name, class_name);
651 }
652
653 /* Now that fields have been read we may resolve the class
654 * (and read annotation if needed). */
655 Class clazz = resolveClass(osc);
656 ClassLoader loader = clazz.getClassLoader();
657 for (int i = 0; i < field_count; i++)
658 {
659 fields[i].resolveType(loader);
660 }
661 boolean oldmode = setBlockDataMode(true);
662 osc.setClass(clazz, lookupClass(clazz.getSuperclass()));
663 classLookupTable.put(clazz, osc);
664 setBlockDataMode(oldmode);
665
666 // find the first non-serializable class in clazz's inheritance hierarchy
667 Class first_nonserial = clazz.getSuperclass();
668 // Maybe it is a primitive class, those don't have a super class,
669 // or Object itself. Otherwise we can keep getting the superclass
670 // till we hit the Object class, or some other non-serializable class.
671
672 if (first_nonserial == null)
673 first_nonserial = clazz;
674 else
675 while (Serializable.class.isAssignableFrom(first_nonserial))
676 first_nonserial = first_nonserial.getSuperclass();
677
678 final Class local_constructor_class = first_nonserial;
679
680 osc.firstNonSerializableParentConstructor =
681 (Constructor)AccessController.doPrivileged(new PrivilegedAction()
682 {
683 public Object run()
684 {
685 try
686 {
687 Constructor c = local_constructor_class.
688 getDeclaredConstructor(new Class[0]);
689 if (Modifier.isPrivate(c.getModifiers()))
690 return null;
691 return c;
692 }
693 catch (NoSuchMethodException e)
694 {
695 // error will be reported later, in newObject()
696 return null;
697 }
698 }
699 });
700
701 osc.realClassIsSerializable = Serializable.class.isAssignableFrom(clazz);
702 osc.realClassIsExternalizable = Externalizable.class.isAssignableFrom(clazz);
703
704 ObjectStreamField[] stream_fields = osc.fields;
705 ObjectStreamField[] real_fields = ObjectStreamClass.lookupForClassObject(clazz).fields;
706 ObjectStreamField[] fieldmapping = new ObjectStreamField[2 * Math.max(stream_fields.length, real_fields.length)];
707
708 int stream_idx = 0;
709 int real_idx = 0;
710 int map_idx = 0;
711
712 /*
713 * Check that there is no type inconsistencies between the lists.
714 * A special checking must be done for the two groups: primitive types and
715 * not primitive types.
716 */
717 checkTypeConsistency(name, real_fields, stream_fields);
718 checkTypeConsistency(name, stream_fields, real_fields);
719
720
721 while (stream_idx < stream_fields.length
722 || real_idx < real_fields.length)
723 {
724 ObjectStreamField stream_field = null;
725 ObjectStreamField real_field = null;
726
727 if (stream_idx == stream_fields.length)
728 {
729 real_field = real_fields[real_idx++];
730 }
731 else if (real_idx == real_fields.length)
732 {
733 stream_field = stream_fields[stream_idx++];
734 }
735 else
736 {
737 int comp_val =
738 real_fields[real_idx].compareTo (stream_fields[stream_idx]);
739
740 if (comp_val < 0)
741 {
742 real_field = real_fields[real_idx++];
743 }
744 else if (comp_val > 0)
745 {
746 stream_field = stream_fields[stream_idx++];
747 }
748 else
749 {
750 stream_field = stream_fields[stream_idx++];
751 real_field = real_fields[real_idx++];
752 if (stream_field.getType() != real_field.getType())
753 throw new InvalidClassException
754 ("invalid field type for " + real_field.getName() +
755 " in class " + name);
756 }
757 }
758
759 /* If some of stream_fields does not correspond to any of real_fields,
760 * or the opposite, then fieldmapping will go short.
761 */
762 if (map_idx == fieldmapping.length)
763 {
764 ObjectStreamField[] newfieldmapping =
765 new ObjectStreamField[fieldmapping.length + 2];
766 System.arraycopy(fieldmapping, 0,
767 newfieldmapping, 0, fieldmapping.length);
768 fieldmapping = newfieldmapping;
769 }
770 fieldmapping[map_idx++] = stream_field;
771 fieldmapping[map_idx++] = real_field;
772 }
773 osc.fieldMapping = fieldmapping;
774
775 return osc;
776 }
777
778 /**
779 * Reads the current objects non-transient, non-static fields from
780 * the current class from the underlying output stream.
781 *
782 * This method is intended to be called from within a object's
783 * <code>private void readObject (ObjectInputStream)</code>
784 * method.
785 *
786 * @exception ClassNotFoundException The class that an object being
787 * read in belongs to cannot be found.
788 *
789 * @exception NotActiveException This method was called from a
790 * context other than from the current object's and current class's
791 * <code>private void readObject (ObjectInputStream)</code>
792 * method.
793 *
794 * @exception IOException Exception from underlying
795 * <code>OutputStream</code>.
796 */
797 public void defaultReadObject()
798 throws ClassNotFoundException, IOException, NotActiveException
799 {
800 if (this.currentObject == null || this.currentObjectStreamClass == null)
801 throw new NotActiveException("defaultReadObject called by non-active"
802 + " class and/or object");
803
804 if (fieldsAlreadyRead)
805 throw new NotActiveException("defaultReadObject called but fields "
806 + "already read from stream (by "
807 + "defaultReadObject or readFields)");
808
809 boolean oldmode = setBlockDataMode(false);
810 readFields(this.currentObject, this.currentObjectStreamClass);
811 setBlockDataMode(oldmode);
812
813 fieldsAlreadyRead = true;
814 }
815
816
817 /**
818 * Registers a <code>ObjectInputValidation</code> to be carried out
819 * on the object graph currently being deserialized before it is
820 * returned to the original caller of <code>readObject ()</code>.
821 * The order of validation for multiple
822 * <code>ObjectInputValidation</code>s can be controled using
823 * <code>priority</code>. Validators with higher priorities are
824 * called first.
825 *
826 * @see java.io.ObjectInputValidation
827 *
828 * @exception InvalidObjectException <code>validator</code> is
829 * <code>null</code>
830 *
831 * @exception NotActiveException an attempt was made to add a
832 * validator outside of the <code>readObject</code> method of the
833 * object currently being deserialized
834 */
835 public void registerValidation(ObjectInputValidation validator,
836 int priority)
837 throws InvalidObjectException, NotActiveException
838 {
839 if (this.currentObject == null || this.currentObjectStreamClass == null)
840 throw new NotActiveException("registerValidation called by non-active "
841 + "class and/or object");
842
843 if (validator == null)
844 throw new InvalidObjectException("attempt to add a null "
845 + "ObjectInputValidation object");
846
847 if (currentObjectValidators == null)
848 currentObjectValidators = new TreeSet<ValidatorAndPriority>();
849
850 currentObjectValidators.add(new ValidatorAndPriority(validator, priority));
851 }
852
853
854 /**
855 * Called when a class is being deserialized. This is a hook to
856 * allow subclasses to read in information written by the
857 * <code>annotateClass (Class)</code> method of an
858 * <code>ObjectOutputStream</code>.
859 *
860 * This implementation looks up the active call stack for a
861 * <code>ClassLoader</code>; if a <code>ClassLoader</code> is found,
862 * it is used to load the class associated with <code>osc</code>,
863 * otherwise, the default system <code>ClassLoader</code> is used.
864 *
865 * @exception IOException Exception from underlying
866 * <code>OutputStream</code>.
867 *
868 * @see java.io.ObjectOutputStream#annotateClass (java.lang.Class)
869 */
870 protected Class<?> resolveClass(ObjectStreamClass osc)
871 throws ClassNotFoundException, IOException
872 {
873 String name = osc.getName();
874 try
875 {
876 return Class.forName(name, true, currentLoader());
877 }
878 catch(ClassNotFoundException x)
879 {
880 if (name.equals("void"))
881 return Void.TYPE;
882 else if (name.equals("boolean"))
883 return Boolean.TYPE;
884 else if (name.equals("byte"))
885 return Byte.TYPE;
886 else if (name.equals("char"))
887 return Character.TYPE;
888 else if (name.equals("short"))
889 return Short.TYPE;
890 else if (name.equals("int"))
891 return Integer.TYPE;
892 else if (name.equals("long"))
893 return Long.TYPE;
894 else if (name.equals("float"))
895 return Float.TYPE;
896 else if (name.equals("double"))
897 return Double.TYPE;
898 else
899 throw x;
900 }
901 }
902
903 /**
904 * Returns the most recent user defined ClassLoader on the execution stack
905 * or null if none is found.
906 */
907 private ClassLoader currentLoader()
908 {
909 return VMStackWalker.firstNonNullClassLoader();
910 }
911
912 /**
913 * Lookup a class stored in the local hashtable. If it is not
914 * use the global lookup function in ObjectStreamClass to build
915 * the ObjectStreamClass. This method is requested according to
916 * the behaviour detected in the JDK by Kaffe's team.
917 *
918 * @param clazz Class to lookup in the hash table or for which
919 * we must build a descriptor.
920 * @return A valid instance of ObjectStreamClass corresponding
921 * to the specified class.
922 */
923 private ObjectStreamClass lookupClass(Class clazz)
924 {
925 if (clazz == null)
926 return null;
927
928 ObjectStreamClass oclazz;
929 oclazz = (ObjectStreamClass)classLookupTable.get(clazz);
930 if (oclazz == null)
931 return ObjectStreamClass.lookup(clazz);
932 else
933 return oclazz;
934 }
935
936 /**
937 * Reconstruct class hierarchy the same way {@link
938 * java.io.ObjectStreamClass#hierarchy} does but using lookupClass
939 * instead of ObjectStreamClass.lookup.
940 *
941 * @param clazz This is the class for which we want the hierarchy.
942 *
943 * @return An array of valid {@link java.io.ObjectStreamClass} instances which
944 * represent the class hierarchy for clazz.
945 */
946 private ObjectStreamClass[] hierarchy(Class clazz)
947 {
948 ObjectStreamClass osc = lookupClass(clazz);
949
950 return osc == null ? new ObjectStreamClass[0] : osc.hierarchy();
951 }
952
953 /**
954 * Allows subclasses to resolve objects that are read from the
955 * stream with other objects to be returned in their place. This
956 * method is called the first time each object is encountered.
957 *
958 * This method must be enabled before it will be called in the
959 * serialization process.
960 *
961 * @exception IOException Exception from underlying
962 * <code>OutputStream</code>.
963 *
964 * @see #enableResolveObject(boolean)
965 */
966 protected Object resolveObject(Object obj) throws IOException
967 {
968 return obj;
969 }
970
971
972 protected Class<?> resolveProxyClass(String[] intfs)
973 throws IOException, ClassNotFoundException
974 {
975 ClassLoader cl = currentLoader();
976
977 Class<?>[] clss = new Class<?>[intfs.length];
978 if(cl == null)
979 {
980 for (int i = 0; i < intfs.length; i++)
981 clss[i] = Class.forName(intfs[i]);
982 cl = ClassLoader.getSystemClassLoader();
983 }
984 else
985 for (int i = 0; i < intfs.length; i++)
986 clss[i] = Class.forName(intfs[i], false, cl);
987 try
988 {
989 return Proxy.getProxyClass(cl, clss);
990 }
991 catch (IllegalArgumentException e)
992 {
993 throw new ClassNotFoundException(null, e);
994 }
995 }
996
997 /**
998 * If <code>enable</code> is <code>true</code> and this object is
999 * trusted, then <code>resolveObject (Object)</code> will be called
1000 * in subsequent calls to <code>readObject (Object)</code>.
1001 * Otherwise, <code>resolveObject (Object)</code> will not be called.
1002 *
1003 * @exception SecurityException This class is not trusted.
1004 */
1005 protected boolean enableResolveObject (boolean enable)
1006 throws SecurityException
1007 {
1008 if (enable)
1009 {
1010 SecurityManager sm = System.getSecurityManager();
1011 if (sm != null)
1012 sm.checkPermission(new SerializablePermission("enableSubstitution"));
1013 }
1014
1015 boolean old_val = this.resolveEnabled;
1016 this.resolveEnabled = enable;
1017 return old_val;
1018 }
1019
1020 /**
1021 * Reads stream magic and stream version information from the
1022 * underlying stream.
1023 *
1024 * @exception IOException Exception from underlying stream.
1025 *
1026 * @exception StreamCorruptedException An invalid stream magic
1027 * number or stream version was read from the stream.
1028 */
1029 protected void readStreamHeader()
1030 throws IOException, StreamCorruptedException
1031 {
1032 if(dump) dumpElement("STREAM MAGIC ");
1033 if (this.realInputStream.readShort() != STREAM_MAGIC)
1034 throw new StreamCorruptedException("Invalid stream magic number");
1035
1036 if(dump) dumpElementln("STREAM VERSION ");
1037 if (this.realInputStream.readShort() != STREAM_VERSION)
1038 throw new StreamCorruptedException("Invalid stream version number");
1039 }
1040
1041 public int read() throws IOException
1042 {
1043 if (this.readDataFromBlock)
1044 {
1045 if (this.blockDataPosition >= this.blockDataBytes)
1046 readNextBlock();
1047 return (this.blockData[this.blockDataPosition++] & 0xff);
1048 }
1049 else
1050 return this.realInputStream.read();
1051 }
1052
1053 public int read(byte[] data, int offset, int length) throws IOException
1054 {
1055 if (this.readDataFromBlock)
1056 {
1057 int remain = this.blockDataBytes - this.blockDataPosition;
1058 if (remain == 0)
1059 {
1060 readNextBlock();
1061 remain = this.blockDataBytes - this.blockDataPosition;
1062 }
1063 length = Math.min(length, remain);
1064 System.arraycopy(this.blockData, this.blockDataPosition,
1065 data, offset, length);
1066 this.blockDataPosition += length;
1067
1068 return length;
1069 }
1070 else
1071 return this.realInputStream.read(data, offset, length);
1072 }
1073
1074 public int available() throws IOException
1075 {
1076 if (this.readDataFromBlock)
1077 {
1078 if (this.blockDataPosition >= this.blockDataBytes)
1079 readNextBlock ();
1080
1081 return this.blockDataBytes - this.blockDataPosition;
1082 }
1083 else
1084 return this.realInputStream.available();
1085 }
1086
1087 public void close() throws IOException
1088 {
1089 this.realInputStream.close();
1090 }
1091
1092 public boolean readBoolean() throws IOException
1093 {
1094 boolean switchmode = true;
1095 boolean oldmode = this.readDataFromBlock;
1096 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1)
1097 switchmode = false;
1098 if (switchmode)
1099 oldmode = setBlockDataMode (true);
1100 boolean value = this.dataInputStream.readBoolean ();
1101 if (switchmode)
1102 setBlockDataMode (oldmode);
1103 return value;
1104 }
1105
1106 public byte readByte() throws IOException
1107 {
1108 boolean switchmode = true;
1109 boolean oldmode = this.readDataFromBlock;
1110 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1)
1111 switchmode = false;
1112 if (switchmode)
1113 oldmode = setBlockDataMode(true);
1114 byte value = this.dataInputStream.readByte();
1115 if (switchmode)
1116 setBlockDataMode(oldmode);
1117 return value;
1118 }
1119
1120 public int readUnsignedByte() throws IOException
1121 {
1122 boolean switchmode = true;
1123 boolean oldmode = this.readDataFromBlock;
1124 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1)
1125 switchmode = false;
1126 if (switchmode)
1127 oldmode = setBlockDataMode(true);
1128 int value = this.dataInputStream.readUnsignedByte();
1129 if (switchmode)
1130 setBlockDataMode(oldmode);
1131 return value;
1132 }
1133
1134 public short readShort() throws IOException
1135 {
1136 boolean switchmode = true;
1137 boolean oldmode = this.readDataFromBlock;
1138 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2)
1139 switchmode = false;
1140 if (switchmode)
1141 oldmode = setBlockDataMode(true);
1142 short value = this.dataInputStream.readShort();
1143 if (switchmode)
1144 setBlockDataMode(oldmode);
1145 return value;
1146 }
1147
1148 public int readUnsignedShort() throws IOException
1149 {
1150 boolean switchmode = true;
1151 boolean oldmode = this.readDataFromBlock;
1152 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2)
1153 switchmode = false;
1154 if (switchmode)
1155 oldmode = setBlockDataMode(true);
1156 int value = this.dataInputStream.readUnsignedShort();
1157 if (switchmode)
1158 setBlockDataMode(oldmode);
1159 return value;
1160 }
1161
1162 public char readChar() throws IOException
1163 {
1164 boolean switchmode = true;
1165 boolean oldmode = this.readDataFromBlock;
1166 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2)
1167 switchmode = false;
1168 if (switchmode)
1169 oldmode = setBlockDataMode(true);
1170 char value = this.dataInputStream.readChar();
1171 if (switchmode)
1172 setBlockDataMode(oldmode);
1173 return value;
1174 }
1175
1176 public int readInt() throws IOException
1177 {
1178 boolean switchmode = true;
1179 boolean oldmode = this.readDataFromBlock;
1180 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4)
1181 switchmode = false;
1182 if (switchmode)
1183 oldmode = setBlockDataMode(true);
1184 int value = this.dataInputStream.readInt();
1185 if (switchmode)
1186 setBlockDataMode(oldmode);
1187 return value;
1188 }
1189
1190 public long readLong() throws IOException
1191 {
1192 boolean switchmode = true;
1193 boolean oldmode = this.readDataFromBlock;
1194 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8)
1195 switchmode = false;
1196 if (switchmode)
1197 oldmode = setBlockDataMode(true);
1198 long value = this.dataInputStream.readLong();
1199 if (switchmode)
1200 setBlockDataMode(oldmode);
1201 return value;
1202 }
1203
1204 public float readFloat() throws IOException
1205 {
1206 boolean switchmode = true;
1207 boolean oldmode = this.readDataFromBlock;
1208 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4)
1209 switchmode = false;
1210 if (switchmode)
1211 oldmode = setBlockDataMode(true);
1212 float value = this.dataInputStream.readFloat();
1213 if (switchmode)
1214 setBlockDataMode(oldmode);
1215 return value;
1216 }
1217
1218 public double readDouble() throws IOException
1219 {
1220 boolean switchmode = true;
1221 boolean oldmode = this.readDataFromBlock;
1222 if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8)
1223 switchmode = false;
1224 if (switchmode)
1225 oldmode = setBlockDataMode(true);
1226 double value = this.dataInputStream.readDouble();
1227 if (switchmode)
1228 setBlockDataMode(oldmode);
1229 return value;
1230 }
1231
1232 public void readFully(byte data[]) throws IOException
1233 {
1234 this.dataInputStream.readFully(data);
1235 }
1236
1237 public void readFully(byte data[], int offset, int size)
1238 throws IOException
1239 {
1240 this.dataInputStream.readFully(data, offset, size);
1241 }
1242
1243 public int skipBytes(int len) throws IOException
1244 {
1245 return this.dataInputStream.skipBytes(len);
1246 }
1247
1248 /**
1249 * @deprecated
1250 * @see java.io.DataInputStream#readLine ()
1251 */
1252 public String readLine() throws IOException
1253 {
1254 return this.dataInputStream.readLine();
1255 }
1256
1257 public String readUTF() throws IOException
1258 {
1259 return this.dataInputStream.readUTF();
1260 }
1261
1262 /**
1263 * This class allows a class to specify exactly which fields should
1264 * be read, and what values should be read for these fields.
1265 *
1266 * XXX: finish up comments
1267 */
1268 public abstract static class GetField
1269 {
1270 public abstract ObjectStreamClass getObjectStreamClass();
1271
1272 public abstract boolean defaulted(String name)
1273 throws IOException, IllegalArgumentException;
1274
1275 public abstract boolean get(String name, boolean defvalue)
1276 throws IOException, IllegalArgumentException;
1277
1278 public abstract char get(String name, char defvalue)
1279 throws IOException, IllegalArgumentException;
1280
1281 public abstract byte get(String name, byte defvalue)
1282 throws IOException, IllegalArgumentException;
1283
1284 public abstract short get(String name, short defvalue)
1285 throws IOException, IllegalArgumentException;
1286
1287 public abstract int get(String name, int defvalue)
1288 throws IOException, IllegalArgumentException;
1289
1290 public abstract long get(String name, long defvalue)
1291 throws IOException, IllegalArgumentException;
1292
1293 public abstract float get(String name, float defvalue)
1294 throws IOException, IllegalArgumentException;
1295
1296 public abstract double get(String name, double defvalue)
1297 throws IOException, IllegalArgumentException;
1298
1299 public abstract Object get(String name, Object defvalue)
1300 throws IOException, IllegalArgumentException;
1301 }
1302
1303 /**
1304 * This method should be called by a method called 'readObject' in the
1305 * deserializing class (if present). It cannot (and should not)be called
1306 * outside of it. Its goal is to read all fields in the real input stream
1307 * and keep them accessible through the {@link GetField} class. Calling
1308 * this method will not alter the deserializing object.
1309 *
1310 * @return A valid freshly created 'GetField' instance to get access to
1311 * the deserialized stream.
1312 * @throws IOException An input/output exception occured.
1313 * @throws ClassNotFoundException
1314 * @throws NotActiveException
1315 */
1316 public GetField readFields()
1317 throws IOException, ClassNotFoundException, NotActiveException
1318 {
1319 if (this.currentObject == null || this.currentObjectStreamClass == null)
1320 throw new NotActiveException("readFields called by non-active class and/or object");
1321
1322 if (prereadFields != null)
1323 return prereadFields;
1324
1325 if (fieldsAlreadyRead)
1326 throw new NotActiveException("readFields called but fields already read from"
1327 + " stream (by defaultReadObject or readFields)");
1328
1329 final ObjectStreamClass clazz = this.currentObjectStreamClass;
1330 final byte[] prim_field_data = new byte[clazz.primFieldSize];
1331 final Object[] objs = new Object[clazz.objectFieldCount];
1332
1333 // Apparently Block data is not used with GetField as per
1334 // empirical evidence against JDK 1.2. Also see Mauve test
1335 // java.io.ObjectInputOutput.Test.GetPutField.
1336 boolean oldmode = setBlockDataMode(false);
1337 readFully(prim_field_data);
1338 for (int i = 0; i < objs.length; ++ i)
1339 objs[i] = readObject();
1340 setBlockDataMode(oldmode);
1341
1342 prereadFields = new GetField()
1343 {
1344 public ObjectStreamClass getObjectStreamClass()
1345 {
1346 return clazz;
1347 }
1348
1349 public boolean defaulted(String name)
1350 throws IOException, IllegalArgumentException
1351 {
1352 ObjectStreamField f = clazz.getField(name);
1353
1354 /* First if we have a serialized field use the descriptor */
1355 if (f != null)
1356 {
1357 /* It is in serialPersistentFields but setClass tells us
1358 * it should not be set. This value is defaulted.
1359 */
1360 if (f.isPersistent() && !f.isToSet())
1361 return true;
1362
1363 return false;
1364 }
1365
1366 /* This is not a serialized field. There should be
1367 * a default value only if the field really exists.
1368 */
1369 try
1370 {
1371 return (clazz.forClass().getDeclaredField (name) != null);
1372 }
1373 catch (NoSuchFieldException e)
1374 {
1375 throw new IllegalArgumentException(e);
1376 }
1377 }
1378
1379 public boolean get(String name, boolean defvalue)
1380 throws IOException, IllegalArgumentException
1381 {
1382 ObjectStreamField field = getField(name, Boolean.TYPE);
1383
1384 if (field == null)
1385 return defvalue;
1386
1387 return prim_field_data[field.getOffset()] == 0 ? false : true;
1388 }
1389
1390 public char get(String name, char defvalue)
1391 throws IOException, IllegalArgumentException
1392 {
1393 ObjectStreamField field = getField(name, Character.TYPE);
1394
1395 if (field == null)
1396 return defvalue;
1397
1398 int off = field.getOffset();
1399
1400 return (char)(((prim_field_data[off++] & 0xFF) << 8)
1401 | (prim_field_data[off] & 0xFF));
1402 }
1403
1404 public byte get(String name, byte defvalue)
1405 throws IOException, IllegalArgumentException
1406 {
1407 ObjectStreamField field = getField(name, Byte.TYPE);
1408
1409 if (field == null)
1410 return defvalue;
1411
1412 return prim_field_data[field.getOffset()];
1413 }
1414
1415 public short get(String name, short defvalue)
1416 throws IOException, IllegalArgumentException
1417 {
1418 ObjectStreamField field = getField(name, Short.TYPE);
1419
1420 if (field == null)
1421 return defvalue;
1422
1423 int off = field.getOffset();
1424
1425 return (short)(((prim_field_data[off++] & 0xFF) << 8)
1426 | (prim_field_data[off] & 0xFF));
1427 }
1428
1429 public int get(String name, int defvalue)
1430 throws IOException, IllegalArgumentException
1431 {
1432 ObjectStreamField field = getField(name, Integer.TYPE);
1433
1434 if (field == null)
1435 return defvalue;
1436
1437 int off = field.getOffset();
1438
1439 return ((prim_field_data[off++] & 0xFF) << 24)
1440 | ((prim_field_data[off++] & 0xFF) << 16)
1441 | ((prim_field_data[off++] & 0xFF) << 8)
1442 | (prim_field_data[off] & 0xFF);
1443 }
1444
1445 public long get(String name, long defvalue)
1446 throws IOException, IllegalArgumentException
1447 {
1448 ObjectStreamField field = getField(name, Long.TYPE);
1449
1450 if (field == null)
1451 return defvalue;
1452
1453 int off = field.getOffset();
1454
1455 return (long)(((prim_field_data[off++] & 0xFFL) << 56)
1456 | ((prim_field_data[off++] & 0xFFL) << 48)
1457 | ((prim_field_data[off++] & 0xFFL) << 40)
1458 | ((prim_field_data[off++] & 0xFFL) << 32)
1459 | ((prim_field_data[off++] & 0xFF) << 24)
1460 | ((prim_field_data[off++] & 0xFF) << 16)
1461 | ((prim_field_data[off++] & 0xFF) << 8)
1462 | (prim_field_data[off] & 0xFF));
1463 }
1464
1465 public float get(String name, float defvalue)
1466 throws IOException, IllegalArgumentException
1467 {
1468 ObjectStreamField field = getField(name, Float.TYPE);
1469
1470 if (field == null)
1471 return defvalue;
1472
1473 int off = field.getOffset();
1474
1475 return Float.intBitsToFloat(((prim_field_data[off++] & 0xFF) << 24)
1476 | ((prim_field_data[off++] & 0xFF) << 16)
1477 | ((prim_field_data[off++] & 0xFF) << 8)
1478 | (prim_field_data[off] & 0xFF));
1479 }
1480
1481 public double get(String name, double defvalue)
1482 throws IOException, IllegalArgumentException
1483 {
1484 ObjectStreamField field = getField(name, Double.TYPE);
1485
1486 if (field == null)
1487 return defvalue;
1488
1489 int off = field.getOffset();
1490
1491 return Double.longBitsToDouble
1492 ( (long) (((prim_field_data[off++] & 0xFFL) << 56)
1493 | ((prim_field_data[off++] & 0xFFL) << 48)
1494 | ((prim_field_data[off++] & 0xFFL) << 40)
1495 | ((prim_field_data[off++] & 0xFFL) << 32)
1496 | ((prim_field_data[off++] & 0xFF) << 24)
1497 | ((prim_field_data[off++] & 0xFF) << 16)
1498 | ((prim_field_data[off++] & 0xFF) << 8)
1499 | (prim_field_data[off] & 0xFF)));
1500 }
1501
1502 public Object get(String name, Object defvalue)
1503 throws IOException, IllegalArgumentException
1504 {
1505 ObjectStreamField field =
1506 getField(name, defvalue == null ? null : defvalue.getClass ());
1507
1508 if (field == null)
1509 return defvalue;
1510
1511 return objs[field.getOffset()];
1512 }
1513
1514 private ObjectStreamField getField(String name, Class type)
1515 throws IllegalArgumentException
1516 {
1517 ObjectStreamField field = clazz.getField(name);
1518 boolean illegal = false;
1519
1520 // XXX This code is horrible and needs to be rewritten!
1521 try
1522 {
1523 try
1524 {
1525 Class field_type = field.getType();
1526
1527 if (type == field_type ||
1528 (type == null && !field_type.isPrimitive()))
1529 {
1530 /* See defaulted */
1531 return field;
1532 }
1533
1534 illegal = true;
1535 throw new IllegalArgumentException
1536 ("Field requested is of type "
1537 + field_type.getName()
1538 + ", but requested type was "
1539 + (type == null ? "Object" : type.getName()));
1540 }
1541 catch (NullPointerException _)
1542 {
1543 /* Here we catch NullPointerException, because it may
1544 only come from the call 'field.getType()'. If field
1545 is null, we have to return null and classpath ethic
1546 say we must try to avoid 'if (xxx == null)'.
1547 */
1548 }
1549 catch (IllegalArgumentException e)
1550 {
1551 throw e;
1552 }
1553
1554 return null;
1555 }
1556 finally
1557 {
1558 /* If this is an unassigned field we should return
1559 * the default value.
1560 */
1561 if (!illegal && field != null && !field.isToSet() && field.isPersistent())
1562 return null;
1563
1564 /* We do not want to modify transient fields. They should
1565 * be left to 0.
1566 */
1567 try
1568 {
1569 Field f = clazz.forClass().getDeclaredField(name);
1570 if (Modifier.isTransient(f.getModifiers()))
1571 throw new IllegalArgumentException
1572 ("no such field (non transient) " + name);
1573 if (field == null && f.getType() != type)
1574 throw new IllegalArgumentException
1575 ("Invalid requested type for field " + name);
1576 }
1577 catch (NoSuchFieldException e)
1578 {
1579 if (field == null)
1580 throw new IllegalArgumentException(e);
1581 }
1582
1583 }
1584 }
1585 };
1586
1587 fieldsAlreadyRead = true;
1588 return prereadFields;
1589 }
1590
1591 /**
1592 * Protected constructor that allows subclasses to override
1593 * deserialization. This constructor should be called by subclasses
1594 * that wish to override <code>readObject (Object)</code>. This
1595 * method does a security check <i>NOTE: currently not
1596 * implemented</i>, then sets a flag that informs
1597 * <code>readObject (Object)</code> to call the subclasses
1598 * <code>readObjectOverride (Object)</code> method.
1599 *
1600 * @see #readObjectOverride()
1601 */
1602 protected ObjectInputStream()
1603 throws IOException, SecurityException
1604 {
1605 SecurityManager sec_man = System.getSecurityManager();
1606 if (sec_man != null)
1607 sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
1608 this.useSubclassMethod = true;
1609 }
1610
1611 /**
1612 * This method allows subclasses to override the default
1613 * de serialization mechanism provided by
1614 * <code>ObjectInputStream</code>. To make this method be used for
1615 * writing objects, subclasses must invoke the 0-argument
1616 * constructor on this class from their constructor.
1617 *
1618 * @see #ObjectInputStream()
1619 */
1620 protected Object readObjectOverride()
1621 throws ClassNotFoundException, IOException, OptionalDataException
1622 {
1623 throw new IOException("Subclass of ObjectInputStream must implement readObjectOverride");
1624 }
1625
1626 /**
1627 * Assigns the next available handle to <code>obj</code>.
1628 *
1629 * @param obj The object for which we want a new handle.
1630 * @param shared True if the handle should be shared
1631 * with later calls.
1632 * @return A valid handle for the specified object.
1633 */
1634 private int assignNewHandle(Object obj, boolean shared)
1635 {
1636 int handle = this.nextOID;
1637 this.nextOID = handle + 1;
1638 rememberHandle(obj,shared,handle);
1639 return handle;
1640 }
1641
1642 /**
1643 * Remember the object associated with the given handle.
1644 *
1645 * @param obj an object
1646 * @param shared true if the reference should be shared
1647 * with later calls.
1648 * @param handle a handle, must be >= baseWireHandle
1649 *
1650 * @see #lookupHandle
1651 */
1652 private void rememberHandle(Object obj, boolean shared,
1653 int handle)
1654 {
1655 handles.put(handle, new Pair<Boolean,Object>(shared, obj));
1656 }
1657
1658 /**
1659 * Look up the object associated with a given handle.
1660 *
1661 * @param handle a handle, must be >= baseWireHandle
1662 * @return the object remembered for handle or null if none.
1663 * @throws StreamCorruptedException if the handle is invalid.
1664 * @throws InvalidObjectException if the reference is not shared.
1665 * @see #rememberHandle
1666 */
1667 private Object lookupHandle(int handle)
1668 throws ObjectStreamException
1669 {
1670 Pair<Boolean,Object> result = handles.get(handle);
1671 if (result == null)
1672 throw new StreamCorruptedException("The handle, " +
1673 Integer.toHexString(handle) +
1674 ", is invalid.");
1675 if (!result.getLeft())
1676 throw new InvalidObjectException("The handle, " +
1677 Integer.toHexString(handle) +
1678 ", is not shared.");
1679 return result.getRight();
1680 }
1681
1682 private Object processResolution(ObjectStreamClass osc, Object obj, int handle,
1683 boolean shared)
1684 throws IOException
1685 {
1686 if (osc != null && obj instanceof Serializable)
1687 {
1688 try
1689 {
1690 Method m = osc.readResolveMethod;
1691 if(m != null)
1692 {
1693 obj = m.invoke(obj, new Object[] {});
1694 }
1695 }
1696 catch (IllegalAccessException ignore)
1697 {
1698 }
1699 catch (InvocationTargetException exception)
1700 {
1701 Throwable cause = exception.getCause();
1702 if (cause instanceof ObjectStreamException)
1703 throw (ObjectStreamException) cause;
1704 else if (cause instanceof RuntimeException)
1705 throw (RuntimeException) cause;
1706 else if (cause instanceof Error)
1707 throw (Error) cause;
1708 }
1709 }
1710
1711 if (this.resolveEnabled)
1712 obj = resolveObject(obj);
1713
1714 rememberHandle(obj, shared, handle);
1715 if (!shared)
1716 {
1717 if (obj instanceof byte[])
1718 return ((byte[]) obj).clone();
1719 if (obj instanceof short[])
1720 return ((short[]) obj).clone();
1721 if (obj instanceof int[])
1722 return ((int[]) obj).clone();
1723 if (obj instanceof long[])
1724 return ((long[]) obj).clone();
1725 if (obj instanceof char[])
1726 return ((char[]) obj).clone();
1727 if (obj instanceof boolean[])
1728 return ((boolean[]) obj).clone();
1729 if (obj instanceof float[])
1730 return ((float[]) obj).clone();
1731 if (obj instanceof double[])
1732 return ((double[]) obj).clone();
1733 if (obj instanceof Object[])
1734 return ((Object[]) obj).clone();
1735 }
1736 return obj;
1737 }
1738
1739 private void clearHandles()
1740 {
1741 handles.clear();
1742 this.nextOID = baseWireHandle;
1743 }
1744
1745 private void readNextBlock() throws IOException
1746 {
1747 byte marker = this.realInputStream.readByte();
1748 while (marker == TC_RESET)
1749 {
1750 if(dump) dumpElementln("RESET");
1751 clearHandles();
1752 marker = this.realInputStream.readByte();
1753 }
1754 readNextBlock(marker);
1755 }
1756
1757 private void readNextBlock(byte marker) throws IOException
1758 {
1759 if (marker == TC_BLOCKDATA)
1760 {
1761 if(dump) dumpElement("BLOCK DATA SIZE=");
1762 this.blockDataBytes = this.realInputStream.readUnsignedByte();
1763 if(dump) dumpElementln (Integer.toString(this.blockDataBytes));
1764 }
1765 else if (marker == TC_BLOCKDATALONG)
1766 {
1767 if(dump) dumpElement("BLOCK DATA LONG SIZE=");
1768 this.blockDataBytes = this.realInputStream.readInt();
1769 if(dump) dumpElementln (Integer.toString(this.blockDataBytes));
1770 }
1771 else
1772 {
1773 throw new EOFException("Attempt to read primitive data, but no data block is active.");
1774 }
1775
1776 if (this.blockData.length < this.blockDataBytes)
1777 this.blockData = new byte[this.blockDataBytes];
1778
1779 this.realInputStream.readFully (this.blockData, 0, this.blockDataBytes);
1780 this.blockDataPosition = 0;
1781 }
1782
1783 private void readArrayElements (Object array, Class clazz)
1784 throws ClassNotFoundException, IOException
1785 {
1786 if (clazz.isPrimitive())
1787 {
1788 if (clazz == Boolean.TYPE)
1789 {
1790 boolean[] cast_array = (boolean[])array;
1791 for (int i=0; i < cast_array.length; i++)
1792 cast_array[i] = this.realInputStream.readBoolean();
1793 return;
1794 }
1795 if (clazz == Byte.TYPE)
1796 {
1797 byte[] cast_array = (byte[])array;
1798 for (int i=0; i < cast_array.length; i++)
1799 cast_array[i] = this.realInputStream.readByte();
1800 return;
1801 }
1802 if (clazz == Character.TYPE)
1803 {
1804 char[] cast_array = (char[])array;
1805 for (int i=0; i < cast_array.length; i++)
1806 cast_array[i] = this.realInputStream.readChar();
1807 return;
1808 }
1809 if (clazz == Double.TYPE)
1810 {
1811 double[] cast_array = (double[])array;
1812 for (int i=0; i < cast_array.length; i++)
1813 cast_array[i] = this.realInputStream.readDouble();
1814 return;
1815 }
1816 if (clazz == Float.TYPE)
1817 {
1818 float[] cast_array = (float[])array;
1819 for (int i=0; i < cast_array.length; i++)
1820 cast_array[i] = this.realInputStream.readFloat();
1821 return;
1822 }
1823 if (clazz == Integer.TYPE)
1824 {
1825 int[] cast_array = (int[])array;
1826 for (int i=0; i < cast_array.length; i++)
1827 cast_array[i] = this.realInputStream.readInt();
1828 return;
1829 }
1830 if (clazz == Long.TYPE)
1831 {
1832 long[] cast_array = (long[])array;
1833 for (int i=0; i < cast_array.length; i++)
1834 cast_array[i] = this.realInputStream.readLong();
1835 return;
1836 }
1837 if (clazz == Short.TYPE)
1838 {
1839 short[] cast_array = (short[])array;
1840 for (int i=0; i < cast_array.length; i++)
1841 cast_array[i] = this.realInputStream.readShort();
1842 return;
1843 }
1844 }
1845 else
1846 {
1847 Object[] cast_array = (Object[])array;
1848 for (int i=0; i < cast_array.length; i++)
1849 cast_array[i] = readObject();
1850 }
1851 }
1852
1853 private void readFields (Object obj, ObjectStreamClass stream_osc)
1854 throws ClassNotFoundException, IOException
1855 {
1856 ObjectStreamField[] fields = stream_osc.fieldMapping;
1857
1858 for (int i = 0; i < fields.length; i += 2)
1859 {
1860 ObjectStreamField stream_field = fields[i];
1861 ObjectStreamField real_field = fields[i + 1];
1862 boolean read_value = (stream_field != null && stream_field.getOffset() >= 0 && stream_field.isToSet());
1863 boolean set_value = (real_field != null && real_field.isToSet());
1864 String field_name;
1865 char type;
1866
1867 if (stream_field != null)
1868 {
1869 field_name = stream_field.getName();
1870 type = stream_field.getTypeCode();
1871 }
1872 else
1873 {
1874 field_name = real_field.getName();
1875 type = real_field.getTypeCode();
1876 }
1877
1878 switch(type)
1879 {
1880 case 'Z':
1881 {
1882 boolean value =
1883 read_value ? this.realInputStream.readBoolean() : false;
1884 if (dump && read_value && set_value)
1885 dumpElementln(" " + field_name + ": " + value);
1886 if (set_value)
1887 real_field.setBooleanField(obj, value);
1888 break;
1889 }
1890 case 'B':
1891 {
1892 byte value =
1893 read_value ? this.realInputStream.readByte() : 0;
1894 if (dump && read_value && set_value)
1895 dumpElementln(" " + field_name + ": " + value);
1896 if (set_value)
1897 real_field.setByteField(obj, value);
1898 break;
1899 }
1900 case 'C':
1901 {
1902 char value =
1903 read_value ? this.realInputStream.readChar(): 0;
1904 if (dump && read_value && set_value)
1905 dumpElementln(" " + field_name + ": " + value);
1906 if (set_value)
1907 real_field.setCharField(obj, value);
1908 break;
1909 }
1910 case 'D':
1911 {
1912 double value =
1913 read_value ? this.realInputStream.readDouble() : 0;
1914 if (dump && read_value && set_value)
1915 dumpElementln(" " + field_name + ": " + value);
1916 if (set_value)
1917 real_field.setDoubleField(obj, value);
1918 break;
1919 }
1920 case 'F':
1921 {
1922 float value =
1923 read_value ? this.realInputStream.readFloat() : 0;
1924 if (dump && read_value && set_value)
1925 dumpElementln(" " + field_name + ": " + value);
1926 if (set_value)
1927 real_field.setFloatField(obj, value);
1928 break;
1929 }
1930 case 'I':
1931 {
1932 int value =
1933 read_value ? this.realInputStream.readInt() : 0;
1934 if (dump && read_value && set_value)
1935 dumpElementln(" " + field_name + ": " + value);
1936 if (set_value)
1937 real_field.setIntField(obj, value);
1938 break;
1939 }
1940 case 'J':
1941 {
1942 long value =
1943 read_value ? this.realInputStream.readLong() : 0;
1944 if (dump && read_value && set_value)
1945 dumpElementln(" " + field_name + ": " + value);
1946 if (set_value)
1947 real_field.setLongField(obj, value);
1948 break;
1949 }
1950 case 'S':
1951 {
1952 short value =
1953 read_value ? this.realInputStream.readShort() : 0;
1954 if (dump && read_value && set_value)
1955 dumpElementln(" " + field_name + ": " + value);
1956 if (set_value)
1957 real_field.setShortField(obj, value);
1958 break;
1959 }
1960 case 'L':
1961 case '[':
1962 {
1963 Object value =
1964 read_value ? readObject() : null;
1965 if (set_value)
1966 real_field.setObjectField(obj, value);
1967 break;
1968 }
1969 default:
1970 throw new InternalError("Invalid type code: " + type);
1971 }
1972 }
1973 }
1974
1975 // Toggles writing primitive data to block-data buffer.
1976 private boolean setBlockDataMode (boolean on)
1977 {
1978 boolean oldmode = this.readDataFromBlock;
1979 this.readDataFromBlock = on;
1980
1981 if (on)
1982 this.dataInputStream = this.blockDataInput;
1983 else
1984 this.dataInputStream = this.realInputStream;
1985 return oldmode;
1986 }
1987
1988 // returns a new instance of REAL_CLASS that has been constructed
1989 // only to the level of CONSTRUCTOR_CLASS (a super class of REAL_CLASS)
1990 private Object newObject (Class real_class, Constructor constructor)
1991 throws ClassNotFoundException, IOException
1992 {
1993 if (constructor == null)
1994 throw new InvalidClassException("Missing accessible no-arg base class constructor for " + real_class.getName());
1995 try
1996 {
1997 return VMObjectInputStream.allocateObject(real_class, constructor.getDeclaringClass(), constructor);
1998 }
1999 catch (InstantiationException e)
2000 {
2001 throw (ClassNotFoundException) new ClassNotFoundException
2002 ("Instance of " + real_class + " could not be created").initCause(e);
2003 }
2004 }
2005
2006 // runs all registered ObjectInputValidations in prioritized order
2007 // on OBJ
2008 private void invokeValidators() throws InvalidObjectException
2009 {
2010 try
2011 {
2012 Iterator<ValidatorAndPriority> it = currentObjectValidators.iterator();
2013 while(it.hasNext())
2014 {
2015 ValidatorAndPriority vap = it.next();
2016 ObjectInputValidation validator = vap.validator;
2017 validator.validateObject();
2018 }
2019 }
2020 finally
2021 {
2022 currentObjectValidators = null;
2023 }
2024 }
2025
2026 private void callReadMethod (Method readObject, Class klass, Object obj)
2027 throws ClassNotFoundException, IOException
2028 {
2029 try
2030 {
2031 readObject.invoke(obj, new Object[] { this });
2032 }
2033 catch (InvocationTargetException x)
2034 {
2035 /* Rethrow if possible. */
2036 Throwable exception = x.getTargetException();
2037 if (exception instanceof RuntimeException)
2038 throw (RuntimeException) exception;
2039 if (exception instanceof IOException)
2040 throw (IOException) exception;
2041 if (exception instanceof ClassNotFoundException)
2042 throw (ClassNotFoundException) exception;
2043
2044 throw (IOException) new IOException(
2045 "Exception thrown from readObject() on " + klass).initCause(x);
2046 }
2047 catch (Exception x)
2048 {
2049 throw (IOException) new IOException(
2050 "Failure invoking readObject() on " + klass).initCause(x);
2051 }
2052
2053 // Invalidate fields which has been read through readFields.
2054 prereadFields = null;
2055 }
2056
2057 private static final int BUFFER_SIZE = 1024;
2058
2059 private DataInputStream realInputStream;
2060 private DataInputStream dataInputStream;
2061 private DataInputStream blockDataInput;
2062 private int blockDataPosition;
2063 private int blockDataBytes;
2064 private byte[] blockData;
2065 private boolean useSubclassMethod;
2066 private int nextOID;
2067 private boolean resolveEnabled;
2068 private Map<Integer,Pair<Boolean,Object>> handles;
2069 private Object currentObject;
2070 private ObjectStreamClass currentObjectStreamClass;
2071 private TreeSet<ValidatorAndPriority> currentObjectValidators;
2072 private boolean readDataFromBlock;
2073 private boolean fieldsAlreadyRead;
2074 private Hashtable<Class,ObjectStreamClass> classLookupTable;
2075 private GetField prereadFields;
2076
2077 private static boolean dump;
2078
2079 // The nesting depth for debugging output
2080 private int depth = 0;
2081
2082 private static final boolean DEBUG = false;
2083
2084 private void dumpElement (String msg)
2085 {
2086 System.out.print(msg);
2087 }
2088
2089 private void dumpElementln (String msg)
2090 {
2091 System.out.println(msg);
2092 for (int i = 0; i < depth; i++)
2093 System.out.print (" ");
2094 System.out.print (Thread.currentThread() + ": ");
2095 }
2096
2097 private void dumpElementln (String msg, Object obj)
2098 {
2099 try
2100 {
2101 System.out.print(msg);
2102 if (java.lang.reflect.Proxy.isProxyClass(obj.getClass()))
2103 System.out.println(obj.getClass());
2104 else
2105 System.out.println(obj);
2106 }
2107 catch (Exception _)
2108 {
2109 }
2110 for (int i = 0; i < depth; i++)
2111 System.out.print (" ");
2112 System.out.print (Thread.currentThread() + ": ");
2113 }
2114
2115 // used to keep a prioritized list of object validators
2116 private static final class ValidatorAndPriority implements Comparable
2117 {
2118 int priority;
2119 ObjectInputValidation validator;
2120
2121 ValidatorAndPriority (ObjectInputValidation validator, int priority)
2122 {
2123 this.priority = priority;
2124 this.validator = validator;
2125 }
2126
2127 public int compareTo (Object o)
2128 {
2129 ValidatorAndPriority vap = (ValidatorAndPriority)o;
2130 return this.priority - vap.priority;
2131 }
2132 }
2133 }
2134