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