| /* ObjectInputStream.java -- Class used to read serialized objects | |
| Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006 | |
| Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath is distributed in the hope that it will be useful, but | |
| WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package java.io; | |
| import gnu.java.io.ObjectIdentityWrapper; | |
| import java.lang.reflect.Array; | |
| import java.lang.reflect.Constructor; | |
| import java.lang.reflect.Field; | |
| import java.lang.reflect.InvocationTargetException; | |
| import java.lang.reflect.Method; | |
| import java.lang.reflect.Modifier; | |
| import java.lang.reflect.Proxy; | |
| import java.security.AccessController; | |
| import java.security.PrivilegedAction; | |
| import java.util.Hashtable; | |
| import java.util.Iterator; | |
| import java.util.TreeSet; | |
| import java.util.Vector; | |
| public class ObjectInputStream extends InputStream | |
| implements ObjectInput, ObjectStreamConstants | |
| { | |
| /** | |
| * Creates a new <code>ObjectInputStream</code> that will do all of | |
| * its reading from <code>in</code>. This method also checks | |
| * the stream by reading the header information (stream magic number | |
| * and stream version). | |
| * | |
| * @exception IOException Reading stream header from underlying | |
| * stream cannot be completed. | |
| * | |
| * @exception StreamCorruptedException An invalid stream magic | |
| * number or stream version was read from the stream. | |
| * | |
| * @see #readStreamHeader() | |
| */ | |
| public ObjectInputStream(InputStream in) | |
| throws IOException, StreamCorruptedException | |
| { | |
| if (DEBUG) | |
| { | |
| String val = System.getProperty("gcj.dumpobjects"); | |
| if (dump == false && val != null && !val.equals("")) | |
| { | |
| dump = true; | |
| System.out.println ("Serialization debugging enabled"); | |
| } | |
| else if (dump == true && (val == null || val.equals(""))) | |
| { | |
| dump = false; | |
| System.out.println ("Serialization debugging disabled"); | |
| } | |
| } | |
| this.resolveEnabled = false; | |
| this.blockDataPosition = 0; | |
| this.blockDataBytes = 0; | |
| this.blockData = new byte[BUFFER_SIZE]; | |
| this.blockDataInput = new DataInputStream(this); | |
| this.realInputStream = new DataInputStream(in); | |
| this.nextOID = baseWireHandle; | |
| this.objectLookupTable = new Hashtable(); | |
| this.classLookupTable = new Hashtable(); | |
| setBlockDataMode(true); | |
| readStreamHeader(); | |
| } | |
| /** | |
| * Returns the next deserialized object read from the underlying stream. | |
| * | |
| * This method can be overriden by a class by implementing | |
| * <code>private void readObject (ObjectInputStream)</code>. | |
| * | |
| * If an exception is thrown from this method, the stream is left in | |
| * an undefined state. This method can also throw Errors and | |
| * RuntimeExceptions if caused by existing readResolve() user code. | |
| * | |
| * @return The object read from the underlying stream. | |
| * | |
| * @exception ClassNotFoundException The class that an object being | |
| * read in belongs to cannot be found. | |
| * | |
| * @exception IOException Exception from underlying | |
| * <code>InputStream</code>. | |
| */ | |
| public final Object readObject() | |
| throws ClassNotFoundException, IOException | |
| { | |
| if (this.useSubclassMethod) | |
| return readObjectOverride(); | |
| Object ret_val; | |
| boolean old_mode = setBlockDataMode(false); | |
| byte marker = this.realInputStream.readByte(); | |
| if (DEBUG) | |
| depth += 2; | |
| if(dump) dumpElement("MARKER: 0x" + Integer.toHexString(marker) + " "); | |
| try | |
| { | |
| ret_val = parseContent(marker); | |
| } | |
| finally | |
| { | |
| setBlockDataMode(old_mode); | |
| if (DEBUG) | |
| depth -= 2; | |
| } | |
| return ret_val; | |
| } | |
| /** | |
| * Handles a content block within the stream, which begins with a marker | |
| * byte indicating its type. | |
| * | |
| * @param marker the byte marker. | |
| * @return an object which represents the parsed content. | |
| * @throws ClassNotFoundException if the class of an object being | |
| * read in cannot be found. | |
| * @throws IOException if invalid data occurs or one is thrown by the | |
| * underlying <code>InputStream</code>. | |
| */ | |
| private Object parseContent(byte marker) | |
| throws ClassNotFoundException, IOException | |
| { | |
| Object ret_val; | |
| boolean is_consumed = false; | |
| switch (marker) | |
| { | |
| case TC_ENDBLOCKDATA: | |
| { | |
| ret_val = null; | |
| is_consumed = true; | |
| break; | |
| } | |
| case TC_BLOCKDATA: | |
| case TC_BLOCKDATALONG: | |
| { | |
| if (marker == TC_BLOCKDATALONG) | |
| { if(dump) dumpElementln("BLOCKDATALONG"); } | |
| else | |
| { if(dump) dumpElementln("BLOCKDATA"); } | |
| readNextBlock(marker); | |
| } | |
| case TC_NULL: | |
| { | |
| if(dump) dumpElementln("NULL"); | |
| ret_val = null; | |
| break; | |
| } | |
| case TC_REFERENCE: | |
| { | |
| if(dump) dumpElement("REFERENCE "); | |
| Integer oid = new Integer(this.realInputStream.readInt()); | |
| if(dump) dumpElementln(Integer.toHexString(oid.intValue())); | |
| ret_val = ((ObjectIdentityWrapper) | |
| this.objectLookupTable.get(oid)).object; | |
| break; | |
| } | |
| case TC_CLASS: | |
| { | |
| if(dump) dumpElementln("CLASS"); | |
| ObjectStreamClass osc = (ObjectStreamClass)readObject(); | |
| Class clazz = osc.forClass(); | |
| assignNewHandle(clazz); | |
| ret_val = clazz; | |
| break; | |
| } | |
| case TC_PROXYCLASSDESC: | |
| { | |
| if(dump) dumpElementln("PROXYCLASS"); | |
| int n_intf = this.realInputStream.readInt(); | |
| String[] intfs = new String[n_intf]; | |
| for (int i = 0; i < n_intf; i++) | |
| { | |
| intfs[i] = this.realInputStream.readUTF(); | |
| } | |
| boolean oldmode = setBlockDataMode(true); | |
| Class cl = resolveProxyClass(intfs); | |
| setBlockDataMode(oldmode); | |
| ObjectStreamClass osc = lookupClass(cl); | |
| if (osc.firstNonSerializableParentConstructor == null) | |
| { | |
| osc.realClassIsSerializable = true; | |
| osc.fields = osc.fieldMapping = new ObjectStreamField[0]; | |
| try | |
| { | |
| osc.firstNonSerializableParentConstructor = | |
| Object.class.getConstructor(new Class[0]); | |
| } | |
| catch (NoSuchMethodException x) | |
| { | |
| throw (InternalError) | |
| new InternalError("Object ctor missing").initCause(x); | |
| } | |
| } | |
| assignNewHandle(osc); | |
| if (!is_consumed) | |
| { | |
| byte b = this.realInputStream.readByte(); | |
| if (b != TC_ENDBLOCKDATA) | |
| throw new IOException("Data annotated to class was not consumed." + b); | |
| } | |
| else | |
| is_consumed = false; | |
| ObjectStreamClass superosc = (ObjectStreamClass)readObject(); | |
| osc.setSuperclass(superosc); | |
| ret_val = osc; | |
| break; | |
| } | |
| case TC_CLASSDESC: | |
| { | |
| ObjectStreamClass osc = readClassDescriptor(); | |
| if (!is_consumed) | |
| { | |
| byte b = this.realInputStream.readByte(); | |
| if (b != TC_ENDBLOCKDATA) | |
| throw new IOException("Data annotated to class was not consumed." + b); | |
| } | |
| else | |
| is_consumed = false; | |
| osc.setSuperclass ((ObjectStreamClass)readObject()); | |
| ret_val = osc; | |
| break; | |
| } | |
| case TC_STRING: | |
| case TC_LONGSTRING: | |
| { | |
| if(dump) dumpElement("STRING="); | |
| String s = this.realInputStream.readUTF(); | |
| if(dump) dumpElementln(s); | |
| ret_val = processResolution(null, s, assignNewHandle(s)); | |
| break; | |
| } | |
| case TC_ARRAY: | |
| { | |
| if(dump) dumpElementln("ARRAY"); | |
| ObjectStreamClass osc = (ObjectStreamClass)readObject(); | |
| Class componentType = osc.forClass().getComponentType(); | |
| if(dump) dumpElement("ARRAY LENGTH="); | |
| int length = this.realInputStream.readInt(); | |
| if(dump) dumpElementln (length + "; COMPONENT TYPE=" + componentType); | |
| Object array = Array.newInstance(componentType, length); | |
| int handle = assignNewHandle(array); | |
| readArrayElements(array, componentType); | |
| if(dump) | |
| for (int i = 0, len = Array.getLength(array); i < len; i++) | |
| dumpElementln(" ELEMENT[" + i + "]=" + Array.get(array, i)); | |
| ret_val = processResolution(null, array, handle); | |
| break; | |
| } | |
| case TC_OBJECT: | |
| { | |
| if(dump) dumpElementln("OBJECT"); | |
| ObjectStreamClass osc = (ObjectStreamClass)readObject(); | |
| Class clazz = osc.forClass(); | |
| if (!osc.realClassIsSerializable) | |
| throw new NotSerializableException | |
| (clazz + " is not Serializable, and thus cannot be deserialized."); | |
| if (osc.realClassIsExternalizable) | |
| { | |
| Externalizable obj = osc.newInstance(); | |
| int handle = assignNewHandle(obj); | |
| boolean read_from_blocks = ((osc.getFlags() & SC_BLOCK_DATA) != 0); | |
| boolean oldmode = this.readDataFromBlock; | |
| if (read_from_blocks) | |
| setBlockDataMode(true); | |
| obj.readExternal(this); | |
| if (read_from_blocks) | |
| { | |
| setBlockDataMode(oldmode); | |
| if (!oldmode) | |
| if (this.realInputStream.readByte() != TC_ENDBLOCKDATA) | |
| throw new IOException("No end of block data seen for class with readExternal (ObjectInputStream) method."); | |
| } | |
| ret_val = processResolution(osc, obj, handle); | |
| break; | |
| } // end if (osc.realClassIsExternalizable) | |
| Object obj = newObject(clazz, osc.firstNonSerializableParentConstructor); | |
| int handle = assignNewHandle(obj); | |
| Object prevObject = this.currentObject; | |
| ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass; | |
| TreeSet prevObjectValidators = this.currentObjectValidators; | |
| this.currentObject = obj; | |
| this.currentObjectValidators = null; | |
| ObjectStreamClass[] hierarchy = | |
| inputGetObjectStreamClasses(clazz); | |
| for (int i = 0; i < hierarchy.length; i++) | |
| { | |
| this.currentObjectStreamClass = hierarchy[i]; | |
| if(dump) dumpElementln("Reading fields of " + this.currentObjectStreamClass.getName ()); | |
| // XXX: should initialize fields in classes in the hierarchy | |
| // that aren't in the stream | |
| // should skip over classes in the stream that aren't in the | |
| // real classes hierarchy | |
| Method readObjectMethod = this.currentObjectStreamClass.readObjectMethod; | |
| if (readObjectMethod != null) | |
| { | |
| fieldsAlreadyRead = false; | |
| boolean oldmode = setBlockDataMode(true); | |
| callReadMethod(readObjectMethod, this.currentObjectStreamClass.forClass(), obj); | |
| setBlockDataMode(oldmode); | |
| } | |
| else | |
| { | |
| readFields(obj, currentObjectStreamClass); | |
| } | |
| if (this.currentObjectStreamClass.hasWriteMethod()) | |
| { | |
| if(dump) dumpElement("ENDBLOCKDATA? "); | |
| try | |
| { | |
| /* Read blocks until an end marker */ | |
| byte writeMarker = this.realInputStream.readByte(); | |
| while (writeMarker != TC_ENDBLOCKDATA) | |
| { | |
| parseContent(writeMarker); | |
| writeMarker = this.realInputStream.readByte(); | |
| } | |
| if(dump) dumpElementln("yes"); | |
| } | |
| catch (EOFException e) | |
| { | |
| throw (IOException) new IOException | |
| ("No end of block data seen for class with readObject (ObjectInputStream) method.").initCause(e); | |
| } | |
| } | |
| } | |
| this.currentObject = prevObject; | |
| this.currentObjectStreamClass = prevObjectStreamClass; | |
| ret_val = processResolution(osc, obj, handle); | |
| if (currentObjectValidators != null) | |
| invokeValidators(); | |
| this.currentObjectValidators = prevObjectValidators; | |
| break; | |
| } | |
| case TC_RESET: | |
| if(dump) dumpElementln("RESET"); | |
| clearHandles(); | |
| ret_val = readObject(); | |
| break; | |
| case TC_EXCEPTION: | |
| { | |
| if(dump) dumpElement("EXCEPTION="); | |
| Exception e = (Exception)readObject(); | |
| if(dump) dumpElementln(e.toString()); | |
| clearHandles(); | |
| throw new WriteAbortedException("Exception thrown during writing of stream", e); | |
| } | |
| case TC_ENUM: | |
| { | |
| /* TC_ENUM classDesc newHandle enumConstantName */ | |
| if (dump) | |
| dumpElementln("ENUM="); | |
| ObjectStreamClass osc = (ObjectStreamClass) readObject(); | |
| String constantName = (String) readObject(); | |
| if (dump) | |
| dumpElementln("CONSTANT NAME = " + constantName); | |
| Class clazz = osc.forClass(); | |
| Enum instance = Enum.valueOf(clazz, constantName); | |
| assignNewHandle(instance); | |
| ret_val = instance; | |
| break; | |
| } | |
| default: | |
| throw new IOException("Unknown marker on stream: " + marker); | |
| } | |
| return ret_val; | |
| } | |
| /** | |
| * This method makes a partial check of types for the fields | |
| * contained given in arguments. It checks primitive types of | |
| * fields1 against non primitive types of fields2. This method | |
| * assumes the two lists has already been sorted according to | |
| * the Java specification. | |
| * | |
| * @param name Name of the class owning the given fields. | |
| * @param fields1 First list to check. | |
| * @param fields2 Second list to check. | |
| * @throws InvalidClassException if a field in fields1, which has a primitive type, is a present | |
| * in the non primitive part in fields2. | |
| */ | |
| private void checkTypeConsistency(String name, ObjectStreamField[] fields1, ObjectStreamField[] fields2) | |
| throws InvalidClassException | |
| { | |
| int nonPrimitive = 0; | |
| for (nonPrimitive = 0; | |
| nonPrimitive < fields1.length | |
| && fields1[nonPrimitive].isPrimitive(); nonPrimitive++) | |
| { | |
| } | |
| if (nonPrimitive == fields1.length) | |
| return; | |
| int i = 0; | |
| ObjectStreamField f1; | |
| ObjectStreamField f2; | |
| while (i < fields2.length | |
| && nonPrimitive < fields1.length) | |
| { | |
| f1 = fields1[nonPrimitive]; | |
| f2 = fields2[i]; | |
| if (!f2.isPrimitive()) | |
| break; | |
| int compVal = f1.getName().compareTo (f2.getName()); | |
| if (compVal < 0) | |
| { | |
| nonPrimitive++; | |
| } | |
| else if (compVal > 0) | |
| { | |
| i++; | |
| } | |
| else | |
| { | |
| throw new InvalidClassException | |
| ("invalid field type for " + f2.getName() + | |
| " in class " + name); | |
| } | |
| } | |
| } | |
| /** | |
| * This method reads a class descriptor from the real input stream | |
| * and use these data to create a new instance of ObjectStreamClass. | |
| * Fields are sorted and ordered for the real read which occurs for | |
| * each instance of the described class. Be aware that if you call that | |
| * method you must ensure that the stream is synchronized, in the other | |
| * case it may be completely desynchronized. | |
| * | |
| * @return A new instance of ObjectStreamClass containing the freshly | |
| * created descriptor. | |
| * @throws ClassNotFoundException if the required class to build the | |
| * descriptor has not been found in the system. | |
| * @throws IOException An input/output error occured. | |
| * @throws InvalidClassException If there was a compatibility problem | |
| * between the class present in the system and the serialized class. | |
| */ | |
| protected ObjectStreamClass readClassDescriptor() | |
| throws ClassNotFoundException, IOException | |
| { | |
| if(dump) dumpElement("CLASSDESC NAME="); | |
| String name = this.realInputStream.readUTF(); | |
| if(dump) dumpElement(name + "; UID="); | |
| long uid = this.realInputStream.readLong (); | |
| if(dump) dumpElement(Long.toHexString(uid) + "; FLAGS="); | |
| byte flags = this.realInputStream.readByte (); | |
| if(dump) dumpElement(Integer.toHexString(flags) + "; FIELD COUNT="); | |
| short field_count = this.realInputStream.readShort(); | |
| if(dump) dumpElementln(Short.toString(field_count)); | |
| ObjectStreamField[] fields = new ObjectStreamField[field_count]; | |
| ObjectStreamClass osc = new ObjectStreamClass(name, uid, | |
| flags, fields); | |
| assignNewHandle(osc); | |
| ClassLoader callersClassLoader = currentLoader(); | |
| for (int i = 0; i < field_count; i++) | |
| { | |
| if(dump) dumpElement(" TYPE CODE="); | |
| char type_code = (char)this.realInputStream.readByte(); | |
| if(dump) dumpElement(type_code + "; FIELD NAME="); | |
| String field_name = this.realInputStream.readUTF(); | |
| if(dump) dumpElementln(field_name); | |
| String class_name; | |
| // If the type code is an array or an object we must | |
| // decode a String here. In the other case we convert | |
| // the type code and pass it to ObjectStreamField. | |
| // Type codes are decoded by gnu.java.lang.reflect.TypeSignature. | |
| if (type_code == 'L' || type_code == '[') | |
| class_name = (String)readObject(); | |
| else | |
| class_name = String.valueOf(type_code); | |
| fields[i] = | |
| new ObjectStreamField(field_name, class_name, callersClassLoader); | |
| } | |
| /* Now that fields have been read we may resolve the class | |
| * (and read annotation if needed). */ | |
| Class clazz = resolveClass(osc); | |
| boolean oldmode = setBlockDataMode(true); | |
| osc.setClass(clazz, lookupClass(clazz.getSuperclass())); | |
| classLookupTable.put(clazz, osc); | |
| setBlockDataMode(oldmode); | |
| // find the first non-serializable class in clazz's inheritance hierarchy | |
| Class first_nonserial = clazz.getSuperclass(); | |
| // Maybe it is a primitive class, those don't have a super class, | |
| // or Object itself. Otherwise we can keep getting the superclass | |
| // till we hit the Object class, or some other non-serializable class. | |
| if (first_nonserial == null) | |
| first_nonserial = clazz; | |
| else | |
| while (Serializable.class.isAssignableFrom(first_nonserial)) | |
| first_nonserial = first_nonserial.getSuperclass(); | |
| final Class local_constructor_class = first_nonserial; | |
| osc.firstNonSerializableParentConstructor = | |
| (Constructor)AccessController.doPrivileged(new PrivilegedAction() | |
| { | |
| public Object run() | |
| { | |
| try | |
| { | |
| Constructor c = local_constructor_class. | |
| getDeclaredConstructor(new Class[0]); | |
| if (Modifier.isPrivate(c.getModifiers())) | |
| return null; | |
| return c; | |
| } | |
| catch (NoSuchMethodException e) | |
| { | |
| // error will be reported later, in newObject() | |
| return null; | |
| } | |
| } | |
| }); | |
| osc.realClassIsSerializable = Serializable.class.isAssignableFrom(clazz); | |
| osc.realClassIsExternalizable = Externalizable.class.isAssignableFrom(clazz); | |
| ObjectStreamField[] stream_fields = osc.fields; | |
| ObjectStreamField[] real_fields = ObjectStreamClass.lookupForClassObject(clazz).fields; | |
| ObjectStreamField[] fieldmapping = new ObjectStreamField[2 * Math.max(stream_fields.length, real_fields.length)]; | |
| int stream_idx = 0; | |
| int real_idx = 0; | |
| int map_idx = 0; | |
| /* | |
| * Check that there is no type inconsistencies between the lists. | |
| * A special checking must be done for the two groups: primitive types and | |
| * not primitive types. | |
| */ | |
| checkTypeConsistency(name, real_fields, stream_fields); | |
| checkTypeConsistency(name, stream_fields, real_fields); | |
| while (stream_idx < stream_fields.length | |
| || real_idx < real_fields.length) | |
| { | |
| ObjectStreamField stream_field = null; | |
| ObjectStreamField real_field = null; | |
| if (stream_idx == stream_fields.length) | |
| { | |
| real_field = real_fields[real_idx++]; | |
| } | |
| else if (real_idx == real_fields.length) | |
| { | |
| stream_field = stream_fields[stream_idx++]; | |
| } | |
| else | |
| { | |
| int comp_val = | |
| real_fields[real_idx].compareTo (stream_fields[stream_idx]); | |
| if (comp_val < 0) | |
| { | |
| real_field = real_fields[real_idx++]; | |
| } | |
| else if (comp_val > 0) | |
| { | |
| stream_field = stream_fields[stream_idx++]; | |
| } | |
| else | |
| { | |
| stream_field = stream_fields[stream_idx++]; | |
| real_field = real_fields[real_idx++]; | |
| if (stream_field.getType() != real_field.getType()) | |
| throw new InvalidClassException | |
| ("invalid field type for " + real_field.getName() + | |
| " in class " + name); | |
| } | |
| } | |
| /* If some of stream_fields does not correspond to any of real_fields, | |
| * or the opposite, then fieldmapping will go short. | |
| */ | |
| if (map_idx == fieldmapping.length) | |
| { | |
| ObjectStreamField[] newfieldmapping = | |
| new ObjectStreamField[fieldmapping.length + 2]; | |
| System.arraycopy(fieldmapping, 0, | |
| newfieldmapping, 0, fieldmapping.length); | |
| fieldmapping = newfieldmapping; | |
| } | |
| fieldmapping[map_idx++] = stream_field; | |
| fieldmapping[map_idx++] = real_field; | |
| } | |
| osc.fieldMapping = fieldmapping; | |
| return osc; | |
| } | |
| /** | |
| * Reads the current objects non-transient, non-static fields from | |
| * the current class from the underlying output stream. | |
| * | |
| * This method is intended to be called from within a object's | |
| * <code>private void readObject (ObjectInputStream)</code> | |
| * method. | |
| * | |
| * @exception ClassNotFoundException The class that an object being | |
| * read in belongs to cannot be found. | |
| * | |
| * @exception NotActiveException This method was called from a | |
| * context other than from the current object's and current class's | |
| * <code>private void readObject (ObjectInputStream)</code> | |
| * method. | |
| * | |
| * @exception IOException Exception from underlying | |
| * <code>OutputStream</code>. | |
| */ | |
| public void defaultReadObject() | |
| throws ClassNotFoundException, IOException, NotActiveException | |
| { | |
| if (this.currentObject == null || this.currentObjectStreamClass == null) | |
| throw new NotActiveException("defaultReadObject called by non-active" | |
| + " class and/or object"); | |
| if (fieldsAlreadyRead) | |
| throw new NotActiveException("defaultReadObject called but fields " | |
| + "already read from stream (by " | |
| + "defaultReadObject or readFields)"); | |
| boolean oldmode = setBlockDataMode(false); | |
| readFields(this.currentObject, this.currentObjectStreamClass); | |
| setBlockDataMode(oldmode); | |
| fieldsAlreadyRead = true; | |
| } | |
| /** | |
| * Registers a <code>ObjectInputValidation</code> to be carried out | |
| * on the object graph currently being deserialized before it is | |
| * returned to the original caller of <code>readObject ()</code>. | |
| * The order of validation for multiple | |
| * <code>ObjectInputValidation</code>s can be controled using | |
| * <code>priority</code>. Validators with higher priorities are | |
| * called first. | |
| * | |
| * @see java.io.ObjectInputValidation | |
| * | |
| * @exception InvalidObjectException <code>validator</code> is | |
| * <code>null</code> | |
| * | |
| * @exception NotActiveException an attempt was made to add a | |
| * validator outside of the <code>readObject</code> method of the | |
| * object currently being deserialized | |
| */ | |
| public void registerValidation(ObjectInputValidation validator, | |
| int priority) | |
| throws InvalidObjectException, NotActiveException | |
| { | |
| if (this.currentObject == null || this.currentObjectStreamClass == null) | |
| throw new NotActiveException("registerValidation called by non-active " | |
| + "class and/or object"); | |
| if (validator == null) | |
| throw new InvalidObjectException("attempt to add a null " | |
| + "ObjectInputValidation object"); | |
| if (currentObjectValidators == null) | |
| currentObjectValidators = new TreeSet(); | |
| currentObjectValidators.add(new ValidatorAndPriority(validator, priority)); | |
| } | |
| /** | |
| * Called when a class is being deserialized. This is a hook to | |
| * allow subclasses to read in information written by the | |
| * <code>annotateClass (Class)</code> method of an | |
| * <code>ObjectOutputStream</code>. | |
| * | |
| * This implementation looks up the active call stack for a | |
| * <code>ClassLoader</code>; if a <code>ClassLoader</code> is found, | |
| * it is used to load the class associated with <code>osc</code>, | |
| * otherwise, the default system <code>ClassLoader</code> is used. | |
| * | |
| * @exception IOException Exception from underlying | |
| * <code>OutputStream</code>. | |
| * | |
| * @see java.io.ObjectOutputStream#annotateClass (java.lang.Class) | |
| */ | |
| protected Class resolveClass(ObjectStreamClass osc) | |
| throws ClassNotFoundException, IOException | |
| { | |
| String name = osc.getName(); | |
| try | |
| { | |
| return Class.forName(name, true, currentLoader()); | |
| } | |
| catch(ClassNotFoundException x) | |
| { | |
| if (name.equals("void")) | |
| return Void.TYPE; | |
| else if (name.equals("boolean")) | |
| return Boolean.TYPE; | |
| else if (name.equals("byte")) | |
| return Byte.TYPE; | |
| else if (name.equals("char")) | |
| return Character.TYPE; | |
| else if (name.equals("short")) | |
| return Short.TYPE; | |
| else if (name.equals("int")) | |
| return Integer.TYPE; | |
| else if (name.equals("long")) | |
| return Long.TYPE; | |
| else if (name.equals("float")) | |
| return Float.TYPE; | |
| else if (name.equals("double")) | |
| return Double.TYPE; | |
| else | |
| throw x; | |
| } | |
| } | |
| /** | |
| * Returns the most recent user defined ClassLoader on the execution stack | |
| * or null if none is found. | |
| */ | |
| private ClassLoader currentLoader() | |
| { | |
| return VMObjectInputStream.currentClassLoader(); | |
| } | |
| /** | |
| * Lookup a class stored in the local hashtable. If it is not | |
| * use the global lookup function in ObjectStreamClass to build | |
| * the ObjectStreamClass. This method is requested according to | |
| * the behaviour detected in the JDK by Kaffe's team. | |
| * | |
| * @param clazz Class to lookup in the hash table or for which | |
| * we must build a descriptor. | |
| * @return A valid instance of ObjectStreamClass corresponding | |
| * to the specified class. | |
| */ | |
| private ObjectStreamClass lookupClass(Class clazz) | |
| { | |
| if (clazz == null) | |
| return null; | |
| ObjectStreamClass oclazz; | |
| oclazz = (ObjectStreamClass)classLookupTable.get(clazz); | |
| if (oclazz == null) | |
| return ObjectStreamClass.lookup(clazz); | |
| else | |
| return oclazz; | |
| } | |
| /** | |
| * Reconstruct class hierarchy the same way | |
| * {@link java.io.ObjectStreamClass#getObjectStreamClasses(Class)} does | |
| * but using lookupClass instead of ObjectStreamClass.lookup. This | |
| * dup is necessary localize the lookup table. Hopefully some future | |
| * rewritings will be able to prevent this. | |
| * | |
| * @param clazz This is the class for which we want the hierarchy. | |
| * | |
| * @return An array of valid {@link java.io.ObjectStreamClass} instances which | |
| * represent the class hierarchy for clazz. | |
| */ | |
| private ObjectStreamClass[] inputGetObjectStreamClasses(Class clazz) | |
| { | |
| ObjectStreamClass osc = lookupClass(clazz); | |
| if (osc == null) | |
| return new ObjectStreamClass[0]; | |
| else | |
| { | |
| Vector oscs = new Vector(); | |
| while (osc != null) | |
| { | |
| oscs.addElement(osc); | |
| osc = osc.getSuper(); | |
| } | |
| int count = oscs.size(); | |
| ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[count]; | |
| for (int i = count - 1; i >= 0; i--) | |
| sorted_oscs[count - i - 1] = (ObjectStreamClass) oscs.elementAt(i); | |
| return sorted_oscs; | |
| } | |
| } | |
| /** | |
| * Allows subclasses to resolve objects that are read from the | |
| * stream with other objects to be returned in their place. This | |
| * method is called the first time each object is encountered. | |
| * | |
| * This method must be enabled before it will be called in the | |
| * serialization process. | |
| * | |
| * @exception IOException Exception from underlying | |
| * <code>OutputStream</code>. | |
| * | |
| * @see #enableResolveObject(boolean) | |
| */ | |
| protected Object resolveObject(Object obj) throws IOException | |
| { | |
| return obj; | |
| } | |
| protected Class resolveProxyClass(String[] intfs) | |
| throws IOException, ClassNotFoundException | |
| { | |
| ClassLoader cl = currentLoader(); | |
| Class[] clss = new Class[intfs.length]; | |
| if(cl == null) | |
| { | |
| for (int i = 0; i < intfs.length; i++) | |
| clss[i] = Class.forName(intfs[i]); | |
| cl = ClassLoader.getSystemClassLoader(); | |
| } | |
| else | |
| for (int i = 0; i < intfs.length; i++) | |
| clss[i] = Class.forName(intfs[i], false, cl); | |
| try | |
| { | |
| return Proxy.getProxyClass(cl, clss); | |
| } | |
| catch (IllegalArgumentException e) | |
| { | |
| throw new ClassNotFoundException(null, e); | |
| } | |
| } | |
| /** | |
| * If <code>enable</code> is <code>true</code> and this object is | |
| * trusted, then <code>resolveObject (Object)</code> will be called | |
| * in subsequent calls to <code>readObject (Object)</code>. | |
| * Otherwise, <code>resolveObject (Object)</code> will not be called. | |
| * | |
| * @exception SecurityException This class is not trusted. | |
| */ | |
| protected boolean enableResolveObject (boolean enable) | |
| throws SecurityException | |
| { | |
| if (enable) | |
| { | |
| SecurityManager sm = System.getSecurityManager(); | |
| if (sm != null) | |
| sm.checkPermission(new SerializablePermission("enableSubstitution")); | |
| } | |
| boolean old_val = this.resolveEnabled; | |
| this.resolveEnabled = enable; | |
| return old_val; | |
| } | |
| /** | |
| * Reads stream magic and stream version information from the | |
| * underlying stream. | |
| * | |
| * @exception IOException Exception from underlying stream. | |
| * | |
| * @exception StreamCorruptedException An invalid stream magic | |
| * number or stream version was read from the stream. | |
| */ | |
| protected void readStreamHeader() | |
| throws IOException, StreamCorruptedException | |
| { | |
| if(dump) dumpElement("STREAM MAGIC "); | |
| if (this.realInputStream.readShort() != STREAM_MAGIC) | |
| throw new StreamCorruptedException("Invalid stream magic number"); | |
| if(dump) dumpElementln("STREAM VERSION "); | |
| if (this.realInputStream.readShort() != STREAM_VERSION) | |
| throw new StreamCorruptedException("Invalid stream version number"); | |
| } | |
| public int read() throws IOException | |
| { | |
| if (this.readDataFromBlock) | |
| { | |
| if (this.blockDataPosition >= this.blockDataBytes) | |
| readNextBlock(); | |
| return (this.blockData[this.blockDataPosition++] & 0xff); | |
| } | |
| else | |
| return this.realInputStream.read(); | |
| } | |
| public int read(byte[] data, int offset, int length) throws IOException | |
| { | |
| if (this.readDataFromBlock) | |
| { | |
| int remain = this.blockDataBytes - this.blockDataPosition; | |
| if (remain == 0) | |
| { | |
| readNextBlock(); | |
| remain = this.blockDataBytes - this.blockDataPosition; | |
| } | |
| length = Math.min(length, remain); | |
| System.arraycopy(this.blockData, this.blockDataPosition, | |
| data, offset, length); | |
| this.blockDataPosition += length; | |
| return length; | |
| } | |
| else | |
| return this.realInputStream.read(data, offset, length); | |
| } | |
| public int available() throws IOException | |
| { | |
| if (this.readDataFromBlock) | |
| { | |
| if (this.blockDataPosition >= this.blockDataBytes) | |
| readNextBlock (); | |
| return this.blockDataBytes - this.blockDataPosition; | |
| } | |
| else | |
| return this.realInputStream.available(); | |
| } | |
| public void close() throws IOException | |
| { | |
| this.realInputStream.close(); | |
| } | |
| public boolean readBoolean() throws IOException | |
| { | |
| boolean switchmode = true; | |
| boolean oldmode = this.readDataFromBlock; | |
| if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1) | |
| switchmode = false; | |
| if (switchmode) | |
| oldmode = setBlockDataMode (true); | |
| boolean value = this.dataInputStream.readBoolean (); | |
| if (switchmode) | |
| setBlockDataMode (oldmode); | |
| return value; | |
| } | |
| public byte readByte() throws IOException | |
| { | |
| boolean switchmode = true; | |
| boolean oldmode = this.readDataFromBlock; | |
| if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1) | |
| switchmode = false; | |
| if (switchmode) | |
| oldmode = setBlockDataMode(true); | |
| byte value = this.dataInputStream.readByte(); | |
| if (switchmode) | |
| setBlockDataMode(oldmode); | |
| return value; | |
| } | |
| public int readUnsignedByte() throws IOException | |
| { | |
| boolean switchmode = true; | |
| boolean oldmode = this.readDataFromBlock; | |
| if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1) | |
| switchmode = false; | |
| if (switchmode) | |
| oldmode = setBlockDataMode(true); | |
| int value = this.dataInputStream.readUnsignedByte(); | |
| if (switchmode) | |
| setBlockDataMode(oldmode); | |
| return value; | |
| } | |
| public short readShort() throws IOException | |
| { | |
| boolean switchmode = true; | |
| boolean oldmode = this.readDataFromBlock; | |
| if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2) | |
| switchmode = false; | |
| if (switchmode) | |
| oldmode = setBlockDataMode(true); | |
| short value = this.dataInputStream.readShort(); | |
| if (switchmode) | |
| setBlockDataMode(oldmode); | |
| return value; | |
| } | |
| public int readUnsignedShort() throws IOException | |
| { | |
| boolean switchmode = true; | |
| boolean oldmode = this.readDataFromBlock; | |
| if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2) | |
| switchmode = false; | |
| if (switchmode) | |
| oldmode = setBlockDataMode(true); | |
| int value = this.dataInputStream.readUnsignedShort(); | |
| if (switchmode) | |
| setBlockDataMode(oldmode); | |
| return value; | |
| } | |
| public char readChar() throws IOException | |
| { | |
| boolean switchmode = true; | |
| boolean oldmode = this.readDataFromBlock; | |
| if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2) | |
| switchmode = false; | |
| if (switchmode) | |
| oldmode = setBlockDataMode(true); | |
| char value = this.dataInputStream.readChar(); | |
| if (switchmode) | |
| setBlockDataMode(oldmode); | |
| return value; | |
| } | |
| public int readInt() throws IOException | |
| { | |
| boolean switchmode = true; | |
| boolean oldmode = this.readDataFromBlock; | |
| if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4) | |
| switchmode = false; | |
| if (switchmode) | |
| oldmode = setBlockDataMode(true); | |
| int value = this.dataInputStream.readInt(); | |
| if (switchmode) | |
| setBlockDataMode(oldmode); | |
| return value; | |
| } | |
| public long readLong() throws IOException | |
| { | |
| boolean switchmode = true; | |
| boolean oldmode = this.readDataFromBlock; | |
| if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8) | |
| switchmode = false; | |
| if (switchmode) | |
| oldmode = setBlockDataMode(true); | |
| long value = this.dataInputStream.readLong(); | |
| if (switchmode) | |
| setBlockDataMode(oldmode); | |
| return value; | |
| } | |
| public float readFloat() throws IOException | |
| { | |
| boolean switchmode = true; | |
| boolean oldmode = this.readDataFromBlock; | |
| if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4) | |
| switchmode = false; | |
| if (switchmode) | |
| oldmode = setBlockDataMode(true); | |
| float value = this.dataInputStream.readFloat(); | |
| if (switchmode) | |
| setBlockDataMode(oldmode); | |
| return value; | |
| } | |
| public double readDouble() throws IOException | |
| { | |
| boolean switchmode = true; | |
| boolean oldmode = this.readDataFromBlock; | |
| if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8) | |
| switchmode = false; | |
| if (switchmode) | |
| oldmode = setBlockDataMode(true); | |
| double value = this.dataInputStream.readDouble(); | |
| if (switchmode) | |
| setBlockDataMode(oldmode); | |
| return value; | |
| } | |
| public void readFully(byte data[]) throws IOException | |
| { | |
| this.dataInputStream.readFully(data); | |
| } | |
| public void readFully(byte data[], int offset, int size) | |
| throws IOException | |
| { | |
| this.dataInputStream.readFully(data, offset, size); | |
| } | |
| public int skipBytes(int len) throws IOException | |
| { | |
| return this.dataInputStream.skipBytes(len); | |
| } | |
| /** | |
| * @deprecated | |
| * @see java.io.DataInputStream#readLine () | |
| */ | |
| public String readLine() throws IOException | |
| { | |
| return this.dataInputStream.readLine(); | |
| } | |
| public String readUTF() throws IOException | |
| { | |
| return this.dataInputStream.readUTF(); | |
| } | |
| /** | |
| * This class allows a class to specify exactly which fields should | |
| * be read, and what values should be read for these fields. | |
| * | |
| * XXX: finish up comments | |
| */ | |
| public abstract static class GetField | |
| { | |
| public abstract ObjectStreamClass getObjectStreamClass(); | |
| public abstract boolean defaulted(String name) | |
| throws IOException, IllegalArgumentException; | |
| public abstract boolean get(String name, boolean defvalue) | |
| throws IOException, IllegalArgumentException; | |
| public abstract char get(String name, char defvalue) | |
| throws IOException, IllegalArgumentException; | |
| public abstract byte get(String name, byte defvalue) | |
| throws IOException, IllegalArgumentException; | |
| public abstract short get(String name, short defvalue) | |
| throws IOException, IllegalArgumentException; | |
| public abstract int get(String name, int defvalue) | |
| throws IOException, IllegalArgumentException; | |
| public abstract long get(String name, long defvalue) | |
| throws IOException, IllegalArgumentException; | |
| public abstract float get(String name, float defvalue) | |
| throws IOException, IllegalArgumentException; | |
| public abstract double get(String name, double defvalue) | |
| throws IOException, IllegalArgumentException; | |
| public abstract Object get(String name, Object defvalue) | |
| throws IOException, IllegalArgumentException; | |
| } | |
| /** | |
| * This method should be called by a method called 'readObject' in the | |
| * deserializing class (if present). It cannot (and should not)be called | |
| * outside of it. Its goal is to read all fields in the real input stream | |
| * and keep them accessible through the {@link GetField} class. Calling | |
| * this method will not alter the deserializing object. | |
| * | |
| * @return A valid freshly created 'GetField' instance to get access to | |
| * the deserialized stream. | |
| * @throws IOException An input/output exception occured. | |
| * @throws ClassNotFoundException | |
| * @throws NotActiveException | |
| */ | |
| public GetField readFields() | |
| throws IOException, ClassNotFoundException, NotActiveException | |
| { | |
| if (this.currentObject == null || this.currentObjectStreamClass == null) | |
| throw new NotActiveException("readFields called by non-active class and/or object"); | |
| if (prereadFields != null) | |
| return prereadFields; | |
| if (fieldsAlreadyRead) | |
| throw new NotActiveException("readFields called but fields already read from" | |
| + " stream (by defaultReadObject or readFields)"); | |
| final ObjectStreamClass clazz = this.currentObjectStreamClass; | |
| final byte[] prim_field_data = new byte[clazz.primFieldSize]; | |
| final Object[] objs = new Object[clazz.objectFieldCount]; | |
| // Apparently Block data is not used with GetField as per | |
| // empirical evidence against JDK 1.2. Also see Mauve test | |
| // java.io.ObjectInputOutput.Test.GetPutField. | |
| boolean oldmode = setBlockDataMode(false); | |
| readFully(prim_field_data); | |
| for (int i = 0; i < objs.length; ++ i) | |
| objs[i] = readObject(); | |
| setBlockDataMode(oldmode); | |
| prereadFields = new GetField() | |
| { | |
| public ObjectStreamClass getObjectStreamClass() | |
| { | |
| return clazz; | |
| } | |
| public boolean defaulted(String name) | |
| throws IOException, IllegalArgumentException | |
| { | |
| ObjectStreamField f = clazz.getField(name); | |
| /* First if we have a serialized field use the descriptor */ | |
| if (f != null) | |
| { | |
| /* It is in serialPersistentFields but setClass tells us | |
| * it should not be set. This value is defaulted. | |
| */ | |
| if (f.isPersistent() && !f.isToSet()) | |
| return true; | |
| return false; | |
| } | |
| /* This is not a serialized field. There should be | |
| * a default value only if the field really exists. | |
| */ | |
| try | |
| { | |
| return (clazz.forClass().getDeclaredField (name) != null); | |
| } | |
| catch (NoSuchFieldException e) | |
| { | |
| throw new IllegalArgumentException(e); | |
| } | |
| } | |
| public boolean get(String name, boolean defvalue) | |
| throws IOException, IllegalArgumentException | |
| { | |
| ObjectStreamField field = getField(name, Boolean.TYPE); | |
| if (field == null) | |
| return defvalue; | |
| return prim_field_data[field.getOffset()] == 0 ? false : true; | |
| } | |
| public char get(String name, char defvalue) | |
| throws IOException, IllegalArgumentException | |
| { | |
| ObjectStreamField field = getField(name, Character.TYPE); | |
| if (field == null) | |
| return defvalue; | |
| int off = field.getOffset(); | |
| return (char)(((prim_field_data[off++] & 0xFF) << 8) | |
| | (prim_field_data[off] & 0xFF)); | |
| } | |
| public byte get(String name, byte defvalue) | |
| throws IOException, IllegalArgumentException | |
| { | |
| ObjectStreamField field = getField(name, Byte.TYPE); | |
| if (field == null) | |
| return defvalue; | |
| return prim_field_data[field.getOffset()]; | |
| } | |
| public short get(String name, short defvalue) | |
| throws IOException, IllegalArgumentException | |
| { | |
| ObjectStreamField field = getField(name, Short.TYPE); | |
| if (field == null) | |
| return defvalue; | |
| int off = field.getOffset(); | |
| return (short)(((prim_field_data[off++] & 0xFF) << 8) | |
| | (prim_field_data[off] & 0xFF)); | |
| } | |
| public int get(String name, int defvalue) | |
| throws IOException, IllegalArgumentException | |
| { | |
| ObjectStreamField field = getField(name, Integer.TYPE); | |
| if (field == null) | |
| return defvalue; | |
| int off = field.getOffset(); | |
| return ((prim_field_data[off++] & 0xFF) << 24) | |
| | ((prim_field_data[off++] & 0xFF) << 16) | |
| | ((prim_field_data[off++] & 0xFF) << 8) | |
| | (prim_field_data[off] & 0xFF); | |
| } | |
| public long get(String name, long defvalue) | |
| throws IOException, IllegalArgumentException | |
| { | |
| ObjectStreamField field = getField(name, Long.TYPE); | |
| if (field == null) | |
| return defvalue; | |
| int off = field.getOffset(); | |
| return (long)(((prim_field_data[off++] & 0xFFL) << 56) | |
| | ((prim_field_data[off++] & 0xFFL) << 48) | |
| | ((prim_field_data[off++] & 0xFFL) << 40) | |
| | ((prim_field_data[off++] & 0xFFL) << 32) | |
| | ((prim_field_data[off++] & 0xFF) << 24) | |
| | ((prim_field_data[off++] & 0xFF) << 16) | |
| | ((prim_field_data[off++] & 0xFF) << 8) | |
| | (prim_field_data[off] & 0xFF)); | |
| } | |
| public float get(String name, float defvalue) | |
| throws IOException, IllegalArgumentException | |
| { | |
| ObjectStreamField field = getField(name, Float.TYPE); | |
| if (field == null) | |
| return defvalue; | |
| int off = field.getOffset(); | |
| return Float.intBitsToFloat(((prim_field_data[off++] & 0xFF) << 24) | |
| | ((prim_field_data[off++] & 0xFF) << 16) | |
| | ((prim_field_data[off++] & 0xFF) << 8) | |
| | (prim_field_data[off] & 0xFF)); | |
| } | |
| public double get(String name, double defvalue) | |
| throws IOException, IllegalArgumentException | |
| { | |
| ObjectStreamField field = getField(name, Double.TYPE); | |
| if (field == null) | |
| return defvalue; | |
| int off = field.getOffset(); | |
| return Double.longBitsToDouble | |
| ( (long) (((prim_field_data[off++] & 0xFFL) << 56) | |
| | ((prim_field_data[off++] & 0xFFL) << 48) | |
| | ((prim_field_data[off++] & 0xFFL) << 40) | |
| | ((prim_field_data[off++] & 0xFFL) << 32) | |
| | ((prim_field_data[off++] & 0xFF) << 24) | |
| | ((prim_field_data[off++] & 0xFF) << 16) | |
| | ((prim_field_data[off++] & 0xFF) << 8) | |
| | (prim_field_data[off] & 0xFF))); | |
| } | |
| public Object get(String name, Object defvalue) | |
| throws IOException, IllegalArgumentException | |
| { | |
| ObjectStreamField field = | |
| getField(name, defvalue == null ? null : defvalue.getClass ()); | |
| if (field == null) | |
| return defvalue; | |
| return objs[field.getOffset()]; | |
| } | |
| private ObjectStreamField getField(String name, Class type) | |
| throws IllegalArgumentException | |
| { | |
| ObjectStreamField field = clazz.getField(name); | |
| boolean illegal = false; | |
| // XXX This code is horrible and needs to be rewritten! | |
| try | |
| { | |
| try | |
| { | |
| Class field_type = field.getType(); | |
| if (type == field_type || | |
| (type == null && !field_type.isPrimitive())) | |
| { | |
| /* See defaulted */ | |
| return field; | |
| } | |
| illegal = true; | |
| throw new IllegalArgumentException | |
| ("Field requested is of type " | |
| + field_type.getName() | |
| + ", but requested type was " | |
| + (type == null ? "Object" : type.getName())); | |
| } | |
| catch (NullPointerException _) | |
| { | |
| /* Here we catch NullPointerException, because it may | |
| only come from the call 'field.getType()'. If field | |
| is null, we have to return null and classpath ethic | |
| say we must try to avoid 'if (xxx == null)'. | |
| */ | |
| } | |
| catch (IllegalArgumentException e) | |
| { | |
| throw e; | |
| } | |
| return null; | |
| } | |
| finally | |
| { | |
| /* If this is an unassigned field we should return | |
| * the default value. | |
| */ | |
| if (!illegal && field != null && !field.isToSet() && field.isPersistent()) | |
| return null; | |
| /* We do not want to modify transient fields. They should | |
| * be left to 0. | |
| */ | |
| try | |
| { | |
| Field f = clazz.forClass().getDeclaredField(name); | |
| if (Modifier.isTransient(f.getModifiers())) | |
| throw new IllegalArgumentException | |
| ("no such field (non transient) " + name); | |
| if (field == null && f.getType() != type) | |
| throw new IllegalArgumentException | |
| ("Invalid requested type for field " + name); | |
| } | |
| catch (NoSuchFieldException e) | |
| { | |
| if (field == null) | |
| throw new IllegalArgumentException(e); | |
| } | |
| } | |
| } | |
| }; | |
| fieldsAlreadyRead = true; | |
| return prereadFields; | |
| } | |
| /** | |
| * Protected constructor that allows subclasses to override | |
| * deserialization. This constructor should be called by subclasses | |
| * that wish to override <code>readObject (Object)</code>. This | |
| * method does a security check <i>NOTE: currently not | |
| * implemented</i>, then sets a flag that informs | |
| * <code>readObject (Object)</code> to call the subclasses | |
| * <code>readObjectOverride (Object)</code> method. | |
| * | |
| * @see #readObjectOverride() | |
| */ | |
| protected ObjectInputStream() | |
| throws IOException, SecurityException | |
| { | |
| SecurityManager sec_man = System.getSecurityManager(); | |
| if (sec_man != null) | |
| sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); | |
| this.useSubclassMethod = true; | |
| } | |
| /** | |
| * This method allows subclasses to override the default | |
| * de serialization mechanism provided by | |
| * <code>ObjectInputStream</code>. To make this method be used for | |
| * writing objects, subclasses must invoke the 0-argument | |
| * constructor on this class from their constructor. | |
| * | |
| * @see #ObjectInputStream() | |
| */ | |
| protected Object readObjectOverride() | |
| throws ClassNotFoundException, IOException, OptionalDataException | |
| { | |
| throw new IOException("Subclass of ObjectInputStream must implement readObjectOverride"); | |
| } | |
| /** | |
| * Assigns the next available handle to <code>obj</code>. | |
| * | |
| * @param obj The object for which we want a new handle. | |
| * @return A valid handle for the specified object. | |
| */ | |
| private int assignNewHandle(Object obj) | |
| { | |
| this.objectLookupTable.put(new Integer(this.nextOID), | |
| new ObjectIdentityWrapper(obj)); | |
| return this.nextOID++; | |
| } | |
| private Object processResolution(ObjectStreamClass osc, Object obj, int handle) | |
| throws IOException | |
| { | |
| if (osc != null && obj instanceof Serializable) | |
| { | |
| try | |
| { | |
| Method m = osc.readResolveMethod; | |
| if(m != null) | |
| { | |
| obj = m.invoke(obj, new Object[] {}); | |
| } | |
| } | |
| catch (IllegalAccessException ignore) | |
| { | |
| } | |
| catch (InvocationTargetException exception) | |
| { | |
| Throwable cause = exception.getCause(); | |
| if (cause instanceof ObjectStreamException) | |
| throw (ObjectStreamException) cause; | |
| else if (cause instanceof RuntimeException) | |
| throw (RuntimeException) cause; | |
| else if (cause instanceof Error) | |
| throw (Error) cause; | |
| } | |
| } | |
| if (this.resolveEnabled) | |
| obj = resolveObject(obj); | |
| this.objectLookupTable.put(new Integer(handle), | |
| new ObjectIdentityWrapper(obj)); | |
| return obj; | |
| } | |
| private void clearHandles() | |
| { | |
| this.objectLookupTable.clear(); | |
| this.nextOID = baseWireHandle; | |
| } | |
| private void readNextBlock() throws IOException | |
| { | |
| byte marker = this.realInputStream.readByte(); | |
| while (marker == TC_RESET) | |
| { | |
| if(dump) dumpElementln("RESET"); | |
| clearHandles(); | |
| marker = this.realInputStream.readByte(); | |
| } | |
| readNextBlock(marker); | |
| } | |
| private void readNextBlock(byte marker) throws IOException | |
| { | |
| if (marker == TC_BLOCKDATA) | |
| { | |
| if(dump) dumpElement("BLOCK DATA SIZE="); | |
| this.blockDataBytes = this.realInputStream.readUnsignedByte(); | |
| if(dump) dumpElementln (Integer.toString(this.blockDataBytes)); | |
| } | |
| else if (marker == TC_BLOCKDATALONG) | |
| { | |
| if(dump) dumpElement("BLOCK DATA LONG SIZE="); | |
| this.blockDataBytes = this.realInputStream.readInt(); | |
| if(dump) dumpElementln (Integer.toString(this.blockDataBytes)); | |
| } | |
| else | |
| { | |
| throw new EOFException("Attempt to read primitive data, but no data block is active."); | |
| } | |
| if (this.blockData.length < this.blockDataBytes) | |
| this.blockData = new byte[this.blockDataBytes]; | |
| this.realInputStream.readFully (this.blockData, 0, this.blockDataBytes); | |
| this.blockDataPosition = 0; | |
| } | |
| private void readArrayElements (Object array, Class clazz) | |
| throws ClassNotFoundException, IOException | |
| { | |
| if (clazz.isPrimitive()) | |
| { | |
| if (clazz == Boolean.TYPE) | |
| { | |
| boolean[] cast_array = (boolean[])array; | |
| for (int i=0; i < cast_array.length; i++) | |
| cast_array[i] = this.realInputStream.readBoolean(); | |
| return; | |
| } | |
| if (clazz == Byte.TYPE) | |
| { | |
| byte[] cast_array = (byte[])array; | |
| for (int i=0; i < cast_array.length; i++) | |
| cast_array[i] = this.realInputStream.readByte(); | |
| return; | |
| } | |
| if (clazz == Character.TYPE) | |
| { | |
| char[] cast_array = (char[])array; | |
| for (int i=0; i < cast_array.length; i++) | |
| cast_array[i] = this.realInputStream.readChar(); | |
| return; | |
| } | |
| if (clazz == Double.TYPE) | |
| { | |
| double[] cast_array = (double[])array; | |
| for (int i=0; i < cast_array.length; i++) | |
| cast_array[i] = this.realInputStream.readDouble(); | |
| return; | |
| } | |
| if (clazz == Float.TYPE) | |
| { | |
| float[] cast_array = (float[])array; | |
| for (int i=0; i < cast_array.length; i++) | |
| cast_array[i] = this.realInputStream.readFloat(); | |
| return; | |
| } | |
| if (clazz == Integer.TYPE) | |
| { | |
| int[] cast_array = (int[])array; | |
| for (int i=0; i < cast_array.length; i++) | |
| cast_array[i] = this.realInputStream.readInt(); | |
| return; | |
| } | |
| if (clazz == Long.TYPE) | |
| { | |
| long[] cast_array = (long[])array; | |
| for (int i=0; i < cast_array.length; i++) | |
| cast_array[i] = this.realInputStream.readLong(); | |
| return; | |
| } | |
| if (clazz == Short.TYPE) | |
| { | |
| short[] cast_array = (short[])array; | |
| for (int i=0; i < cast_array.length; i++) | |
| cast_array[i] = this.realInputStream.readShort(); | |
| return; | |
| } | |
| } | |
| else | |
| { | |
| Object[] cast_array = (Object[])array; | |
| for (int i=0; i < cast_array.length; i++) | |
| cast_array[i] = readObject(); | |
| } | |
| } | |
| private void readFields (Object obj, ObjectStreamClass stream_osc) | |
| throws ClassNotFoundException, IOException | |
| { | |
| ObjectStreamField[] fields = stream_osc.fieldMapping; | |
| for (int i = 0; i < fields.length; i += 2) | |
| { | |
| ObjectStreamField stream_field = fields[i]; | |
| ObjectStreamField real_field = fields[i + 1]; | |
| boolean read_value = (stream_field != null && stream_field.getOffset() >= 0 && stream_field.isToSet()); | |
| boolean set_value = (real_field != null && real_field.isToSet()); | |
| String field_name; | |
| char type; | |
| if (stream_field != null) | |
| { | |
| field_name = stream_field.getName(); | |
| type = stream_field.getTypeCode(); | |
| } | |
| else | |
| { | |
| field_name = real_field.getName(); | |
| type = real_field.getTypeCode(); | |
| } | |
| switch(type) | |
| { | |
| case 'Z': | |
| { | |
| boolean value = | |
| read_value ? this.realInputStream.readBoolean() : false; | |
| if (dump && read_value && set_value) | |
| dumpElementln(" " + field_name + ": " + value); | |
| if (set_value) | |
| real_field.setBooleanField(obj, value); | |
| break; | |
| } | |
| case 'B': | |
| { | |
| byte value = | |
| read_value ? this.realInputStream.readByte() : 0; | |
| if (dump && read_value && set_value) | |
| dumpElementln(" " + field_name + ": " + value); | |
| if (set_value) | |
| real_field.setByteField(obj, value); | |
| break; | |
| } | |
| case 'C': | |
| { | |
| char value = | |
| read_value ? this.realInputStream.readChar(): 0; | |
| if (dump && read_value && set_value) | |
| dumpElementln(" " + field_name + ": " + value); | |
| if (set_value) | |
| real_field.setCharField(obj, value); | |
| break; | |
| } | |
| case 'D': | |
| { | |
| double value = | |
| read_value ? this.realInputStream.readDouble() : 0; | |
| if (dump && read_value && set_value) | |
| dumpElementln(" " + field_name + ": " + value); | |
| if (set_value) | |
| real_field.setDoubleField(obj, value); | |
| break; | |
| } | |
| case 'F': | |
| { | |
| float value = | |
| read_value ? this.realInputStream.readFloat() : 0; | |
| if (dump && read_value && set_value) | |
| dumpElementln(" " + field_name + ": " + value); | |
| if (set_value) | |
| real_field.setFloatField(obj, value); | |
| break; | |
| } | |
| case 'I': | |
| { | |
| int value = | |
| read_value ? this.realInputStream.readInt() : 0; | |
| if (dump && read_value && set_value) | |
| dumpElementln(" " + field_name + ": " + value); | |
| if (set_value) | |
| real_field.setIntField(obj, value); | |
| break; | |
| } | |
| case 'J': | |
| { | |
| long value = | |
| read_value ? this.realInputStream.readLong() : 0; | |
| if (dump && read_value && set_value) | |
| dumpElementln(" " + field_name + ": " + value); | |
| if (set_value) | |
| real_field.setLongField(obj, value); | |
| break; | |
| } | |
| case 'S': | |
| { | |
| short value = | |
| read_value ? this.realInputStream.readShort() : 0; | |
| if (dump && read_value && set_value) | |
| dumpElementln(" " + field_name + ": " + value); | |
| if (set_value) | |
| real_field.setShortField(obj, value); | |
| break; | |
| } | |
| case 'L': | |
| case '[': | |
| { | |
| Object value = | |
| read_value ? readObject() : null; | |
| if (set_value) | |
| real_field.setObjectField(obj, value); | |
| break; | |
| } | |
| default: | |
| throw new InternalError("Invalid type code: " + type); | |
| } | |
| } | |
| } | |
| // Toggles writing primitive data to block-data buffer. | |
| private boolean setBlockDataMode (boolean on) | |
| { | |
| boolean oldmode = this.readDataFromBlock; | |
| this.readDataFromBlock = on; | |
| if (on) | |
| this.dataInputStream = this.blockDataInput; | |
| else | |
| this.dataInputStream = this.realInputStream; | |
| return oldmode; | |
| } | |
| // returns a new instance of REAL_CLASS that has been constructed | |
| // only to the level of CONSTRUCTOR_CLASS (a super class of REAL_CLASS) | |
| private Object newObject (Class real_class, Constructor constructor) | |
| throws ClassNotFoundException, IOException | |
| { | |
| if (constructor == null) | |
| throw new InvalidClassException("Missing accessible no-arg base class constructor for " + real_class.getName()); | |
| try | |
| { | |
| return VMObjectInputStream.allocateObject(real_class, constructor.getDeclaringClass(), constructor); | |
| } | |
| catch (InstantiationException e) | |
| { | |
| throw (ClassNotFoundException) new ClassNotFoundException | |
| ("Instance of " + real_class + " could not be created").initCause(e); | |
| } | |
| } | |
| // runs all registered ObjectInputValidations in prioritized order | |
| // on OBJ | |
| private void invokeValidators() throws InvalidObjectException | |
| { | |
| try | |
| { | |
| Iterator it = currentObjectValidators.iterator(); | |
| while(it.hasNext()) | |
| { | |
| ValidatorAndPriority vap = (ValidatorAndPriority) it.next(); | |
| ObjectInputValidation validator = vap.validator; | |
| validator.validateObject(); | |
| } | |
| } | |
| finally | |
| { | |
| currentObjectValidators = null; | |
| } | |
| } | |
| private void callReadMethod (Method readObject, Class klass, Object obj) | |
| throws ClassNotFoundException, IOException | |
| { | |
| try | |
| { | |
| readObject.invoke(obj, new Object[] { this }); | |
| } | |
| catch (InvocationTargetException x) | |
| { | |
| /* Rethrow if possible. */ | |
| Throwable exception = x.getTargetException(); | |
| if (exception instanceof RuntimeException) | |
| throw (RuntimeException) exception; | |
| if (exception instanceof IOException) | |
| throw (IOException) exception; | |
| if (exception instanceof ClassNotFoundException) | |
| throw (ClassNotFoundException) exception; | |
| throw (IOException) new IOException( | |
| "Exception thrown from readObject() on " + klass).initCause(x); | |
| } | |
| catch (Exception x) | |
| { | |
| throw (IOException) new IOException( | |
| "Failure invoking readObject() on " + klass).initCause(x); | |
| } | |
| // Invalidate fields which has been read through readFields. | |
| prereadFields = null; | |
| } | |
| private static final int BUFFER_SIZE = 1024; | |
| private DataInputStream realInputStream; | |
| private DataInputStream dataInputStream; | |
| private DataInputStream blockDataInput; | |
| private int blockDataPosition; | |
| private int blockDataBytes; | |
| private byte[] blockData; | |
| private boolean useSubclassMethod; | |
| private int nextOID; | |
| private boolean resolveEnabled; | |
| private Hashtable objectLookupTable; | |
| private Object currentObject; | |
| private ObjectStreamClass currentObjectStreamClass; | |
| private TreeSet currentObjectValidators; | |
| private boolean readDataFromBlock; | |
| private boolean fieldsAlreadyRead; | |
| private Hashtable classLookupTable; | |
| private GetField prereadFields; | |
| private static boolean dump; | |
| // The nesting depth for debugging output | |
| private int depth = 0; | |
| private static final boolean DEBUG = false; | |
| private void dumpElement (String msg) | |
| { | |
| System.out.print(msg); | |
| } | |
| private void dumpElementln (String msg) | |
| { | |
| System.out.println(msg); | |
| for (int i = 0; i < depth; i++) | |
| System.out.print (" "); | |
| System.out.print (Thread.currentThread() + ": "); | |
| } | |
| // used to keep a prioritized list of object validators | |
| private static final class ValidatorAndPriority implements Comparable | |
| { | |
| int priority; | |
| ObjectInputValidation validator; | |
| ValidatorAndPriority (ObjectInputValidation validator, int priority) | |
| { | |
| this.priority = priority; | |
| this.validator = validator; | |
| } | |
| public int compareTo (Object o) | |
| { | |
| ValidatorAndPriority vap = (ValidatorAndPriority)o; | |
| return this.priority - vap.priority; | |
| } | |
| } | |
| } | |