From harmony-commits-return-286-apmail-incubator-harmony-commits-archive=incubator.apache.org@incubator.apache.org Thu Dec 01 06:18:22 2005 Return-Path: Delivered-To: apmail-incubator-harmony-commits-archive@www.apache.org Received: (qmail 47479 invoked from network); 1 Dec 2005 06:18:21 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199) by minotaur.apache.org with SMTP; 1 Dec 2005 06:18:21 -0000 Received: (qmail 646 invoked by uid 500); 1 Dec 2005 06:15:48 -0000 Delivered-To: apmail-incubator-harmony-commits-archive@incubator.apache.org Received: (qmail 98914 invoked by uid 500); 1 Dec 2005 06:14:19 -0000 Mailing-List: contact harmony-commits-help@incubator.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: harmony-dev@incubator.apache.org Delivered-To: mailing list harmony-commits@incubator.apache.org Received: (qmail 98149 invoked by uid 99); 1 Dec 2005 06:13:02 -0000 X-ASF-Spam-Status: No, hits=-9.4 required=10.0 tests=ALL_TRUSTED,NO_REAL_NAME X-Spam-Check-By: apache.org Received: from [209.237.227.194] (HELO minotaur.apache.org) (209.237.227.194) by apache.org (qpsmtpd/0.29) with SMTP; Wed, 30 Nov 2005 22:12:02 -0800 Received: (qmail 43874 invoked by uid 65534); 1 Dec 2005 06:11:20 -0000 Message-ID: <20051201061120.43873.qmail@minotaur.apache.org> Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r350181 [97/198] - in /incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core: ./ depends/ depends/files/ depends/jars/ depends/libs/ depends/libs/linux.IA32/ depends/libs/win.IA32/ depends/oss/ depends/oss/linux.IA32/ depends/oss/win.I... Date: Thu, 01 Dec 2005 06:04:00 -0000 To: harmony-commits@incubator.apache.org From: geirm@apache.org X-Mailer: svnmailer-1.0.5 X-Spam-Rating: minotaur.apache.org 1.6.2 0/1000/N Added: incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/luni/src/java/io/ObjectInputStream.java URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/luni/src/java/io/ObjectInputStream.java?rev=350181&view=auto ============================================================================== --- incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/luni/src/java/io/ObjectInputStream.java (added) +++ incubator/harmony/enhanced/trunk/sandbox/contribs/ibm_core/java-src/luni/src/java/io/ObjectInputStream.java Wed Nov 30 21:29:27 2005 @@ -0,0 +1,2546 @@ +/* Copyright 1998, 2005 The Apache Software Foundation or its licensors, as applicable + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package java.io; + + +import java.lang.reflect.Constructor; +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.ArrayList; +import java.util.Hashtable; +import java.util.IdentityHashMap; + +import com.ibm.oti.util.PriviAction; + +/** + * An ObjectInputStream can be used to load Java objects from a stream where the + * objects were saved using an ObjectOutputStream. Primitive data (ints, bytes, + * chars, etc) can also be loaded if the data was saved as primitive types as + * well. It is invalid to attempt to read an object as primitive data. + * + * @see ObjectOutputStream + * @see ObjectInput + * @see Serializable + * @see Externalizable + */ +public class ObjectInputStream extends InputStream implements ObjectInput, + ObjectStreamConstants { + private static InputStream emptyStream = new ByteArrayInputStream( + new byte[0]); + + private boolean hasPushbackTC; // If the receiver has already read & not + // consumed a TC code + + private byte pushbackTC; // Pushback TC code if teh variable above is + // true + + private int nestedLevels; // How many nested levels to readObject. When we + // reach 0 we have to validate the graph then + // reset it + + private int currentHandle; // All objects are assigned an ID (integer + // handle) + + private DataInputStream input; // Where we read from + + private DataInputStream primitiveTypes; // Where we read primitive types + // from + + private InputStream primitiveData = emptyStream;// Where we keep primitive + // type data + + private boolean enableResolve; // Resolve object is a mechanism for + // replacement + + private Hashtable objectsRead; // Table mapping Integer (handle) -> Object + + private Object currentObject; // Used by defaultReadObject + + private ObjectStreamClass currentClass; // Used by defaultReadObject + + private InputValidationDesc[] validations; // All validations to be + // executed when the complete + // graph is read. See inner type + // below. + + private boolean subclassOverridingImplementation;// Allows the receiver + // to decide if it needs + // to call + // readObjectOverride + + private ClassLoader callerClassLoader; // Original caller's class loader, + // used to perform class lookups + + private boolean mustResolve = true; // false when reading missing fields + + private Integer descriptorHandle; // Handle for the current class + // descriptor + + private IdentityHashMap readResolveCache; // cache for readResolve methods + + // Internal type used to keep track of validators & corresponding priority + class InputValidationDesc { + ObjectInputValidation validator; + + int priority; + } + + /** + * Inner class to provide access to serializable fields + */ + abstract static public class GetField { + /** + * @return ObjectStreamClass + */ + public abstract ObjectStreamClass getObjectStreamClass(); + + /** + * @param name + * @return true if the default value is set, + * false otherwise + * + * @throws IOException + * @throws IllegalArgumentException + */ + public abstract boolean defaulted(String name) throws IOException, + IllegalArgumentException; + + /** + * @param name + * @param defaultValue + * @return the value + * + * @throws IOException + * @throws IllegalArgumentException + */ + public abstract boolean get(String name, boolean defaultValue) + throws IOException, IllegalArgumentException; + + /** + * @param name + * @param defaultValue + * @return the value + * + * @throws IOException + * @throws IllegalArgumentException + */ + public abstract char get(String name, char defaultValue) + throws IOException, IllegalArgumentException; + + /** + * @param name + * @param defaultValue + * @return the value + * + * @throws IOException + * @throws IllegalArgumentException + */ + public abstract byte get(String name, byte defaultValue) + throws IOException, IllegalArgumentException; + + /** + * @param name + * @param defaultValue + * @return the value + * + * @throws IOException + * @throws IllegalArgumentException + */ + public abstract short get(String name, short defaultValue) + throws IOException, IllegalArgumentException; + + /** + * @param name + * @param defaultValue + * @return the value + * + * @throws IOException + * @throws IllegalArgumentException + */ + public abstract int get(String name, int defaultValue) + throws IOException, IllegalArgumentException; + + /** + * @param name + * @param defaultValue + * @return the value + * + * @throws IOException + * @throws IllegalArgumentException + */ + public abstract long get(String name, long defaultValue) + throws IOException, IllegalArgumentException; + + /** + * @param name + * @param defaultValue + * @return the value + * + * @throws IOException + * @throws IllegalArgumentException + */ + public abstract float get(String name, float defaultValue) + throws IOException, IllegalArgumentException; + + /** + * @param name + * @param defaultValue + * @return the value + * + * @throws IOException + * @throws IllegalArgumentException + */ + public abstract double get(String name, double defaultValue) + throws IOException, IllegalArgumentException; + + /** + * @param name + * @param defaultValue + * @return the value + * + * @throws IOException + * @throws IllegalArgumentException + */ + public abstract Object get(String name, Object defaultValue) + throws IOException, IllegalArgumentException; + } + + /** + * Constructs a new ObjectInputStream. The representation and proper + * initialization is on the hands of subclasses. + * + * @throws IOException + * If not called from a subclass + * @throws SecurityException + * If subclasses are not allowed + * + * @see SecurityManager#checkPermission(java.security.Permission) + */ + protected ObjectInputStream() throws IOException, SecurityException { + super(); + SecurityManager currentManager = System.getSecurityManager(); + if (currentManager != null) + currentManager.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); + // WARNING - we should throw IOException if not called from a subclass + // according to the JavaDoc. Add the test. + this.subclassOverridingImplementation = true; + } + + /** + * Constructs a new ObjectInputStream on the InputStream input. + * All reads are now filtered through this stream. + * + * @param input + * The non-null InputStream to filter reads on. + * + * @throws IOException + * If an IO exception happened when reading the stream header. + * @throws StreamCorruptedException + * If the underlying stream does not contain serialized objects + * that can be read. + */ + public ObjectInputStream(InputStream input) + throws StreamCorruptedException, IOException { + final Class implementationClass = getClass(); + final Class thisClass = ObjectInputStream.class; + SecurityManager sm = System.getSecurityManager(); + if (sm != null && implementationClass != thisClass) { + boolean mustCheck = ((Boolean) AccessController + .doPrivileged(new PrivilegedAction() { + public Object run() { + try { + Method method = implementationClass + .getMethod( + "readFields", //$NON-NLS-1$ + ObjectStreamClass.EMPTY_CONSTRUCTOR_PARAM_TYPES); + if (method.getDeclaringClass() != thisClass) { + return Boolean.TRUE; + } + } catch (NoSuchMethodException e) { + } + try { + Method method = implementationClass + .getMethod( + "readUnshared", //$NON-NLS-1$ + ObjectStreamClass.EMPTY_CONSTRUCTOR_PARAM_TYPES); + if (method.getDeclaringClass() != thisClass) { + return Boolean.TRUE; + } + } catch (NoSuchMethodException e) { + } + return Boolean.FALSE; + } + })).booleanValue(); + if (mustCheck) { + sm + .checkPermission(ObjectStreamConstants.SUBCLASS_IMPLEMENTATION_PERMISSION); + } + } + this.input = (input instanceof DataInputStream) ? (DataInputStream) input + : new DataInputStream(input); + primitiveTypes = new DataInputStream(this); + enableResolve = false; + this.subclassOverridingImplementation = false; + this.readResolveCache = new IdentityHashMap(); + resetState(); + nestedLevels = 0; + // So read...() methods can be used by + // subclasses during readStreamHeader() + primitiveData = this.input; + // Has to be done here according to the specification + readStreamHeader(); + primitiveData = emptyStream; + } + + /** + * Returns the number of bytes of primitive data available from the + * receiver. It should not be used at any arbitrary position; just when + * reading primitive data types (ints, chars, etc). + * + * @return the number of available primitive data bytes + * + * @throws IOException + * If any IO problem occurred when trying to compute the bytes + * available. + */ + public int available() throws IOException { + // returns 0 if next data is an object, or N if reading primitive types + checkReadPrimitiveTypes(); + return primitiveData.available(); + } + + /** + * Checks to see if it is ok to read primitive types at this point from the + * receiver. One is not supposed to read primitive types when about to read + * an object, for example, so an exception has to be thrown. + * + * @throws IOException + * If any IO problem occurred when trying to read primitive type + * or if it is illegal to read primitive types + */ + private void checkReadPrimitiveTypes() throws IOException { + // If we still have primitive data, it is ok to read primitive data + if (primitiveData == input || primitiveData.available() > 0) + return; + + // If we got here either we had no Stream previously created or + // we no longer have data in that one, so get more bytes + + do { + int next = 0; + if (hasPushbackTC) { + hasPushbackTC = false; + } else { + next = input.read(); + pushbackTC = (byte) next; + } + switch (pushbackTC) { + case TC_BLOCKDATA: + primitiveData = new ByteArrayInputStream(readBlockData()); + return; + case TC_BLOCKDATALONG: + primitiveData = new ByteArrayInputStream(readBlockDataLong()); + return; + case TC_RESET: + resetState(); + break; + default: + if (next != -1) + pushbackTC(); + return; + } + // Only TC_RESET falls through + } while (true); + } + + /** + * Close this ObjectInputStream. This implementation closes the target + * stream. + * + * @throws IOException + * If an error occurs attempting to close this stream. + */ + public void close() throws IOException { + input.close(); + } + + /** + * Default method to read objects from the receiver. Fields defined in the + * object's class and superclasses (which are Serializable) will be read. + * + * @throws IOException + * If an IO error occurs attempting to read the object data + * @throws ClassNotFoundException + * If the class of the object cannot be found + * @throws NotActiveException + * If this method is not called from readObject() + * + * @see ObjectOutputStream#defaultWriteObject + */ + public void defaultReadObject() throws IOException, ClassNotFoundException, + NotActiveException { + // We can't be called from just anywhere. There are rules. + if (currentObject != null || !mustResolve) + readFieldValues(currentObject, currentClass); + else + throw new NotActiveException(); + } + + /** + * Enables/disables object replacement for the receiver. By default this is + * not enabled. Only trusted subclasses (loaded with system class loader) + * can override this behavior. + * + * @param enable + * if true, enables replacement. If false, disables replacement. + * @return the previous configuration (if it was enabled or disabled) + * + * @throws SecurityException + * If the class of the receiver is not trusted + * + * @see #resolveObject + * @see ObjectOutputStream#enableReplaceObject + */ + + protected boolean enableResolveObject(boolean enable) + throws SecurityException { + if (enable) { + // The Stream has to be trusted for this feature to be enabled. + // trusted means the stream's classloader has to be null + SecurityManager currentManager = System.getSecurityManager(); + if (currentManager != null) + currentManager.checkPermission(SUBSTITUTION_PERMISSION); + } + boolean originalValue = enableResolve; + enableResolve = enable; + return originalValue; + } + + /** + * Checks if two classes belong to the same package and returns true in the + * positive case. Return false otherwise. + * + * @param c1 + * one of the classes to test + * @param c2 + * the other class to test + * @return true if the two classes belong to the same + * package, false otherwise + */ + private boolean inSamePackage(Class c1, Class c2) { + String nameC1 = c1.getName(); + String nameC2 = c2.getName(); + int indexDotC1 = nameC1.lastIndexOf('.'); + int indexDotC2 = nameC2.lastIndexOf('.'); + if (indexDotC1 != indexDotC1) + return false; // cannot be in the same package if indices are not + // the same + if (indexDotC1 < 0) + return true; // both of them are in default package + return nameC1.substring(0, indexDotC1).equals( + nameC2.substring(0, indexDotC2)); + } + + /** + * Create and return a new instance of class instantiationClass + * but running the constructor defined in class + * constructorClass (same as instantiationClass + * or a superclass). + * + * Has to be native to avoid visibility rules and to be able to have + * instantiationClass not the same as + * constructorClass (no such API in java.lang.reflect). + * + * @param instantiationClass + * The new object will be an instance of this class + * @param constructorClass + * The empty constructor to run will be in this class + * @return the object created from instantiationClass + */ + private static native Object newInstance(Class instantiationClass, + Class constructorClass); + + /** + * Return the next int handle to be used to indicate cyclic + * references being loaded from the stream. + * + * @return the next handle to represent the next cyclic reference + */ + private int nextHandle() { + return this.currentHandle++; + } + + /** + * Return the next token code (TC) from the receiver, which indicates what + * kind of object follows + * + * @return the next TC from the receiver + * + * @throws IOException + * If an IO error occurs + * + * @see ObjectStreamConstants + */ + private byte nextTC() throws IOException { + if (hasPushbackTC) + hasPushbackTC = false; // We are consuming it + else + // Just in case a later call decides to really push it back, + // we don't require the caller to pass it as parameter + pushbackTC = input.readByte(); + return pushbackTC; + } + + /** + * Pushes back the last TC code read + * + */ + private void pushbackTC() { + hasPushbackTC = true; + } + + /** + * Reads a single byte from the receiver and returns the result as an int. + * The low-order byte is returned or -1 of the end of stream was + * encountered. + * + * @return The byte read or -1 if end of stream. + * + * @throws IOException + * If an IO exception happened when reading the primitive data. + */ + public int read() throws IOException { + checkReadPrimitiveTypes(); + return primitiveData.read(); + } + + /** + * Reads at most length bytes from the receiver and stores + * them in byte array buffer starting at offset + * offset. Answer the number of bytes actually read or -1 if + * no bytes were read and end of stream was encountered. + * + * @param buffer + * the byte array in which to store the read bytes. + * @param offset + * the offset in buffer to store the read bytes. + * @param length + * the maximum number of bytes to store in buffer. + * @return The number of bytes actually read or -1 if end of stream. + * + * @throws IOException + * If an IO exception happened when reading the primitive data. + */ + public int read(byte[] buffer, int offset, int length) throws IOException { + if (buffer != null) { + // avoid int overflow + if (0 <= offset && offset <= buffer.length && 0 <= length + && length <= buffer.length - offset) { + if (length == 0) + return 0; + checkReadPrimitiveTypes(); + return primitiveData.read(buffer, offset, length); + } + throw new ArrayIndexOutOfBoundsException(); + } + throw new NullPointerException(); + } + + /** + * Reads and returns an array of raw bytes with primitive data. The array + * will have up to 255 bytes. The primitive data will be in the format + * described by DataOutputStream. + * + * @return The primitive data read, as raw bytes + * + * @throws IOException + * If an IO exception happened when reading the primitive data. + */ + private byte[] readBlockData() throws IOException { + byte[] result = new byte[input.readByte() & 0xff]; + input.readFully(result); + return result; + } + + /** + * Reads and returns an array of raw bytes with primitive data. The array + * will have more than 255 bytes. The primitive data will be in the format + * described by DataOutputStream. + * + * @return The primitive data read, as raw bytes + * + * @throws IOException + * If an IO exception happened when reading the primitive data. + */ + private byte[] readBlockDataLong() throws IOException { + byte[] result = new byte[input.readInt()]; + input.readFully(result); + return result; + } + + /** + * Reads and returns primitive data of type boolean read from the receiver + * + * @return A boolean saved as primitive data using + * ObjectOutputStream.writeBoolean() + * + * @throws IOException + * If an IO exception happened when reading the primitive data. + */ + public boolean readBoolean() throws IOException { + return primitiveTypes.readBoolean(); + } + + /** + * Reads and returns primitive data of type byte read from the receiver + * + * @return A byte saved as primitive data using + * ObjectOutputStream.writeByte() + * + * @throws IOException + * If an IO exception happened when reading the primitive data. + */ + public byte readByte() throws IOException { + return primitiveTypes.readByte(); + } + + /** + * Reads and returns primitive data of type char read from the receiver + * + * @return A char saved as primitive data using + * ObjectOutputStream.writeChar() + * + * @throws IOException + * If an IO exception happened when reading the primitive data. + */ + public char readChar() throws IOException { + return primitiveTypes.readChar(); + } + + /** + * Reads and discards block data and objects until TC_ENDBLOCKDATA is found. + * + * @throws IOException + * If an IO exception happened when reading the optional class + * annotation. + * @throws ClassNotFoundException + * If the class corresponding to the class descriptor could not + * be found. + */ + private void discardData() throws ClassNotFoundException, IOException { + primitiveData = emptyStream; + boolean resolve = mustResolve; + mustResolve = false; + do { + byte tc = nextTC(); + if (tc == TC_ENDBLOCKDATA) { + mustResolve = resolve; + return; // End of annotation + } + readContent(tc); + } while (true); + } + + /** + * Reads a class descriptor (an ObjectStreamClass) from the + * stream. + * + * @return the class descriptor read from the stream + * + * @throws IOException + * If an IO exception happened when reading the class + * descriptor. + * @throws ClassNotFoundException + * If the class corresponding to the class descriptor could not + * be found. + */ + private ObjectStreamClass readClassDesc() throws ClassNotFoundException, + IOException { + byte tc = nextTC(); + switch (tc) { + case TC_CLASSDESC: + return readNewClassDesc(false); + case TC_PROXYCLASSDESC: + Class proxyClass = readNewProxyClassDesc(); + ObjectStreamClass streamClass = ObjectStreamClass + .lookup(proxyClass); + streamClass.setLoadFields(new ObjectStreamField[0]); + registerObjectRead(streamClass, new Integer(nextHandle())); + streamClass.setSuperclass(readClassDesc()); + return streamClass; + case TC_REFERENCE: + return (ObjectStreamClass) readCyclicReference(); + case TC_NULL: + return null; + default: + throw new StreamCorruptedException(com.ibm.oti.util.Msg.getString( + "K00d2", Integer.toHexString(tc & 0xff))); //$NON-NLS-1$ + } + } + + /** + * Reads the content of the receiver based on the previously read token + * tc. + * + * @param tc + * The token code for the next item in the stream + * @return the object read from the stream + * + * @throws IOException + * If an IO exception happened when reading the class + * descriptor. + * @throws ClassNotFoundException + * If the class corresponding to the object being read could not + * be found. + */ + private Object readContent(byte tc) throws ClassNotFoundException, + IOException { + switch (tc) { + case TC_BLOCKDATA: + return readBlockData(); + case TC_BLOCKDATALONG: + return readBlockDataLong(); + case TC_CLASS: + return readNewClass(false); + case TC_CLASSDESC: + return readNewClassDesc(false); + case TC_ARRAY: + return readNewArray(false); + case TC_OBJECT: + return readNewObject(false); + case TC_STRING: + return readNewString(false); + case TC_LONGSTRING: + return readNewLongString(false); + case TC_REFERENCE: + return readCyclicReference(); + case TC_NULL: + return null; + case TC_EXCEPTION: + Exception exc = readException(); + throw new WriteAbortedException(com.ibm.oti.util.Msg + .getString("K00d3"), exc); //$NON-NLS-1$ + case TC_RESET: + resetState(); + return null; + default: + throw new StreamCorruptedException(com.ibm.oti.util.Msg.getString( + "K00d2", Integer.toHexString(tc & 0xff))); //$NON-NLS-1$ + } + } + + /** + * Reads the content of the receiver based on the previously read token + * tc. Primitive data content is considered an error. + * + * @param unshared + * read the object unshared + * @return the object read from the stream + * + * @throws IOException + * If an IO exception happened when reading the class + * descriptor. + * @throws ClassNotFoundException + * If the class corresponding to the object being read could not + * be found. + */ + private Object readNonPrimitiveContent(boolean unshared) + throws ClassNotFoundException, IOException { + checkReadPrimitiveTypes(); + if (primitiveData.available() > 0) { + OptionalDataException e = new OptionalDataException(); + e.length = primitiveData.available(); + throw e; + } + + do { + byte tc = nextTC(); + switch (tc) { + case TC_CLASS: + return readNewClass(unshared); + case TC_CLASSDESC: + return readNewClassDesc(unshared); + case TC_ARRAY: + return readNewArray(unshared); + case TC_OBJECT: + return readNewObject(unshared); + case TC_STRING: + return readNewString(unshared); + case TC_LONGSTRING: + return readNewLongString(unshared); + case TC_REFERENCE: + if (unshared) { + readNewHandle(); + throw new InvalidObjectException( + "Unshared read of back reference"); //$NON-NLS-1$ + } + return readCyclicReference(); + case TC_NULL: + return null; + case TC_EXCEPTION: + Exception exc = readException(); + throw new WriteAbortedException(com.ibm.oti.util.Msg + .getString("K00d3"), exc); //$NON-NLS-1$ + case TC_RESET: + resetState(); + break; + case TC_ENDBLOCKDATA: // Can occur reading class annotation + pushbackTC(); + OptionalDataException e = new OptionalDataException(); + e.eof = true; + throw e; + default: + throw new StreamCorruptedException(com.ibm.oti.util.Msg + .getString("K00d2", Integer.toHexString(tc & 0xff))); //$NON-NLS-1$ + } + // Only TC_RESET falls through + } while (true); + } + + /** + * Reads the next item from the stream assuming it is a cyclic reference to + * an object previouly read. Return the actual object previously read. + * + * @return the object previously read from the stream + * + * @throws IOException + * If an IO exception happened when reading the class + * descriptor. + * @throws InvalidObjectException + * If the cyclic reference is not valid. + */ + private Object readCyclicReference() throws InvalidObjectException, + IOException { + return registeredObjectRead(readNewHandle()); + } + + /** + * Reads and returns primitive data of type double read from the receiver + * + * @return A double saved as primitive data using + * ObjectOutputStream.writeDouble() + * + * @throws IOException + * If an IO exception happened when reading the primitive data. + */ + public double readDouble() throws IOException { + return primitiveTypes.readDouble(); + } + + /** + * Read the next item assuming it is an exception. The exception is not a + * regular instance in the object graph, but the exception instance that + * happened (if any) when dumping the original object graph. The set of seen + * objects will be reset just before and just after loading this exception + * object. + *

+ * When exceptions are found normally in the object graph, they are loaded + * as a regular object, and not by this method. In that case, the set of + * "known objects" is not reset. + * + * + * @return the exception read + * + * @throws IOException + * If an IO exception happened when reading the exception + * object. + * @throws ClassNotFoundException + * If a class could not be found when reading the object graph + * for the exception + * @throws OptionalDataException + * If optional data could not be found when reading the + * exception graph + * @throws WriteAbortedException + * If another exception was caused when dumping this exception + */ + private Exception readException() throws WriteAbortedException, + OptionalDataException, ClassNotFoundException, IOException { + resetSeenObjects(); + + // Now we read the Throwable object that was saved + // WARNING - the grammar says it is a Throwable, but the + // WriteAbortedException + // constructor takes an Exception. So, we read an Exception from the + // stream + Exception exc = (Exception) readObject(); + + // We reset the receiver's state (the grammar has "reset" in normal + // font) + resetSeenObjects(); + return exc; + } + + /** + * Reads a collection of field descriptors (name, type name, etc) for the + * class descriptor cDesc (an ObjectStreamClass) + * + * @param cDesc + * The class descriptor (an ObjectStreamClass) + * for which to write field information + * + * @throws IOException + * If an IO exception happened when reading the field + * descriptors. + * @throws ClassNotFoundException + * If a class for one of the field types could not be found + * + * @see #readObject() + */ + private void readFieldDescriptors(ObjectStreamClass cDesc) + throws ClassNotFoundException, IOException { + short numFields = input.readShort(); + ObjectStreamField[] fields = new ObjectStreamField[numFields]; + + // We set it now, but each element will be inserted in the array further + // down + cDesc.setLoadFields(fields); + + // Check ObjectOutputStream.writeFieldDescriptors + for (short i = 0; i < numFields; i++) { + char typecode = (char) input.readByte(); + String fieldName = input.readUTF(); + boolean isPrimType = ObjectStreamClass.isPrimitiveType(typecode); + String classSig; + if (isPrimType) { + classSig = String.valueOf(typecode); + } else { + // The spec says it is a UTF, but experience shows they dump + // this String + // using writeObject (unlike the field name, which is saved with + // writeUTF) + classSig = (String) readObject(); + } + ObjectStreamField f = new ObjectStreamField(classSig, fieldName); + fields[i] = f; + } + } + + /** + * Reads the fields of the object being read from the stream. The stream + * will use the currently active getField object, allowing + * users to load emulated fields, for cross-loading compatibility when a + * class definition changes. + * + * @return the fields being read + * + * @throws IOException + * If an IO exception happened + * @throws ClassNotFoundException + * If a class of an object being de-serialized can not be found + * @throws NotActiveException + * If there is no object currently being loaded (invalid to call + * this method) + */ + + public GetField readFields() throws IOException, ClassNotFoundException, + NotActiveException { + // We can't be called from just anywhere. There are rules. + if (currentObject != null) { + EmulatedFieldsForLoading result = new EmulatedFieldsForLoading( + currentClass); + readFieldValues(result); + return result; + } + throw new NotActiveException(); + } + + /** + * reads a collection of field values for the emulated fields + * emulatedFields + * + * @param emulatedFields + * an EmulatedFieldsForLoading, concrete subclass + * of GetField + * + * @throws IOException + * If an IO exception happened when reading the field values. + * @throws InvalidClassException + * If an incompatible type is being assigned to an emulated + * field. + * @throws OptionalDataException + * If optional data could not be found when reading the + * exception graph + * + * @see #readFields + * @see #readObject() + */ + private void readFieldValues(EmulatedFieldsForLoading emulatedFields) + throws OptionalDataException, InvalidClassException, IOException { + EmulatedFields.ObjectSlot[] slots = emulatedFields.emulatedFields() + .slots(); + for (int i = 0; i < slots.length; i++) { + slots[i].defaulted = false; + Class type = slots[i].field.getType(); + if (type == Integer.TYPE) { + slots[i].fieldValue = new Integer(input.readInt()); + } else if (type == Byte.TYPE) { + slots[i].fieldValue = new Byte(input.readByte()); + } else if (type == Character.TYPE) { + slots[i].fieldValue = new Character(input.readChar()); + } else if (type == Short.TYPE) { + slots[i].fieldValue = new Short(input.readShort()); + } else if (type == Boolean.TYPE) { + slots[i].fieldValue = new Boolean(input.readBoolean()); + } else if (type == Long.TYPE) { + slots[i].fieldValue = new Long(input.readLong()); + } else if (type == Float.TYPE) { + slots[i].fieldValue = new Float(input.readFloat()); + } else if (type == Double.TYPE) { + slots[i].fieldValue = new Double(input.readDouble()); + } else { + // Either array or Object + try { + slots[i].fieldValue = readObject(); + } catch (ClassNotFoundException cnf) { + throw new InvalidClassException(cnf.toString()); // WARNING- + // Not + // sure + // this + // is + // the + // right + // thing + // to + // do. + // Write + // test + // case. + } + } + } + } + + /** + * Reads a collection of field values for the class descriptor + * classDesc (an ObjectStreamClass). The + * values will be used to set instance fields in object obj. + * This is the default mechanism, when emulated fields (an + * GetField) are not used. Actual values to load are stored + * directly into the object obj. + * + * @param obj + * Instance in which the fields will be set. + * @param classDesc + * A class descriptor (an ObjectStreamClass) + * defining which fields should be loaded. + * + * @throws IOException + * If an IO exception happened when reading the field values. + * @throws InvalidClassException + * If an incompatible type is being assigned to an emulated + * field. + * @throws OptionalDataException + * If optional data could not be found when reading the + * exception graph + * @throws ClassNotFoundException + * If a class of an object being de-serialized can not be found + * + * @see #readFields + * @see #readObject() + */ + private void readFieldValues(Object obj, ObjectStreamClass classDesc) + throws OptionalDataException, ClassNotFoundException, IOException { + // Now we must read all fields and assign them to the receiver + ObjectStreamField[] fields = classDesc.getLoadFields(); + Class declaringClass = classDesc.forClass(); + if (declaringClass == null && mustResolve) + throw new ClassNotFoundException(classDesc.getName()); + + for (int i = 0; i < fields.length; i++) { + ObjectStreamField fieldDesc = fields[i]; + // Now we read&set the instance field whose name is described by the + // fieldDesc we read before + + // Code duplication starts, just because Java is typed + if (fieldDesc.isPrimitive()) { + try { + switch (fieldDesc.getTypeCode()) { + case 'B': + setField(obj, declaringClass, fieldDesc.getName(), + input.readByte()); + break; + case 'C': + setField(obj, declaringClass, fieldDesc.getName(), + input.readChar()); + break; + case 'D': + setField(obj, declaringClass, fieldDesc.getName(), + input.readDouble()); + break; + case 'F': + setField(obj, declaringClass, fieldDesc.getName(), + input.readFloat()); + break; + case 'I': + setField(obj, declaringClass, fieldDesc.getName(), + input.readInt()); + break; + case 'J': + setField(obj, declaringClass, fieldDesc.getName(), + input.readLong()); + break; + case 'S': + setField(obj, declaringClass, fieldDesc.getName(), + input.readShort()); + break; + case 'Z': + setField(obj, declaringClass, fieldDesc.getName(), + input.readBoolean()); + break; + default: + throw new StreamCorruptedException(com.ibm.oti.util.Msg + .getString("K00d5", fieldDesc.getTypeCode())); //$NON-NLS-1$ + } + } catch (NoSuchFieldError err) { + } + } else { + // Object type (array included). + String fieldName = fieldDesc.getName(); + boolean setBack = false; + ObjectStreamField field = classDesc.getField(fieldName); + if (mustResolve && field == null) { + setBack = true; + mustResolve = false; + } + Object toSet; + if (field != null && field.getUnshared()) + toSet = readUnshared(); + else + toSet = readObject(); + if (setBack) + mustResolve = true; + if (field != null) { + if (toSet != null) { + Class fieldType = field.getType(); + Class valueType = toSet.getClass(); + if (!fieldType.isAssignableFrom(valueType)) + throw new ClassCastException(com.ibm.oti.util.Msg + .getString("K00d4", new String[] { //$NON-NLS-1$ + fieldType.toString(), + valueType.toString(), + classDesc.getName() + "." //$NON-NLS-1$ + + fieldName })); + try { + objSetField(obj, declaringClass, fieldName, field + .getTypeString(), toSet); + } catch (NoSuchFieldError e) { + } + } + } + } + } + } + + /** + * Reads and returns primitive data of type float read from the receiver + * + * @return A float saved as primitive data using + * ObjectOutputStream.writeFloat() + * + * @throws IOException + * If an IO exception happened when reading the primitive data. + */ + public float readFloat() throws IOException { + return primitiveTypes.readFloat(); + } + + /** + * Reads bytes from the receiver into the byte array buffer. + * This method will block until buffer.length number of bytes + * have been read. + * + * @param buffer + * the buffer to read bytes into + * + * @throws IOException + * if a problem occurs reading from this stream. + */ + public void readFully(byte[] buffer) throws IOException { + primitiveTypes.readFully(buffer); + } + + /** + * Reads bytes from the receiver into the byte array buffer. + * This method will block until length number of bytes have + * been read. + * + * @param buffer + * the byte array in which to store the read bytes. + * @param offset + * the offset in buffer to store the read bytes. + * @param length + * the maximum number of bytes to store in buffer. + * + * @throws IOException + * if a problem occurs reading from this stream. + */ + public void readFully(byte[] buffer, int offset, int length) + throws IOException { + primitiveTypes.readFully(buffer, offset, length); + } + + /** + * Walks the hierarchy of classes described by class descriptor + * classDesc and reads the field values corresponding to + * fields declared by the corresponding class descriptor. The instance to + * store field values into is object. If the class + * (corresponding to class descriptor classDesc) defines + * private instance method readObject it will be used to load + * field values. + * + * @param object + * Instance into which stored field values loaded. + * @param classDesc + * A class descriptor (an ObjectStreamClass) + * defining which fields should be loaded. + * + * @throws IOException + * If an IO exception happened when reading the field values in + * the hierarchy. + * @throws ClassNotFoundException + * If a class for one of the field types could not be found + * @throws NotActiveException + * If defaultReadObject is called from the wrong + * context. + * + * @see #defaultReadObject + * @see #readObject() + */ + private void readHierarchy(Object object, ObjectStreamClass classDesc) + throws IOException, ClassNotFoundException, NotActiveException { + // We can't be called from just anywhere. There are rules. + if (object == null && mustResolve) + throw new NotActiveException(); + + ArrayList streamClassList = new ArrayList(32); + ObjectStreamClass nextStreamClass = classDesc; + while (nextStreamClass != null) { + streamClassList.add(0, nextStreamClass); + nextStreamClass = nextStreamClass.getSuperclass(); + } + ArrayList classList = new ArrayList(32); + Class nextClass = object.getClass(); + while (nextClass != null) { + Class testClass = nextClass.getSuperclass(); + if (testClass != null) { + classList.add(0, nextClass); + } + nextClass = testClass; + } + int lastIndex = 0; + for (int i = 0; i < classList.size(); i++) { + Class superclass = (Class) classList.get(i); + int index = findStreamSuperclass(superclass, streamClassList, + lastIndex); + if (index == -1) { + readObjectNoData(object, superclass); + } else { + for (int j = lastIndex; j <= index; j++) { + readObjectForClass(object, + (ObjectStreamClass) streamClassList.get(j)); + } + } + lastIndex = index + 1; + } + } + + private int findStreamSuperclass(Class cl, ArrayList classList, + int lastIndex) { + for (int i = lastIndex; i < classList.size(); i++) { + if (cl.getName().equals( + ((ObjectStreamClass) classList.get(i)).getName())) { + return i; + } + } + return -1; + } + + private void readObjectNoData(Object object, Class cl) + throws ObjectStreamException { + if (!ObjectStreamClass.isSerializable(cl)) { + return; + } + + final Method readMethod = ObjectStreamClass + .getPrivateReadObjectNoDataMethod(cl); + if (readMethod != null) { + AccessController.doPrivileged(new PriviAction(readMethod)); + try { + readMethod.invoke(object, new Object[0]); + } catch (InvocationTargetException e) { + Throwable ex = e.getTargetException(); + if (ex instanceof RuntimeException) + throw (RuntimeException) ex; + else if (ex instanceof Error) + throw (Error) ex; + throw (ObjectStreamException) ex; + } catch (IllegalAccessException e) { + throw new RuntimeException(e.toString()); + } + } + } + + private void readObjectForClass(Object object, ObjectStreamClass classDesc) + throws IOException, ClassNotFoundException, NotActiveException { + // Have to do this before calling defaultreadObject or anything that + // calls defaultReadObject + currentObject = object; + currentClass = classDesc; + + boolean hadWriteMethod = (classDesc.getFlags() & SC_WRITE_METHOD) > 0; + Class targetClass = classDesc.forClass(); + final Method readMethod; + if (targetClass == null || !mustResolve) + readMethod = null; + else + readMethod = ObjectStreamClass + .getPrivateReadObjectMethod(targetClass); + try { + if (readMethod != null) { + // We have to be able to fetch its value, even if it is private + AccessController.doPrivileged(new PriviAction(readMethod)); + try { + readMethod.invoke(object, new Object[] { this }); + } catch (InvocationTargetException e) { + Throwable ex = e.getTargetException(); + if (ex instanceof ClassNotFoundException) + throw (ClassNotFoundException) ex; + else if (ex instanceof RuntimeException) + throw (RuntimeException) ex; + else if (ex instanceof Error) + throw (Error) ex; + throw (IOException) ex; + } catch (IllegalAccessException e) { + throw new RuntimeException(e.toString()); + } + } else { + defaultReadObject(); + } + if (hadWriteMethod) { + discardData(); + } + } finally { + // Cleanup, needs to run always so that we can later detect invalid + // calls to defaultReadObject + currentObject = null; // We did not set this, so we do not need to + // clean it + currentClass = null; + } + } + + /** + * Reads and returns primitive data of type int read from the receiver + * + * @return an int saved as primitive data using + * ObjectOutputStream.writeInt() + * + * @throws IOException + * If an IO exception happened when reading the primitive data. + */ + public int readInt() throws IOException { + return primitiveTypes.readInt(); + } + + /** + * Reads and returns the next line (primitive data of type String) read from + * the receiver + * + * @return a String saved as primitive data using + * ObjectOutputStream.writeLine() + * + * @throws IOException + * If an IO exception happened when reading the primitive data. + * + * @deprecated Use BufferedReader + */ + public String readLine() throws IOException { + return primitiveTypes.readLine(); + } + + /** + * Reads and returns primitive data of type long read from the receiver + * + * @return a long saved as primitive data using + * ObjectOutputStream.writeLong() + * + * @throws IOException + * If an IO exception happened when reading the primitive data. + */ + public long readLong() throws IOException { + return primitiveTypes.readLong(); + } + + /** + * Read a new array from the receiver. It is assumed the array has not been + * read yet (not a cyclic rfeference). Return the array read. + * + * @param unshared + * read the object unshared + * @return the array read + * + * @throws IOException + * If an IO exception happened when reading the array. + * @throws ClassNotFoundException + * If a class for one of the objects could not be found + * @throws OptionalDataException + * If optional data could not be found when reading the array. + */ + private Object readNewArray(boolean unshared) throws OptionalDataException, + ClassNotFoundException, IOException { + ObjectStreamClass classDesc = readClassDesc(); + + if (classDesc == null) + throw new InvalidClassException(com.ibm.oti.util.Msg + .getString("K00d1")); //$NON-NLS-1$ + + Integer newHandle = new Integer(nextHandle()); + + // Array size + int size = input.readInt(); + Class arrayClass = classDesc.forClass(); + Class componentType = arrayClass.getComponentType(); + Object result = java.lang.reflect.Array + .newInstance(componentType, size); + if (!unshared) { + registerObjectRead(result, newHandle); + } + + // Now we have code duplication just because Java is typed. We have to + // read N elements + // and assign to array positions, but we must typecast the array first, + // and also + // call different methods depending on the elements. + + if (componentType.isPrimitive()) { + if (componentType == Integer.TYPE) { + int[] intArray = (int[]) result; + for (int i = 0; i < size; i++) + intArray[i] = input.readInt(); + } else if (componentType == Byte.TYPE) { + byte[] byteArray = (byte[]) result; + input.readFully(byteArray, 0, size); + } else if (componentType == Character.TYPE) { + char[] charArray = (char[]) result; + for (int i = 0; i < size; i++) + charArray[i] = input.readChar(); + } else if (componentType == Short.TYPE) { + short[] shortArray = (short[]) result; + for (int i = 0; i < size; i++) + shortArray[i] = input.readShort(); + } else if (componentType == Boolean.TYPE) { + boolean[] booleanArray = (boolean[]) result; + for (int i = 0; i < size; i++) + booleanArray[i] = input.readBoolean(); + } else if (componentType == Long.TYPE) { + long[] longArray = (long[]) result; + for (int i = 0; i < size; i++) + longArray[i] = input.readLong(); + } else if (componentType == Float.TYPE) { + float[] floatArray = (float[]) result; + for (int i = 0; i < size; i++) + floatArray[i] = input.readFloat(); + } else if (componentType == Double.TYPE) { + double[] doubleArray = (double[]) result; + for (int i = 0; i < size; i++) + doubleArray[i] = input.readDouble(); + } else + throw new ClassNotFoundException(com.ibm.oti.util.Msg + .getString("K00d7", classDesc.getName())); //$NON-NLS-1$ + } else { + // Array of Objects + Object[] objectArray = (Object[]) result; + for (int i = 0; i < size; i++) + objectArray[i] = readObject(); + } + if (enableResolve) { + result = resolveObject(result); + registerObjectRead(result, newHandle); + } + return result; + } + + /** + * Reads a new class from the receiver. It is assumed the class has not been + * read yet (not a cyclic rfeference). Return the class read. + * + * @param unshared + * read the object unshared + * @return The java.lang.Class read from the stream. + * + * @throws IOException + * If an IO exception happened when reading the class. + * @throws ClassNotFoundException + * If a class for one of the objects could not be found + */ + private Class readNewClass(boolean unshared) throws ClassNotFoundException, + IOException { + ObjectStreamClass classDesc = readClassDesc(); + + if (classDesc != null) { + Integer newHandle = new Integer(nextHandle()); + Class localClass = classDesc.forClass(); + if (localClass != null && !unshared) + registerObjectRead(localClass, newHandle); + return localClass; + } + throw new InvalidClassException(com.ibm.oti.util.Msg.getString("K00d1")); //$NON-NLS-1$ + } + + /** + * Reads a new class descriptor from the receiver. It is assumed the class + * descriptor has not been read yet (not a cyclic rfeference). Return the + * class descriptor read. + * + * @param unshared + * read the object unshared + * @return The ObjectStreamClass read from the stream. + * + * @throws IOException + * If an IO exception happened when reading the class + * descriptor. + * @throws ClassNotFoundException + * If a class for one of the objects could not be found + */ + private ObjectStreamClass readNewClassDesc(boolean unshared) + throws ClassNotFoundException, IOException { + // So read...() methods can be used by + // subclasses during readClassDescriptor() + primitiveData = input; + Integer oldHandle = descriptorHandle; + descriptorHandle = new Integer(nextHandle()); + ObjectStreamClass newClassDesc = readClassDescriptor(); + if (descriptorHandle != null && !unshared) { + registerObjectRead(newClassDesc, descriptorHandle); + } + descriptorHandle = oldHandle; + primitiveData = emptyStream; + + // We need to map classDesc to class. + try { + newClassDesc.setClass(resolveClass(newClassDesc)); + // Check SUIDs + verifySUID(newClassDesc); + } catch (ClassNotFoundException e) { + if (mustResolve) + throw e; + // Just continue, the class may not be required + } + + // Resolve the field signatures using the class loader of the + // resolved class + ObjectStreamField[] fields = newClassDesc.getLoadFields(); + ClassLoader loader = newClassDesc.forClass() == null ? callerClassLoader + : newClassDesc.forClass().getClassLoader(); + for (int i = 0; i < fields.length; i++) + fields[i].resolve(loader); + + // Consume unread class annotation data and TC_ENDBLOCKDATA + discardData(); + newClassDesc.setSuperclass(readClassDesc()); + + return newClassDesc; + } + + /** + * Reads a new proxy class descriptor from the receiver. It is assumed the + * proxy class descriptor has not been read yet (not a cyclic rfeference). + * Return the proxy class descriptor read. + * + * @return The Class read from the stream. + * + * @throws IOException + * If an IO exception happened when reading the class + * descriptor. + * @throws ClassNotFoundException + * If a class for one of the objects could not be found + */ + private Class readNewProxyClassDesc() throws ClassNotFoundException, + IOException { + int count = input.readInt(); + String[] interfaceNames = new String[count]; + for (int i = 0; i < count; i++) + interfaceNames[i] = input.readUTF(); + Class proxy = resolveProxyClass(interfaceNames); + // Consume unread class annotation data and TC_ENDBLOCKDATA + discardData(); + return proxy; + } + + /** + * Reads a new class descriptor from the receiver. Return the class + * descriptor read. + * + * @return The ObjectStreamClass read from the stream. + * + * @throws IOException + * If an IO exception happened when reading the class + * descriptor. + * @throws ClassNotFoundException + * If a class for one of the objects could not be found + */ + protected ObjectStreamClass readClassDescriptor() throws IOException, + ClassNotFoundException { + if (descriptorHandle == null) + throw new NotActiveException(); + + ObjectStreamClass newClassDesc = new ObjectStreamClass(); + newClassDesc.setName(input.readUTF()); + newClassDesc.setSerialVersionUID(input.readLong()); + newClassDesc.setFlags(input.readByte()); + + // We must register the class descriptor before reading field + // descriptors. + registerObjectRead(newClassDesc, descriptorHandle); + descriptorHandle = null; + + readFieldDescriptors(newClassDesc); + return newClassDesc; + } + + /** + * Retrieves the proxy class corresponding to the interface names. + * + * @param interfaceNames + * The interfaces used to create the proxy class + * @return A proxy class + * + * @throws IOException + * If any IO problem occurred when trying to load the class. + * @throws ClassNotFoundException + * If the proxy class cannot be created + */ + protected Class resolveProxyClass(String[] interfaceNames) + throws IOException, ClassNotFoundException { + ClassLoader loader = com.ibm.oti.vm.VM.getNonBootstrapClassLoader(); + Class[] interfaces = new Class[interfaceNames.length]; + for (int i = 0; i < interfaceNames.length; i++) { + interfaces[i] = Class.forName(interfaceNames[i], false, loader); + } + try { + return Proxy.getProxyClass(loader, interfaces); + } catch (IllegalArgumentException e) { + throw new ClassNotFoundException(e.toString(), e); + } + } + + /** + * Write a new handfle describing a cyclic reference from the stream. + * + * @return the handle read + * + * @throws IOException + * If an IO exception happened when reading the handle + */ + private Integer readNewHandle() throws IOException { + return new Integer(input.readInt()); + } + + /** + * Read a new object from the stream. It is assumed the object has not been + * loade yet (not a cyclic reference). Return the object read. + * + * If the object implements Externalizable its + * readExternal is called. Otherwise, all fields described by + * the class hierarchy are loaded. Each class can define how its declared + * instance fields are laoded by defining a private method + * readObject + * + * @param unshared + * read the object unshared + * @return the object read + * + * @throws IOException + * If an IO exception happened when reading the object. + * @throws OptionalDataException + * If optional data could not be found when reading the object + * graph + * @throws ClassNotFoundException + * If a class for one of the objects could not be found + */ + private Object readNewObject(boolean unshared) + throws OptionalDataException, ClassNotFoundException, IOException { + ObjectStreamClass classDesc = readClassDesc(); + + if (classDesc == null) + throw new InvalidClassException(com.ibm.oti.util.Msg + .getString("K00d1")); //$NON-NLS-1$ + + Integer newHandle = new Integer(nextHandle()); + + // Note that these values come from the Stream, and in fact it could be + // that the + // classes have been changed so that the info below now conflicts with + // the newer class + boolean wasExternalizable = (classDesc.getFlags() & SC_EXTERNALIZABLE) > 0; + boolean wasSerializable = (classDesc.getFlags() & SC_SERIALIZABLE) > 0; + + // Maybe we should cache the values above in classDesc ? It may be the + // case that when reading classDesc + // we may need to read more stuff depending on the values above + + Class objectClass = classDesc.forClass(); + + Object result, registeredResult = null; + if (objectClass != null) { + // The class of the instance may not be the same as the class of the + // constructor to run + Class constructorClass = objectClass; // This is the constructor + // to run if Externalizable + + // WARNING - What if the object is serializable and externalizable ? + // Is that possible ? + if (wasSerializable) { + // Now we must run the constructor of the class just above the + // one that implements Serializable + // so that slots that were not dumped can be initialized + // properly + while (constructorClass != null + & ObjectStreamClass.isSerializable(constructorClass)) { + constructorClass = constructorClass.getSuperclass(); + } + } + + // Fetch the empty constructor + Constructor constructor; + try { + constructor = constructorClass + .getDeclaredConstructor(ObjectStreamClass.EMPTY_CONSTRUCTOR_PARAM_TYPES); + } catch (NoSuchMethodException nsmEx) { + constructor = null; + } + + // Has to have an empty constructor + if (constructor == null) + throw new InvalidClassException(constructorClass.getName(), + com.ibm.oti.util.Msg.getString("K00dc")); //$NON-NLS-1$ + + int constructorModifiers = constructor.getModifiers(); + + // Now we must check if the empty constructor is visible to the + // instantiation class + if (Modifier.isPrivate(constructorModifiers) + || (wasExternalizable && !Modifier + .isPublic(constructorModifiers))) + throw new InvalidClassException(constructorClass.getName(), + com.ibm.oti.util.Msg.getString("K00dc")); //$NON-NLS-1$ + + // We know we are testing from a subclass, so the only other case + // where the visibility + // is not allowed is when the constructor has default visibility and + // the instantiation class + // is in a different package than the constructor class + if (!Modifier.isPublic(constructorModifiers) + && !Modifier.isProtected(constructorModifiers)) { + // Not public, not private and not protected...means default + // visibility. Check if same package + if (!inSamePackage(constructorClass, objectClass)) + throw new InvalidClassException(constructorClass.getName(), + com.ibm.oti.util.Msg.getString("K00dc")); //$NON-NLS-1$ + } + + // Now we know which class to instantiate and which constructor to + // run. We are allowed to run the constructor. + result = newInstance(objectClass, constructorClass); + if (!unshared) { + registerObjectRead(result, newHandle); + } + registeredResult = result; + } else { + result = null; + } + + try { + // This is how we know what to do in defaultReadObject. And it is + // also + // used by defaultReadObject to check if it was called from an + // invalid place. + // It also allows readExternal to call defaultReadObject and have it + // work. + currentObject = result; + currentClass = classDesc; + + // If Externalizable, just let the object read itself + if (wasExternalizable) { + boolean blockData = (classDesc.getFlags() & SC_BLOCK_DATA) > 0; + if (!blockData) + primitiveData = input; + if (mustResolve) { + Externalizable extern = (Externalizable) result; + extern.readExternal(this); + } + if (blockData) { + // Similar to readHierarchy. Anything not read by + // readExternal has to be consumed here + discardData(); + } else { + primitiveData = emptyStream; + } + } else { + // If we got here, it is Serializable but not Externalizable. + // Walk the hierarchy reading each class' slots + readHierarchy(result, classDesc); + } + } finally { + // Cleanup, needs to run always so that we can later detect invalid + // calls to defaultReadObject + currentObject = null; + currentClass = null; + } + + if (objectClass != null) { + Object readResolveMethod = readResolveCache.get(objectClass); + if (readResolveMethod != this) { + if (readResolveMethod == null) { + final Method readResolve = ObjectStreamClass + .methodReadResolve(objectClass); + if (readResolve == null) { + readResolveCache.put(objectClass, this); + readResolveMethod = null; + } else { + // Has replacement method + AccessController.doPrivileged(new PriviAction( + readResolve)); + readResolveCache.put(objectClass, readResolve); + readResolveMethod = readResolve; + } + } + if (readResolveMethod != null) { + try { + result = ((Method) readResolveMethod).invoke(result, + null); + } catch (IllegalAccessException iae) { + } catch (InvocationTargetException ite) { + Throwable target = ite.getTargetException(); + if (target instanceof ObjectStreamException) + throw (ObjectStreamException) target; + else if (target instanceof Error) + throw (Error) target; + else + throw (RuntimeException) target; + } + } + } + } + // We get here either if class-based replacement was not needed or if it + // was needed + // but produced the same object or if it could not be computed. + + // The object to return is the one we instantiated or a replacement for + // it + if (result != null && enableResolve) + result = resolveObject(result); + if (registeredResult != result && !unshared) { + registerObjectRead(result, newHandle); + } + return result; + } + + /** + * Read a new String in UTF format from the receiver. Return the string + * read. + * + * @param unshared + * read the object unshared + * @return the string just read. + * + * @throws IOException + * If an IO exception happened when reading the String. + */ + private Object readNewString(boolean unshared) throws IOException { + Object result = input.readUTF(); + if (enableResolve) + result = resolveObject(result); + int newHandle = nextHandle(); + if (!unshared) { + registerObjectRead(result, new Integer(newHandle)); + } + return result; + } + + /** + * Read a new String in UTF format from the receiver. Return the string + * read. + * + * @param unshared + * read the object unshared + * @return the string just read. + * + * @throws IOException + * If an IO exception happened when reading the String. + */ + private Object readNewLongString(boolean unshared) throws IOException { + long length = input.readLong(); + Object result = input.decodeUTF((int) length); + if (enableResolve) + result = resolveObject(result); + int newHandle = nextHandle(); + if (!unshared) { + registerObjectRead(result, new Integer(newHandle)); + } + return result; + } + + /** + * Read the next object from the receiver's underlying stream. + * + * @return the new object read. + * + * @throws IOException + * If an IO exception happened when reading the object + * @throws ClassNotFoundException + * If the class of one of the objects in the object graph could + * not be found + * @throws OptionalDataException + * If primitive data types were found instead of an object. + * + * @see ObjectOutputStream#writeObject(Object) + */ + public final Object readObject() throws OptionalDataException, + ClassNotFoundException, IOException { + return readObject(false); + } + + /** + * Read the next unshared object from the receiver's underlying stream. + * + * @return the new object read. + * + * @throws IOException + * If an IO exception happened when reading the object + * @throws ClassNotFoundException + * If the class of one of the objects in the object graph could + * not be found + * + * @see ObjectOutputStream#writeUnshared + */ + public Object readUnshared() throws IOException, ClassNotFoundException { + return readObject(true); + } + + private Object readObject(boolean unshared) throws OptionalDataException, + ClassNotFoundException, IOException { + boolean restoreInput = (primitiveData == input); + if (restoreInput) { + primitiveData = emptyStream; + } + + // This is the spec'ed behavior in JDK 1.2. Very bizarre way to allow + // behavior overriding. + if (subclassOverridingImplementation && !unshared) + return readObjectOverride(); + + // If we still had primitive types to read, should we discard them + // (reset the primitiveTypes stream) + // or leave as is, so that attempts to read primitive types won't read + // 'past data' ??? + Object result; + try { + // We need this so we can tell when we are returning to the + // original/outside caller + if (++nestedLevels == 1) { + // Remember the caller's class loader + callerClassLoader = com.ibm.oti.vm.VM + .getNonBootstrapClassLoader(); + } + + result = readNonPrimitiveContent(unshared); + if (restoreInput) { + primitiveData = input; + } + } finally { + // We need this so we can tell when we are returning to the + // original/outside caller + if (--nestedLevels == 0) { + // We are going to return to the original caller, perform + // cleanups. + callerClassLoader = null; // No more need to remember the + // caller's class loader + } + } + + // Done reading this object. Is it time to return to the original caller + // ? If so + // we need to perform validations first. + + if (nestedLevels == 0 && validations != null) { + // We are going to return to the original caller. If validation is + // enabled we + // need to run them now and then cleanup the validation collection + try { + for (int i = 0; i < validations.length; i++) + validations[i].validator.validateObject(); + } finally { + validations = null; + } // validations have to be renewed, since they are only called + // from readObject + } + return result; + } + + /** + * Method to be overriden by subclasses to read the next object from the + * receiver's underlying stream. + * + * @return the new object read. + * + * @throws IOException + * If an IO exception happened when reading the object + * @throws ClassNotFoundException + * If the class of one of the objects in the object graph could + * not be found + * @throws OptionalDataException + * If primitive data types were found instead of an object. + * + * @see ObjectOutputStream#writeObjectOverride + */ + protected Object readObjectOverride() throws OptionalDataException, + ClassNotFoundException, IOException { + // Subclasses must override. + throw new IOException(); + } + + /** + * Reads and returns primitive data of type short from the receiver + * + * @return a short saved as primitive data using + * ObjectOutputStream.writeShort() + * + * @throws IOException + * If an IO exception happened when reading the primitive data. + */ + public short readShort() throws IOException { + return primitiveTypes.readShort(); + } + + /** + * Reads and validates the ObjectInputStream header from the receiver + * + * @throws IOException + * If an IO exception happened when reading the stream header. + * @throws StreamCorruptedException + * If the underlying stream does not contain serialized objects + * that can be read. + */ + protected void readStreamHeader() throws IOException, + StreamCorruptedException { + if (input.readShort() == STREAM_MAGIC + && input.readShort() == STREAM_VERSION) + return; + throw new StreamCorruptedException(); + } + + /** + * Reads and returns primitive data of type byte (unsigned) from the + * receiver + * + * @return a byte saved as primitive data using + * ObjectOutputStream.writeUnsignedByte() + * + * @throws IOException + * If an IO exception happened when reading the primitive data. + */ + public int readUnsignedByte() throws IOException { + return primitiveTypes.readUnsignedByte(); + } + + /** + * Reads and returns primitive data of type short (unsigned) from the + * receiver + * + * @return a short saved as primitive data using + * ObjectOutputStream.writeUnsignedShort() + * + * @throws IOException + * If an IO exception happened when reading the primitive data. + */ + public int readUnsignedShort() throws IOException { + return primitiveTypes.readUnsignedShort(); + } + + /** + * Reads and returns primitive data of type String read in UTF format from + * the receiver + * + * @return a String saved as primitive data using + * ObjectOutputStream.writeUTF() + * + * @throws IOException + * If an IO exception happened when reading the primitive data. + */ + public String readUTF() throws IOException { + return primitiveTypes.readUTF(); + } + + /** + * Return the object previously read tagged with handle handle. + * + * @param handle + * The handle that this object was assigned when it was read. + * @return the object previusly read. + * + * @throws InvalidObjectException + * If there is no previously read object with this handle + */ + private Object registeredObjectRead(Integer handle) + throws InvalidObjectException { + Object objectRead = objectsRead.get(handle); + if (objectRead == null) { + throw new InvalidObjectException( + "Back reference to unshared object"); //$NON-NLS-1$ + } + return objectRead; + } + + /** + * Assume object obj has been read, and assign a handle to + * it, handle. + * + * @param obj + * Non-null object being loaded. + * @param handle + * An Integer, the handle to this object + * + * @see #nextHandle + */ + private void registerObjectRead(Object obj, Integer handle) { + objectsRead.put(handle, obj); + } + + /** + * Register object validator object to be executed to perform + * validation of objects loaded from the receiver. Validations will be run + * in order of decreasing priority, defined by priority. + * + * @param object + * An ObjectInputValidation to validate objects loaded. + * @param priority + * validator priority + * + * @throws NotActiveException + * If this method is not called from readObject() + * @throws InvalidObjectException + * If object is null. + */ + public synchronized void registerValidation(ObjectInputValidation object, + int priority) throws NotActiveException, InvalidObjectException { + if (object != null) { + // Validation can only be registered when inside readObject calls + Object instanceBeingRead = this.currentObject; + // We can't be called from just anywhere. There are rules. + if (instanceBeingRead != null) { + // From now on it is just insertion in a SortedCollection. Since + // the Java class + // libraries don't provide that, we have to implement it from + // scratch here. + + InputValidationDesc desc = new InputValidationDesc(); + desc.validator = object; + desc.priority = priority; + // No need for this, validateObject does not take a parameter + // desc.toValidate = instanceBeingRead; + if (validations == null) { + validations = new InputValidationDesc[1]; + validations[0] = desc; + } else { + int i = 0; + for (; i < validations.length; i++) { + InputValidationDesc validation = validations[i]; + // Sorted, higher priority first. + if (priority >= validation.priority) + break; // Found the index where to insert + } + InputValidationDesc[] oldValidations = validations; + int currentSize = oldValidations.length; + validations = new InputValidationDesc[currentSize + 1]; + System.arraycopy(oldValidations, 0, validations, 0, i); + System.arraycopy(oldValidations, i, validations, i + 1, + currentSize - i); + validations[i] = desc; + } + } else + throw new NotActiveException(); + } else + throw new InvalidObjectException(com.ibm.oti.util.Msg + .getString("K00d9")); //$NON-NLS-1$ + } + + /** + * Reset the collection of objects already loaded by the receiver. + * + * + */ + private void resetSeenObjects() { + objectsRead = new Hashtable(); + currentHandle = baseWireHandle; + primitiveData = emptyStream; + } + + /** + * Reset the receiver. The collection of objects already read by the + * receiver is reset, and internal structures are also reset so that the + * receiver knows it is in a fresh clean state. + * + */ + private void resetState() { + resetSeenObjects(); + hasPushbackTC = false; + pushbackTC = 0; + // nestedLevels = 0; + } + + /** + * Loads the Java class corresponding to the class descriptor + * osClass(ObjectStreamClass) just read from the receiver. + * + * @param osClass + * An ObjectStreamClass read from the receiver. + * @return a Class corresponding to the descriptor loaded. + * + * @throws IOException + * If any IO problem occurred when trying to load the class. + * @throws ClassNotFoundException + * If the corresponding class cannot be found. + */ + protected Class resolveClass(ObjectStreamClass osClass) throws IOException, + ClassNotFoundException { + // Use the first non-null ClassLoader on the stack. If null, use the + // system class loader + return Class.forName(osClass.getName(), true, callerClassLoader); + } + + /** + * If enableResolveObject() was activated, computes the + * replacement object for the original object object and + * returns the replacement. Otherwise returns object. + * + * + * @param object + * Original object for which a replacement may be defined + * @return a possibly new, replacement object for object + * + * @throws IOException + * If any IO problem occurred when trying to resolve the object. + * + * @see #enableResolveObject + * @see ObjectOutputStream#enableReplaceObject + * @see ObjectOutputStream#replaceObject + */ + protected Object resolveObject(Object object) throws IOException { + // By default no object replacement. Subclasses can override + return object; + } + + /** + * Set a given declared field named fieldName of + * instance to the new byte value + * value. + * + * This method could be implemented non-natively on top of java.lang.reflect + * implementations that support the setAccessible API, at the + * expense of extra object creation (java.lang.reflect.Field). Otherwise + * Serialization could not set private fields, except by the use of a native + * method like this one. + * + * + * @param instance + * Object whose field to set + * @param declaringClass + * instance's declaring class + * @param fieldName + * Name of the field to set + * @param value + * New value for the field + * + * @throws NoSuchFieldError + * If the field does not exist. + */ + private static native void setField(Object instance, Class declaringClass, + String fieldName, byte value) throws NoSuchFieldError; + + /** + * Set a given declared field named fieldName of + * instance to the new char value + * value. + * + * This method could be implemented non-natively on top of java.lang.reflect + * implementations that support the setAccessible API, at the + * expense of extra object creation (java.lang.reflect.Field). Otherwise + * Serialization could not set private fields, except by the use of a native + * method like this one. + * + * @param instance + * Object whose field to set + * @param declaringClass + * instance's declaring class + * @param fieldName + * Name of the field to set + * @param value + * New value for the field + * + * @throws NoSuchFieldError + * If the field does not exist. + */ + private static native void setField(Object instance, Class declaringClass, + String fieldName, char value) throws NoSuchFieldError; + + /** + * Set a given declared field named fieldName of + * instance to the new double value + * value. + * + * This method could be implemented non-natively on top of java.lang.reflect + * implementations that support the setAccessible API, at the + * expense of extra object creation (java.lang.reflect.Field). Otherwise + * Serialization could not set private fields, except by the use of a native + * method like this one. + * + * @param instance + * Object whose field to set + * @param declaringClass + * instance's declaring class + * @param fieldName + * Name of the field to set + * @param value + * New value for the field + * + * @throws NoSuchFieldError + * If the field does not exist. + */ + private static native void setField(Object instance, Class declaringClass, + String fieldName, double value) throws NoSuchFieldError; + + /** + * Set a given declared field named fieldName of + * instance to the new float value + * value. + * + * This method could be implemented non-natively on top of java.lang.reflect + * implementations that support the setAccessible API, at the + * expense of extra object creation (java.lang.reflect.Field). Otherwise + * Serialization could not set private fields, except by the use of a native + * method like this one. + * + * @param instance + * Object whose field to set + * @param declaringClass + * instance's declaring class + * @param fieldName + * Name of the field to set + * @param value + * New value for the field + * + * @throws NoSuchFieldError + * If the field does not exist. + */ + private static native void setField(Object instance, Class declaringClass, + String fieldName, float value) throws NoSuchFieldError; + + /** + * Set a given declared field named fieldName of + * instance to the new int value + * value. + * + * This method could be implemented non-natively on top of java.lang.reflect + * implementations that support the setAccessible API, at the + * expense of extra object creation (java.lang.reflect.Field). Otherwise + * Serialization could not set private fields, except by the use of a native + * method like this one. + * + * @param instance + * Object whose field to set + * @param declaringClass + * instance's declaring class + * @param fieldName + * Name of the field to set + * @param value + * New value for the field + * + * @throws NoSuchFieldError + * If the field does not exist. + */ + private static native void setField(Object instance, Class declaringClass, + String fieldName, int value) throws NoSuchFieldError; + + /** + * Set a given declared field named fieldName of + * instance to the new long value + * value. + * + * This method could be implemented non-natively on top of java.lang.reflect + * implementations that support the setAccessible API, at the + * expense of extra object creation (java.lang.reflect.Field). Otherwise + * Serialization could not set private fields, except by the use of a native + * method like this one. + * + * @param instance + * Object whose field to set + * @param declaringClass + * instance's declaring class + * @param fieldName + * Name of the field to set + * @param value + * New value for the field + * + * @throws NoSuchFieldError + * If the field does not exist. + */ + private static native void setField(Object instance, Class declaringClass, + String fieldName, long value) throws NoSuchFieldError; + + /** + * Set a given declared field named fieldName of + * instance to the new value value. + * + * This method could be implemented non-natively on top of java.lang.reflect + * implementations that support the setAccessible API, at the + * expense of extra object creation (java.lang.reflect.Field). Otherwise + * Serialization could not set private fields, except by the use of a native + * method like this one. + * + * @param instance + * Object whose field to set + * @param declaringClass + * Class which delares the field + * @param fieldName + * Name of the field to set + * @param fieldTypeName + * Name of the class defining the type of the field + * @param value + * New value for the field + * + * @throws NoSuchFieldError + * If the field does not exist. + */ + private static native void objSetField(Object instance, + Class declaringClass, String fieldName, String fieldTypeName, + Object value) throws NoSuchFieldError; + + /** + * Set a given declared field named fieldName of + * instance to the new short value + * value. + * + * This method could be implemented non-natively on top of java.lang.reflect + * implementations that support the setAccessible API, at the + * expense of extra object creation (java.lang.reflect.Field). Otherwise + * Serialization could not set private fields, except by the use of a native + * method like this one. + * + * @param instance + * Object whose field to set + * @param declaringClass + * instance's declaring class + * @param fieldName + * Name of the field to set + * @param value + * New value for the field + * + * @throws NoSuchFieldError + * If the field does not exist. + */ + private static native void setField(Object instance, Class declaringClass, + String fieldName, short value) throws NoSuchFieldError; + + /** + * Set a given declared field named fieldName of + * instance to the new boolean value + * value. + * + * This method could be implemented non-natively on top of java.lang.reflect + * implementations that support the setAccessible API, at the + * expense of extra object creation (java.lang.reflect.Field). Otherwise + * Serialization could not set private fields, except by the use of a native + * method like this one. + * + * @param instance + * Object whose field to set + * @param declaringClass + * instance's declaring class + * @param fieldName + * Name of the field to set + * @param value + * New value for the field + * + * @throws NoSuchFieldError + * If the field does not exist. + */ + private static native void setField(Object instance, Class declaringClass, + String fieldName, boolean value) throws NoSuchFieldError; + + /** + * Skips length bytes of primitive data from the receiver. It + * should not be used to skip bytes at any arbitrary position; just when + * reading primitive data types (ints, chars, etc). + * + * + * @param length + * How many bytes to skip + * @return number of bytes skipped + * + * @throws IOException + * If any IO problem occurred when trying to skip the bytes. + */ + public int skipBytes(int length) throws IOException { + // To be used with available. Ok to call if reading primitive buffer + int offset = 0; + while (offset < length) { + checkReadPrimitiveTypes(); + long skipped = primitiveData.skip(length - offset); + if (skipped == 0) + return offset; + offset += (int) skipped; + } + return length; + } + + /** + * Verify if the SUID for descriptor loadedStreamClassmatches + * the SUID of the corresponding loaded class. + * + * + * @param loadedStreamClass + * An ObjectStreamClass that was loaded from the stream. + * + * @throws InvalidClassException + * If teh SUID of the stream class does not match the VM class + * + */ + private void verifySUID(ObjectStreamClass loadedStreamClass) + throws InvalidClassException { + Class localClass = loadedStreamClass.forClass(); + // Instances of java.lang.Class + // are always Serializable, even if their instances aren't (e.g. + // java.lang.Object.class). + // We cannot call lookup because it returns null if the parameter + // represents instances + // that cannot be serialized, and that is not what we want. If we are + // loading an instance + // of java.lang.Class, we better have the corresponding + // ObjectStreamClass. + + ObjectStreamClass localStreamClass = ObjectStreamClass + .lookupStreamClass(localClass); + if (loadedStreamClass.getSerialVersionUID() != localStreamClass + .getSerialVersionUID()) + throw new InvalidClassException(loadedStreamClass.getName(), + com.ibm.oti.util.Msg.getString("K00da", loadedStreamClass, //$NON-NLS-1$ + localStreamClass)); + } +}