/* * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code 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 * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ /* * Licensed Materials - Property of IBM * RMI-IIOP v1.0 * Copyright IBM Corp. 1998 1999 All Rights Reserved * */ package org.jboss.com.sun.corba.se.impl.io; import java.io.EOFException; import java.io.Externalizable; import java.io.IOException; import java.io.InvalidClassException; import java.io.InvalidObjectException; import java.io.NotActiveException; import java.io.ObjectInputValidation; import java.io.OptionalDataException; import java.io.StreamCorruptedException; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.security.AccessController; import java.security.PrivilegedAction; import java.security.PrivilegedExceptionAction; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.Vector; import javax.rmi.CORBA.Util; import javax.rmi.CORBA.ValueHandler; import org.jboss.com.sun.corba.se.impl.logging.OMGSystemException; import org.jboss.com.sun.corba.se.impl.logging.UtilSystemException; import org.jboss.com.sun.corba.se.impl.util.Utility; import org.jboss.com.sun.corba.se.spi.logging.CORBALogDomains; import org.jboss.com.sun.org.omg.CORBA.ValueDefPackage.FullValueDescription; import org.jboss.com.sun.org.omg.SendingContext.CodeBase; import org.jboss.sun.corba.Bridge; import org.omg.CORBA.CompletionStatus; import org.omg.CORBA.MARSHAL; import org.omg.CORBA.ORB; import org.omg.CORBA.SystemException; import org.omg.CORBA.TCKind; import org.omg.CORBA.TypeCode; import org.omg.CORBA.ValueMember; import org.omg.CORBA.portable.IndirectionException; import org.omg.CORBA.portable.ValueInputStream; /** * IIOPInputStream is used by the ValueHandlerImpl to handle Java serialization input semantics. * * @author Stephen Lewallen * @since JDK1.1.6 */ public class IIOPInputStream extends org.jboss.com.sun.corba.se.impl.io.InputStreamHook { private static Bridge bridge = AccessController.doPrivileged(new PrivilegedAction<Bridge>() { public Bridge run() { return Bridge.get(); } }); private static OMGSystemException omgWrapper = OMGSystemException.get(CORBALogDomains.RPC_ENCODING); private static UtilSystemException utilWrapper = UtilSystemException.get(CORBALogDomains.RPC_ENCODING); // Necessary to pass the appropriate fields into the defaultReadObjectDelegate method (which takes no parameters // since it's called from java.io.ObjectInpuStream defaultReadObject() which we can't change). // // This is only used in the case where the fields had to be obtained remotely because of a serializable version // difference. Set in inputObjectUsingFVD. Part of serialization evolution fixes for Ladybird, bug 4365188. private ValueMember defaultReadObjectFVDMembers[] = null; private org.omg.CORBA_2_3.portable.InputStream orbStream; private CodeBase cbSender; private ValueHandlerImpl vhandler; // d4365188 private Object currentObject = null; private ObjectStreamClass currentClassDesc = null; private Class<?> currentClass = null; private int recursionDepth = 0; private int simpleReadDepth = 0; // The ActiveRecursionManager replaces the old RecursionManager which used to record how many recursions were made, // and resolve them after an object was completely deserialized. // // That created problems (as in bug 4414154) because when custom unmarshaling in readObject, there can be recursive // references to one of the objects currently being unmarshaled, and the passive recursion system failed. ActiveRecursionManager activeRecursionMgr = new ActiveRecursionManager(); private IOException abortIOException = null; /* Remember the first exception that stopped this stream. */ private ClassNotFoundException abortClassNotFoundException = null; /* * Vector of validation callback objects The vector is created as needed. The vector is maintained in order of * highest (first) priority to lowest */ private Vector<?> callbacks; /* * Arrays used to keep track of classes and ObjectStreamClasses as they are being merged; used in inputObject. * spClass is the stack pointer for both. */ ObjectStreamClass[] classdesc; Class<?>[] classes; int spClass; private static final String kEmptyStr = ""; // TCKind TypeCodes used in FVD inputClassFields // public static final TypeCode kRemoteTypeCode = new TypeCodeImpl(TCKind._tk_objref); // public static final TypeCode kValueTypeCode = new TypeCodeImpl(TCKind._tk_value); // removed TypeCodeImpl dependency public static final TypeCode kRemoteTypeCode = ORB.init().get_primitive_tc(TCKind.tk_objref); public static final TypeCode kValueTypeCode = ORB.init().get_primitive_tc(TCKind.tk_value); private byte streamFormatVersion; // Since java.io.OptionalDataException's constructors are package private, but we need to throw it in some special // cases, we try to do it by reflection. private static final Constructor<?> OPT_DATA_EXCEPTION_CTOR; private Object[] readObjectArgList = {this}; static { OPT_DATA_EXCEPTION_CTOR = getOptDataExceptionCtor(); } // Grab the OptionalDataException boolean ctor and make it accessible. Note that any exceptions will be wrapped in // ExceptionInInitializerErrors. private static Constructor<?> getOptDataExceptionCtor() { try { Constructor<?> result = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() { public Constructor<?> run() throws NoSuchMethodException, SecurityException { Constructor<?> boolCtor = OptionalDataException.class .getDeclaredConstructor(new Class[]{Boolean.TYPE}); boolCtor.setAccessible(true); return boolCtor; } }); if (result == null) // XXX I18N, logging needed. throw new Error("Unable to find OptionalDataException constructor"); return result; } catch (Exception ex) { // XXX I18N, logging needed. throw new ExceptionInInitializerError(ex); } } // Create a new OptionalDataException with the EOF marker set to true. See handleOptionalDataMarshalException. private OptionalDataException createOptionalDataException() { try { OptionalDataException result = (OptionalDataException) OPT_DATA_EXCEPTION_CTOR .newInstance(new Object[]{Boolean.TRUE}); if (result == null) // XXX I18N, logging needed. throw new Error("Created null OptionalDataException"); return result; } catch (Exception ex) { // XXX I18N, logging needed. throw new Error("Couldn't create OptionalDataException", ex); } } // Return the stream format version currently being used to deserialize an object protected byte getStreamFormatVersion() { return streamFormatVersion; } // At the beginning of data sent by a writeObject or writeExternal method there is a byte telling the reader the // stream format version. private void readFormatVersion() throws IOException { streamFormatVersion = orbStream.read_octet(); if (streamFormatVersion < 1 || streamFormatVersion > vhandler.getMaximumStreamFormatVersion()) { SystemException sysex = omgWrapper.unsupportedFormatVersion(CompletionStatus.COMPLETED_MAYBE); // XXX I18N? Logging for IOException? IOException result = new IOException("Unsupported format version: " + streamFormatVersion); result.initCause(sysex); throw result; } if (streamFormatVersion == 2) { if (!(orbStream instanceof ValueInputStream)) { SystemException sysex = omgWrapper.notAValueinputstream(CompletionStatus.COMPLETED_MAYBE); // XXX I18N? Logging for IOException? IOException result = new IOException("Not a ValueInputStream"); result.initCause(sysex); throw result; } } } public static void setTestFVDFlag(boolean val) { // useFVDOnly = val; } /** * Dummy constructor; passes upper stream a dummy stream; **/ public IIOPInputStream() throws java.io.IOException { super(); resetStream(); } public final void setOrbStream(org.omg.CORBA_2_3.portable.InputStream os) { orbStream = os; } public final org.omg.CORBA_2_3.portable.InputStream getOrbStream() { return orbStream; } // added setSender and getSender public final void setSender(CodeBase cb) { cbSender = cb; } public final CodeBase getSender() { return cbSender; } // 4365188 this is added to enable backward compatability w/ wrong // rep-ids public final void setValueHandler(ValueHandler vh) { vhandler = (org.jboss.com.sun.corba.se.impl.io.ValueHandlerImpl) vh; } public final ValueHandler getValueHandler() { return vhandler; } public final void increaseRecursionDepth() { recursionDepth++; } public final int decreaseRecursionDepth() { return --recursionDepth; } /** * Override the actions of the final method "readObject()" in ObjectInputStream. * * @since JDK1.1.6 * * Read an object from the ObjectInputStream. The class of the object, the signature of the class, and the * values of the non-transient and non-static fields of the class and all of its supertypes are read. Default * deserializing for a class can be overriden using the writeObject and readObject methods. Objects * referenced by this object are read transitively so that a complete equivalent graph of objects is * reconstructed by readObject. * <p> * * The root object is completly restored when all of its fields and the objects it references are completely * restored. At this point the object validation callbacks are executed in order based on their registered * priorities. The callbacks are registered by objects (in the readObject special methods) as they are * individually restored. * * Exceptions are thrown for problems with the InputStream and for classes that should not be deserialized. * All exceptions are fatal to the InputStream and leave it in an indeterminate state; it is up to the caller * to ignore or recover the stream state. * @exception java.lang.ClassNotFoundException * Class of a serialized object cannot be found. * @exception InvalidClassException * Something is wrong with a class used by serialization. * @exception StreamCorruptedException * Control information in the stream is inconsistent. * @exception OptionalDataException * Primitive data was found in the stream instead of objects. * @exception IOException * Any of the usual Input/Output related exceptions. * @since JDK1.1 */ public final Object readObjectDelegate() throws IOException { try { readObjectState.readData(this); return orbStream.read_abstract_interface(); } catch (MARSHAL marshalException) { handleOptionalDataMarshalException(marshalException, true); throw marshalException; } catch (IndirectionException cdrie) { // The CDR stream had never seen the given offset before, so check the recursion manager (it will throw an // IOException if it doesn't have a reference, either). return activeRecursionMgr.getObject(cdrie.offset); } } final Object simpleReadObject(Class<?> clz, String repositoryID, org.jboss.com.sun.org.omg.SendingContext.CodeBase sender, int offset) /* throws OptionalDataException, ClassNotFoundException, IOException */ { /* Save the current state and get ready to read an object. */ Object prevObject = currentObject; ObjectStreamClass prevClassDesc = currentClassDesc; Class<?> prevClass = currentClass; byte oldStreamFormatVersion = streamFormatVersion; simpleReadDepth++; // Entering Object obj = null; /* * Check for reset, handle it before reading an object. */ try { // d4365188: backward compatability if (vhandler.useFullValueDescription(clz, repositoryID)) { obj = inputObjectUsingFVD(clz, repositoryID, sender, offset); } else { obj = inputObject(clz, repositoryID, sender, offset); } obj = currentClassDesc.readResolve(obj); } catch (ClassNotFoundException cnfe) { bridge.throwException(cnfe); return null; } catch (IOException ioe) { // System.out.println("CLZ = " + clz + "; " + ioe.toString()); bridge.throwException(ioe); return null; } finally { simpleReadDepth--; currentObject = prevObject; currentClassDesc = prevClassDesc; currentClass = prevClass; streamFormatVersion = oldStreamFormatVersion; } /* * Check for thrown exceptions and re-throw them, clearing them if this is the last recursive call . */ IOException exIOE = abortIOException; if (simpleReadDepth == 0) abortIOException = null; if (exIOE != null) { bridge.throwException(exIOE); return null; } ClassNotFoundException exCNF = abortClassNotFoundException; if (simpleReadDepth == 0) abortClassNotFoundException = null; if (exCNF != null) { bridge.throwException(exCNF); return null; } return obj; } public final void simpleSkipObject(String repositoryID, org.jboss.com.sun.org.omg.SendingContext.CodeBase sender) /* throws OptionalDataException, ClassNotFoundException, IOException */ { /* Save the current state and get ready to read an object. */ Object prevObject = currentObject; ObjectStreamClass prevClassDesc = currentClassDesc; Class<?> prevClass = currentClass; byte oldStreamFormatVersion = streamFormatVersion; simpleReadDepth++; // Entering /* * Check for reset, handle it before reading an object. */ try { skipObjectUsingFVD(repositoryID, sender); } catch (ClassNotFoundException cnfe) { bridge.throwException(cnfe); return; } catch (IOException ioe) { bridge.throwException(ioe); return; } finally { simpleReadDepth--; streamFormatVersion = oldStreamFormatVersion; currentObject = prevObject; currentClassDesc = prevClassDesc; currentClass = prevClass; } /* * Check for thrown exceptions and re-throw them, clearing them if this is the last recursive call . */ IOException exIOE = abortIOException; if (simpleReadDepth == 0) abortIOException = null; if (exIOE != null) { bridge.throwException(exIOE); return; } ClassNotFoundException exCNF = abortClassNotFoundException; if (simpleReadDepth == 0) abortClassNotFoundException = null; if (exCNF != null) { bridge.throwException(exCNF); return; } return; } /** * This method is called by trusted subclasses of ObjectOutputStream that constructed ObjectOutputStream using the * protected no-arg constructor. The subclass is expected to provide an override method with the modifier "final". * * @return the Object read from the stream. * * @see #ObjectInputStream() * @see #readObject * @since JDK 1.2 */ protected final Object readObjectOverride() throws OptionalDataException, ClassNotFoundException, IOException { return readObjectDelegate(); } /** * Override the actions of the final method "defaultReadObject()" in ObjectInputStream. * * @since JDK1.1.6 * * Read the non-static and non-transient fields of the current class from this stream. This may only be * called from the readObject method of the class being deserialized. It will throw the NotActiveException if * it is called otherwise. * * @exception java.lang.ClassNotFoundException * if the class of a serialized object could not be found. * @exception IOException * if an I/O error occurs. * @exception NotActiveException * if the stream is not currently reading objects. * @since JDK1.1 */ public final void defaultReadObjectDelegate() /* throws IOException, ClassNotFoundException, NotActiveException */ { try { if (currentObject == null || currentClassDesc == null) // XXX I18N, logging needed. throw new NotActiveException("defaultReadObjectDelegate"); // The array will be null unless fields were retrieved remotely because of a serializable version // difference. Bug fix for 4365188. See the definition of defaultReadObjectFVDMembers for more information. if (defaultReadObjectFVDMembers != null && defaultReadObjectFVDMembers.length > 0) { // WARNING: Be very careful! What if some of these fields actually have to do this, too? This works // because the defaultReadObjectFVDMembers reference is passed to inputClassFields, but there is no // guarantee that defaultReadObjectFVDMembers will point to the same array after calling // inputClassFields. // Use the remote fields to unmarshal. inputClassFields(currentObject, currentClass, currentClassDesc, defaultReadObjectFVDMembers, cbSender); } else { // Use the local fields to unmarshal. ObjectStreamField[] fields = currentClassDesc.getFieldsNoCopy(); if (fields.length > 0) { inputClassFields(currentObject, currentClass, fields, cbSender); } } } catch (NotActiveException nae) { bridge.throwException(nae); } catch (IOException ioe) { bridge.throwException(ioe); } catch (ClassNotFoundException cnfe) { bridge.throwException(cnfe); } } /** * Override the actions of the final method "enableResolveObject()" in ObjectInputStream. * * @since JDK1.1.6 * * Enable the stream to allow objects read from the stream to be replaced. If the stream is a trusted class * it is allowed to enable replacment. Trusted classes are those classes with a classLoader equals null. * <p> * * When enabled the resolveObject method is called for every object being deserialized. * * @exception SecurityException * The classloader of this stream object is non-null. * @since JDK1.1 */ public final boolean enableResolveObjectDelegate(boolean enable) /* throws SecurityException */ { return false; } // The following three methods allow the implementing orbStream to provide mark/reset behavior as defined in // java.io.InputStream. public final void mark(int readAheadLimit) { orbStream.mark(readAheadLimit); } public final boolean markSupported() { return orbStream.markSupported(); } public final void reset() throws IOException { try { orbStream.reset(); } catch (Error e) { IOException err = new IOException(e.getMessage()); err.initCause(e); throw err; } } public final int available() throws IOException { return 0; // unreliable } public final void close() throws IOException { // no op } public final int read() throws IOException { try { readObjectState.readData(this); return (orbStream.read_octet() << 0) & 0x000000FF; } catch (MARSHAL marshalException) { if (marshalException.minor == OMGSystemException.RMIIIOP_OPTIONAL_DATA_INCOMPATIBLE1) { setState(IN_READ_OBJECT_NO_MORE_OPT_DATA); return -1; } throw marshalException; } catch (Error e) { IOException exc = new IOException(e.getMessage()); exc.initCause(e); throw exc; } } public final int read(byte data[], int offset, int length) throws IOException { try { readObjectState.readData(this); orbStream.read_octet_array(data, offset, length); return length; } catch (MARSHAL marshalException) { if (marshalException.minor == OMGSystemException.RMIIIOP_OPTIONAL_DATA_INCOMPATIBLE1) { setState(IN_READ_OBJECT_NO_MORE_OPT_DATA); return -1; } throw marshalException; } catch (Error e) { IOException exc = new IOException(e.getMessage()); exc.initCause(e); throw exc; } } public final boolean readBoolean() throws IOException { try { readObjectState.readData(this); return orbStream.read_boolean(); } catch (MARSHAL marshalException) { handleOptionalDataMarshalException(marshalException, false); throw marshalException; } catch (Error e) { IOException exc = new IOException(e.getMessage()); exc.initCause(e); throw exc; } } public final byte readByte() throws IOException { try { readObjectState.readData(this); return orbStream.read_octet(); } catch (MARSHAL marshalException) { handleOptionalDataMarshalException(marshalException, false); throw marshalException; } catch (Error e) { IOException exc = new IOException(e.getMessage()); exc.initCause(e); throw exc; } } public final char readChar() throws IOException { try { readObjectState.readData(this); return orbStream.read_wchar(); } catch (MARSHAL marshalException) { handleOptionalDataMarshalException(marshalException, false); throw marshalException; } catch (Error e) { IOException exc = new IOException(e.getMessage()); exc.initCause(e); throw exc; } } public final double readDouble() throws IOException { try { readObjectState.readData(this); return orbStream.read_double(); } catch (MARSHAL marshalException) { handleOptionalDataMarshalException(marshalException, false); throw marshalException; } catch (Error e) { IOException exc = new IOException(e.getMessage()); exc.initCause(e); throw exc; } } public final float readFloat() throws IOException { try { readObjectState.readData(this); return orbStream.read_float(); } catch (MARSHAL marshalException) { handleOptionalDataMarshalException(marshalException, false); throw marshalException; } catch (Error e) { IOException exc = new IOException(e.getMessage()); exc.initCause(e); throw exc; } } public final void readFully(byte data[]) throws IOException { // d11623 : implement readFully, required for serializing some core classes readFully(data, 0, data.length); } public final void readFully(byte data[], int offset, int size) throws IOException { // d11623 : implement readFully, required for serializing some core classes try { readObjectState.readData(this); orbStream.read_octet_array(data, offset, size); } catch (MARSHAL marshalException) { handleOptionalDataMarshalException(marshalException, false); throw marshalException; } catch (Error e) { IOException exc = new IOException(e.getMessage()); exc.initCause(e); throw exc; } } public final int readInt() throws IOException { try { readObjectState.readData(this); return orbStream.read_long(); } catch (MARSHAL marshalException) { handleOptionalDataMarshalException(marshalException, false); throw marshalException; } catch (Error e) { IOException exc = new IOException(e.getMessage()); exc.initCause(e); throw exc; } } public final String readLine() throws IOException { // XXX I18N, logging needed. throw new IOException("Method readLine not supported"); } public final long readLong() throws IOException { try { readObjectState.readData(this); return orbStream.read_longlong(); } catch (MARSHAL marshalException) { handleOptionalDataMarshalException(marshalException, false); throw marshalException; } catch (Error e) { IOException exc = new IOException(e.getMessage()); exc.initCause(e); throw exc; } } public final short readShort() throws IOException { try { readObjectState.readData(this); return orbStream.read_short(); } catch (MARSHAL marshalException) { handleOptionalDataMarshalException(marshalException, false); throw marshalException; } catch (Error e) { IOException exc = new IOException(e.getMessage()); exc.initCause(e); throw exc; } } protected final void readStreamHeader() throws IOException, StreamCorruptedException { // no op } public final int readUnsignedByte() throws IOException { try { readObjectState.readData(this); return (orbStream.read_octet() << 0) & 0x000000FF; } catch (MARSHAL marshalException) { handleOptionalDataMarshalException(marshalException, false); throw marshalException; } catch (Error e) { IOException exc = new IOException(e.getMessage()); exc.initCause(e); throw exc; } } public final int readUnsignedShort() throws IOException { try { readObjectState.readData(this); return (orbStream.read_ushort() << 0) & 0x0000FFFF; } catch (MARSHAL marshalException) { handleOptionalDataMarshalException(marshalException, false); throw marshalException; } catch (Error e) { IOException exc = new IOException(e.getMessage()); exc.initCause(e); throw exc; } } /** * Helper method for correcting the Kestrel bug 4367783 (dealing with larger than 8-bit chars). The old behavior is * preserved in orbutil.IIOPInputStream_1_3 in order to interoperate with our legacy ORBs. */ protected String internalReadUTF(org.omg.CORBA.portable.InputStream stream) { return stream.read_wstring(); } public final String readUTF() throws IOException { try { readObjectState.readData(this); return internalReadUTF(orbStream); } catch (MARSHAL marshalException) { handleOptionalDataMarshalException(marshalException, false); throw marshalException; } catch (Error e) { IOException exc = new IOException(e.getMessage()); exc.initCause(e); throw exc; } } // If the ORB stream detects an incompatibility between what's on the wire and what our Serializable's readObject // wants, it throws a MARSHAL exception with a specific minor code. This is rethrown to the readObject as an // OptionalDataException.So far in RMI-IIOP, this process isn't specific enough to tell the readObject how much data // is available, so we always set the OptionalDataException's EOF marker to true. private void handleOptionalDataMarshalException(MARSHAL marshalException, boolean objectRead) throws IOException { // Java Object Serialization spec 3.4: "If the readObject method of the class attempts to read more data than is // present in the optional part of the stream for this class, the stream will return -1 for bytewise reads, // throw an EOFException for primitive data reads, or throw an OptionalDataException with the eof field set to // true for object reads." if (marshalException.minor == OMGSystemException.RMIIIOP_OPTIONAL_DATA_INCOMPATIBLE1) { IOException result; if (!objectRead) result = new EOFException("No more optional data"); else result = createOptionalDataException(); result.initCause(marshalException); setState(IN_READ_OBJECT_NO_MORE_OPT_DATA); throw result; } } public final synchronized void registerValidation(ObjectInputValidation obj, int prio) throws NotActiveException, InvalidObjectException { // XXX I18N, logging needed. throw new Error("Method registerValidation not supported"); } protected final Class<?> resolveClass(ObjectStreamClass v) throws IOException, ClassNotFoundException { // XXX I18N, logging needed. throw new IOException("Method resolveClass not supported"); } protected final Object resolveObject(Object obj) throws IOException { // XXX I18N, logging needed. throw new IOException("Method resolveObject not supported"); } public final int skipBytes(int len) throws IOException { try { readObjectState.readData(this); byte buf[] = new byte[len]; orbStream.read_octet_array(buf, 0, len); return len; } catch (MARSHAL marshalException) { handleOptionalDataMarshalException(marshalException, false); throw marshalException; } catch (Error e) { IOException exc = new IOException(e.getMessage()); exc.initCause(e); throw exc; } } private Object inputObject(Class<?> clz, String repositoryID, org.jboss.com.sun.org.omg.SendingContext.CodeBase sender, int offset) throws IOException, ClassNotFoundException { /* * Get the descriptor and then class of the incoming object. */ currentClassDesc = ObjectStreamClass.lookup(clz); currentClass = currentClassDesc.forClass(); // currentClassDesc.setClass(currentClass); if (currentClass == null) // XXX I18N, logging needed. throw new ClassNotFoundException(currentClassDesc.getName()); try { /* * If Externalizable, Create an instance and tell it to read its data. else, Handle it as a serializable * class. */ if (currentClassDesc.isExternalizable()) { try { currentObject = (currentClass == null) ? null : currentClassDesc.newInstance(); if (currentObject != null) { // Store this object and its beginning position since there might be indirections to it while // it's been unmarshalled. activeRecursionMgr.addObject(offset, currentObject); // Read format version readFormatVersion(); Externalizable ext = (Externalizable) currentObject; ext.readExternal(this); } } catch (InvocationTargetException e) { InvalidClassException exc = new InvalidClassException(currentClass.getName(), "InvocationTargetException accessing no-arg constructor"); exc.initCause(e); throw exc; } catch (UnsupportedOperationException e) { InvalidClassException exc = new InvalidClassException(currentClass.getName(), "UnsupportedOperationException accessing no-arg constructor"); exc.initCause(e); throw exc; } catch (InstantiationException e) { InvalidClassException exc = new InvalidClassException(currentClass.getName(), "InstantiationException accessing no-arg constructor"); exc.initCause(e); throw exc; } } // end : if (currentClassDesc.isExternalizable()) else { /* * Count number of classes and descriptors we might have to work on. */ ObjectStreamClass currdesc = currentClassDesc; Class<?> currclass = currentClass; int spBase = spClass; // current top of stack /* * The object's classes should be processed from supertype to subtype Push all the clases of the current * object onto a stack. Note that only the serializable classes are represented in the descriptor list. * * Handle versioning where one or more supertypes of have been inserted or removed. The stack will * contain pairs of descriptors and the corresponding class. If the object has a class that did not * occur in the original the descriptor will be null. If the original object had a descriptor for a * class not present in the local hierarchy of the object the class will be null. */ /* * This is your basic diff pattern, made simpler because reordering is not allowed. */ // sun.4296963 ibm.11861 // d11861 we should stop when we find the highest serializable class // We need this so that when we allocate the new object below, we // can call the constructor of the non-serializable superclass. // Note that in the JRMP variant of this code the // ObjectStreamClass.lookup() method handles this, but we've put // this fix here rather than change lookup because the new behaviour // is needed in other cases. for (currdesc = currentClassDesc, currclass = currentClass; currdesc != null && currdesc.isSerializable(); /* sun.4296963 ibm.11861 */ currdesc = currdesc.getSuperclass()) { /* * Search the classes to see if the class of this descriptor appears further up the hierarchy. Until * it's found assume its an inserted class. If it's not found, its the descriptor's class that has * been removed. */ Class<?> cc = currdesc.forClass(); Class<?> cl; for (cl = currclass; cl != null; cl = cl.getSuperclass()) { if (cc == cl) { // found a superclass that matches this descriptor break; } else { /* * Ignore a class that doesn't match. No action is needed since it is already initialized. */ } } // end : for (cl = currclass; cl != null; cl = cl.getSuperclass()) /* * Test if there is room for this new entry. If not, double the size of the arrays and copy the * contents. */ spClass++; if (spClass >= classes.length) { int newlen = classes.length * 2; Class<?>[] newclasses = new Class[newlen]; ObjectStreamClass[] newclassdesc = new ObjectStreamClass[newlen]; System.arraycopy(classes, 0, newclasses, 0, classes.length); System.arraycopy(classdesc, 0, newclassdesc, 0, classes.length); classes = newclasses; classdesc = newclassdesc; } if (cl == null) { /* * Class not found corresponding to this descriptor. Pop off all the extra classes pushed. Push * the descriptor and a null class. */ classdesc[spClass] = currdesc; classes[spClass] = null; } else { /* * Current class descriptor matches current class. Some classes may have been inserted. Record * the match and advance the class, continue with the next descriptor. */ classdesc[spClass] = currdesc; classes[spClass] = cl; currclass = cl.getSuperclass(); } } // end : for (currdesc = currentClassDesc, currclass = currentClass; /* * Allocate a new object. The object is only constructed above the highest serializable class and is set * to default values for all more specialized classes. */ try { currentObject = (currentClass == null) ? null : currentClassDesc.newInstance(); // Store this object and its beginning position since there might be indirections to it while it's // been unmarshalled. activeRecursionMgr.addObject(offset, currentObject); } catch (InvocationTargetException e) { InvalidClassException exc = new InvalidClassException(currentClass.getName(), "InvocationTargetException accessing no-arg constructor"); exc.initCause(e); throw exc; } catch (UnsupportedOperationException e) { InvalidClassException exc = new InvalidClassException(currentClass.getName(), "UnsupportedOperationException accessing no-arg constructor"); exc.initCause(e); throw exc; } catch (InstantiationException e) { InvalidClassException exc = new InvalidClassException(currentClass.getName(), "InstantiationException accessing no-arg constructor"); exc.initCause(e); throw exc; } /* * For all the pushed descriptors and classes. if the class has its own writeObject and readObject * methods call the readObject method else invoke the defaultReadObject method */ try { for (; spClass > spBase; spClass--) { /* * Set current descriptor and corresponding class */ currentClassDesc = classdesc[spClass]; currentClass = classes[spClass]; if (classes[spClass] != null) { /* * Read the data from the stream described by the descriptor and store into the matching * class. */ ReadObjectState oldState = readObjectState; setState(DEFAULT_STATE); try { // Changed since invokeObjectReader no longer does this. if (currentClassDesc.hasWriteObject()) { // Read format version readFormatVersion(); // Read defaultWriteObject indicator boolean calledDefaultWriteObject = readBoolean(); readObjectState.beginUnmarshalCustomValue(this, calledDefaultWriteObject, (currentClassDesc.readObjectMethod != null)); } else { if (currentClassDesc.hasReadObject()) setState(IN_READ_OBJECT_REMOTE_NOT_CUSTOM_MARSHALED); } if (!invokeObjectReader(currentClassDesc, currentObject, currentClass) || readObjectState == IN_READ_OBJECT_DEFAULTS_SENT) { // Error case of no readObject and didn't call defaultWriteObject handled in default // state ObjectStreamField[] fields = currentClassDesc.getFieldsNoCopy(); if (fields.length > 0) { inputClassFields(currentObject, currentClass, fields, sender); } } if (currentClassDesc.hasWriteObject()) readObjectState.endUnmarshalCustomValue(this); } finally { setState(oldState); } } else { // _REVISIT_ : Can we ever get here? /* * No local class for this descriptor, Skip over the data for this class. like * defaultReadObject with a null currentObject. The code will read the values but discard * them. */ ObjectStreamField[] fields = currentClassDesc.getFieldsNoCopy(); if (fields.length > 0) { inputClassFields(null, currentClass, fields, sender); } } } } finally { // Make sure we exit at the same stack level as when we started. spClass = spBase; } } } finally { // We've completed deserializing this object. Any future indirections will be handled correctly at the CDR // level. The ActiveRecursionManager only deals with objects currently being deserialized. activeRecursionMgr.removeObject(offset); } return currentObject; } // This retrieves a vector of FVD's for the hierarchy of serializable classes stemming from // repositoryID. It is assumed that the sender will not provide base_value id's for non-serializable // classes! private Vector<FullValueDescription> getOrderedDescriptions(String repositoryID, org.jboss.com.sun.org.omg.SendingContext.CodeBase sender) { Vector<FullValueDescription> descs = new Vector<FullValueDescription>(); if (sender == null) { return descs; } FullValueDescription aFVD = sender.meta(repositoryID); while (aFVD != null) { descs.insertElementAt(aFVD, 0); if ((aFVD.base_value != null) && !kEmptyStr.equals(aFVD.base_value)) { aFVD = sender.meta(aFVD.base_value); } else return descs; } return descs; } /** * This input method uses FullValueDescriptions retrieved from the sender's runtime to read in the data. This method * is capable of throwing out data not applicable to client's fields. This method handles instances where the reader * has a class not sent by the sender, the sender sent a class not present on the reader, and/or the reader's class * does not match the sender's class. * * NOTE : If the local description indicates custom marshaling and the remote type's FVD also indicates custom * marsahling than the local type is used to read the data off the wire. However, if either says custom while the * other does not, a MARSHAL error is thrown. Externalizable is a form of custom marshaling. * */ private Object inputObjectUsingFVD(Class<?> clz, String repositoryID, org.jboss.com.sun.org.omg.SendingContext.CodeBase sender, int offset) throws IOException, ClassNotFoundException { int spBase = spClass; // current top of stack try { /* * Get the descriptor and then class of the incoming object. */ ObjectStreamClass currdesc = currentClassDesc = ObjectStreamClass.lookup(clz); Class<?> currclass = currentClass = clz; /* * If Externalizable, Create an instance and tell it to read its data. else, Handle it as a serializable * class. */ if (currentClassDesc.isExternalizable()) { try { currentObject = (currentClass == null) ? null : currentClassDesc.newInstance(); if (currentObject != null) { // Store this object and its beginning position since there might be indirections to it while // it's been unmarshalled. activeRecursionMgr.addObject(offset, currentObject); // Read format version readFormatVersion(); Externalizable ext = (Externalizable) currentObject; ext.readExternal(this); } } catch (InvocationTargetException e) { InvalidClassException exc = new InvalidClassException(currentClass.getName(), "InvocationTargetException accessing no-arg constructor"); exc.initCause(e); throw exc; } catch (UnsupportedOperationException e) { InvalidClassException exc = new InvalidClassException(currentClass.getName(), "UnsupportedOperationException accessing no-arg constructor"); exc.initCause(e); throw exc; } catch (InstantiationException e) { InvalidClassException exc = new InvalidClassException(currentClass.getName(), "InstantiationException accessing no-arg constructor"); exc.initCause(e); throw exc; } } else { /* * This is your basic diff pattern, made simpler because reordering is not allowed. */ for (currdesc = currentClassDesc, currclass = currentClass; currdesc != null && currdesc.isSerializable(); /* sun.4296963 ibm.11861 */ currdesc = currdesc.getSuperclass()) { /* * Search the classes to see if the class of this descriptor appears further up the hierarchy. Until * it's found assume its an inserted class. If it's not found, its the descriptor's class that has * been removed. */ Class<?> cc = currdesc.forClass(); Class<?> cl; for (cl = currclass; cl != null; cl = cl.getSuperclass()) { if (cc == cl) { // found a superclass that matches this descriptor break; } else { /* * Ignore a class that doesn't match. No action is needed since it is already initialized. */ } } // end : for (cl = currclass; cl != null; cl = cl.getSuperclass()) /* * Test if there is room for this new entry. If not, double the size of the arrays and copy the * contents. */ spClass++; if (spClass >= classes.length) { int newlen = classes.length * 2; Class<?>[] newclasses = new Class[newlen]; ObjectStreamClass[] newclassdesc = new ObjectStreamClass[newlen]; System.arraycopy(classes, 0, newclasses, 0, classes.length); System.arraycopy(classdesc, 0, newclassdesc, 0, classes.length); classes = newclasses; classdesc = newclassdesc; } if (cl == null) { /* * Class not found corresponding to this descriptor. Pop off all the extra classes pushed. Push * the descriptor and a null class. */ classdesc[spClass] = currdesc; classes[spClass] = null; } else { /* * Current class descriptor matches current class. Some classes may have been inserted. Record * the match and advance the class, continue with the next descriptor. */ classdesc[spClass] = currdesc; classes[spClass] = cl; currclass = cl.getSuperclass(); } } // end : for (currdesc = currentClassDesc, currclass = currentClass; /* * Allocate a new object. */ try { currentObject = (currentClass == null) ? null : currentClassDesc.newInstance(); // Store this object and its beginning position since there might be indirections to it while it's // been unmarshalled. activeRecursionMgr.addObject(offset, currentObject); } catch (InvocationTargetException e) { InvalidClassException exc = new InvalidClassException(currentClass.getName(), "InvocationTargetException accessing no-arg constructor"); exc.initCause(e); throw exc; } catch (UnsupportedOperationException e) { InvalidClassException exc = new InvalidClassException(currentClass.getName(), "UnsupportedOperationException accessing no-arg constructor"); exc.initCause(e); throw exc; } catch (InstantiationException e) { InvalidClassException exc = new InvalidClassException(currentClass.getName(), "InstantiationException accessing no-arg constructor"); exc.initCause(e); throw exc; } Enumeration<FullValueDescription> fvdsList = getOrderedDescriptions(repositoryID, sender).elements(); while ((fvdsList.hasMoreElements()) && (spClass > spBase)) { FullValueDescription fvd = fvdsList.nextElement(); // d4365188: backward compatability String repIDForFVD = vhandler.getClassName(fvd.id); String repIDForClass = vhandler.getClassName(vhandler.getRMIRepositoryID(currentClass)); while ((spClass > spBase) && (!repIDForFVD.equals(repIDForClass))) { int pos = findNextClass(repIDForFVD, classes, spClass, spBase); if (pos != -1) { spClass = pos; currclass = currentClass = classes[spClass]; repIDForClass = vhandler.getClassName(vhandler.getRMIRepositoryID(currentClass)); } else { // Read and throw away one level of the fvdslist // This seems to mean that the sender had a superclass that we don't have if (fvd.is_custom) { readFormatVersion(); boolean calledDefaultWriteObject = readBoolean(); if (calledDefaultWriteObject) inputClassFields(null, null, null, fvd.members, sender); if (getStreamFormatVersion() == 2) { ((ValueInputStream) getOrbStream()).start_value(); ((ValueInputStream) getOrbStream()).end_value(); } // WARNING: If stream format version is 1 and there's optional data, we'll get some form // of exception down the line or data corruption. } else { inputClassFields(null, currentClass, null, fvd.members, sender); } if (fvdsList.hasMoreElements()) { fvd = fvdsList.nextElement(); repIDForFVD = vhandler.getClassName(fvd.id); } else return currentObject; } } currdesc = currentClassDesc = ObjectStreamClass.lookup(currentClass); if (!repIDForClass.equals("java.lang.Object")) { // If the sender used custom marshaling, then it should have put the two bytes on the wire // indicating stream format version and whether or not the writeObject method called // defaultWriteObject/writeFields. ReadObjectState oldState = readObjectState; setState(DEFAULT_STATE); try { if (fvd.is_custom) { // Read format version readFormatVersion(); // Read defaultWriteObject indicator boolean calledDefaultWriteObject = readBoolean(); readObjectState.beginUnmarshalCustomValue(this, calledDefaultWriteObject, (currentClassDesc.readObjectMethod != null)); } boolean usedReadObject = false; // Always use readObject if it exists, and fall back to default unmarshaling if it doesn't. try { if (!fvd.is_custom && currentClassDesc.hasReadObject()) setState(IN_READ_OBJECT_REMOTE_NOT_CUSTOM_MARSHALED); // See the definition of defaultReadObjectFVDMembers for more information. This concerns // making sure we use the remote FVD's members in defaultReadObject. defaultReadObjectFVDMembers = fvd.members; usedReadObject = invokeObjectReader(currentClassDesc, currentObject, currentClass); } finally { defaultReadObjectFVDMembers = null; } // Note that the !usedReadObject !calledDefaultWriteObject case is handled by the // beginUnmarshalCustomValue method of the default state if (!usedReadObject || readObjectState == IN_READ_OBJECT_DEFAULTS_SENT) inputClassFields(currentObject, currentClass, currdesc, fvd.members, sender); if (fvd.is_custom) readObjectState.endUnmarshalCustomValue(this); } finally { setState(oldState); } currclass = currentClass = classes[--spClass]; } else { // The remaining hierarchy of the local class does not match the sender's FVD. So, use remaining // FVDs to read data off wire. If any remaining FVDs indicate custom marshaling, throw MARSHAL // error. inputClassFields(null, currentClass, null, fvd.members, sender); while (fvdsList.hasMoreElements()) { fvd = fvdsList.nextElement(); if (fvd.is_custom) skipCustomUsingFVD(fvd.members, sender); else inputClassFields(null, currentClass, null, fvd.members, sender); } } } // end : while(fvdsList.hasMoreElements()) while (fvdsList.hasMoreElements()) { FullValueDescription fvd = fvdsList.nextElement(); if (fvd.is_custom) skipCustomUsingFVD(fvd.members, sender); else throwAwayData(fvd.members, sender); } } return currentObject; } finally { // Make sure we exit at the same stack level as when we started. spClass = spBase; // We've completed deserializing this object. Any future indirections will be handled correctly at the CDR // level. The ActiveRecursionManager only deals with objects currently being deserialized. activeRecursionMgr.removeObject(offset); } } /** * This input method uses FullValueDescriptions retrieved from the sender's runtime to read in the data. This method * is capable of throwing out data not applicable to client's fields. * * NOTE : If the local description indicates custom marshaling and the remote type's FVD also indicates custom * marsahling than the local type is used to read the data off the wire. However, if either says custom while the * other does not, a MARSHAL error is thrown. Externalizable is a form of custom marshaling. * */ private Object skipObjectUsingFVD(String repositoryID, org.jboss.com.sun.org.omg.SendingContext.CodeBase sender) throws IOException, ClassNotFoundException { Enumeration<FullValueDescription> fvdsList = getOrderedDescriptions(repositoryID, sender).elements(); while (fvdsList.hasMoreElements()) { FullValueDescription fvd = fvdsList.nextElement(); String repIDForFVD = vhandler.getClassName(fvd.id); if (!repIDForFVD.equals("java.lang.Object")) { if (fvd.is_custom) { readFormatVersion(); boolean calledDefaultWriteObject = readBoolean(); if (calledDefaultWriteObject) inputClassFields(null, null, null, fvd.members, sender); if (getStreamFormatVersion() == 2) { ((ValueInputStream) getOrbStream()).start_value(); ((ValueInputStream) getOrbStream()).end_value(); } // WARNING: If stream format version is 1 and there's optional data, we'll get some form of // exception down the line. } else { // Use default marshaling inputClassFields(null, null, null, fvd.members, sender); } } } // end : while(fvdsList.hasMoreElements()) return null; } // ///////////////// private int findNextClass(String classname, Class<?> classes[], int _spClass, int _spBase) { for (int i = _spClass; i > _spBase; i--) { if (classname.equals(classes[i].getName())) { return i; } } return -1; } /* * Invoke the readObject method if present. Assumes that in the case of custom marshaling, the format version and * defaultWriteObject indicator were already removed. */ private boolean invokeObjectReader(ObjectStreamClass osc, Object obj, Class<?> aclass) throws InvalidClassException, StreamCorruptedException, ClassNotFoundException, IOException { if (osc.readObjectMethod == null) { return false; } try { osc.readObjectMethod.invoke(obj, readObjectArgList); return true; } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if (t instanceof ClassNotFoundException) throw (ClassNotFoundException) t; else if (t instanceof IOException) throw (IOException) t; else if (t instanceof RuntimeException) throw (RuntimeException) t; else if (t instanceof Error) throw (Error) t; else // XXX I18N, logging needed. throw new Error("internal error"); } catch (IllegalAccessException e) { return false; } } /* * Reset the stream to be just like it was after the constructor. */ private void resetStream() throws IOException { if (classes == null) classes = new Class[20]; else { for (int i = 0; i < classes.length; i++) classes[i] = null; } if (classdesc == null) classdesc = new ObjectStreamClass[20]; else { for (int i = 0; i < classdesc.length; i++) classdesc[i] = null; } spClass = 0; if (callbacks != null) callbacks.setSize(0); // discard any pending callbacks } /** * Factored out of inputClassFields This reads a primitive value and sets it in the field of o described by the * ObjectStreamField field. * * Note that reflection cannot be used here, because reflection cannot be used to set final fields. */ private void inputPrimitiveField(Object o, Class<?> cl, ObjectStreamField field) throws InvalidClassException, IOException { try { switch (field.getTypeCode()) { case 'B' : byte byteValue = orbStream.read_octet(); bridge.putByte(o, field.getFieldID(), byteValue); // reflective code: field.getField().setByte( o, byteValue ) ; break; case 'Z' : boolean booleanValue = orbStream.read_boolean(); bridge.putBoolean(o, field.getFieldID(), booleanValue); // reflective code: field.getField().setBoolean( o, booleanValue ) ; break; case 'C' : char charValue = orbStream.read_wchar(); bridge.putChar(o, field.getFieldID(), charValue); // reflective code: field.getField().setChar( o, charValue ) ; break; case 'S' : short shortValue = orbStream.read_short(); bridge.putShort(o, field.getFieldID(), shortValue); // reflective code: field.getField().setShort( o, shortValue ) ; break; case 'I' : int intValue = orbStream.read_long(); bridge.putInt(o, field.getFieldID(), intValue); // reflective code: field.getField().setInt( o, intValue ) ; break; case 'J' : long longValue = orbStream.read_longlong(); bridge.putLong(o, field.getFieldID(), longValue); // reflective code: field.getField().setLong( o, longValue ) ; break; case 'F' : float floatValue = orbStream.read_float(); bridge.putFloat(o, field.getFieldID(), floatValue); // reflective code: field.getField().setFloat( o, floatValue ) ; break; case 'D' : double doubleValue = orbStream.read_double(); bridge.putDouble(o, field.getFieldID(), doubleValue); // reflective code: field.getField().setDouble( o, doubleValue ) ; break; default : // XXX I18N, logging needed. throw new InvalidClassException(cl.getName()); } } catch (IllegalArgumentException e) { /* * This case should never happen. If the field types are not the same, InvalidClassException is raised when * matching the local class to the serialized ObjectStreamClass. */ ClassCastException cce = new ClassCastException("Assigning instance of class " + field.getType().getName() + " to field " + currentClassDesc.getName() + '#' + field.getField().getName()); cce.initCause(e); throw cce; } } private Object inputObjectField(org.omg.CORBA.ValueMember field, org.jboss.com.sun.org.omg.SendingContext.CodeBase sender) throws IndirectionException, ClassNotFoundException, IOException, StreamCorruptedException { Object objectValue = null; Class<?> type = null; String id = field.id; try { type = vhandler.getClassFromType(id); } catch (ClassNotFoundException cnfe) { // Make sure type = null type = null; } String signature = null; if (type != null) signature = ValueUtility.getSignature(field); if (signature != null && (signature.equals("Ljava/lang/Object;") || signature.equals("Ljava/io/Serializable;") || signature .equals("Ljava/io/Externalizable;"))) { objectValue = javax.rmi.CORBA.Util.readAny(orbStream); } else { // Decide what method call to make based on the type. If it is a type for which we need to load a stub, // convert the type to the correct stub type. // // NOTE : Since FullValueDescription does not allow us to ask whether something is an interface we do not // have the ability to optimize this check. int callType = ValueHandlerImpl.kValueType; if (!vhandler.isSequence(id)) { if (field.type.kind().value() == kRemoteTypeCode.kind().value()) { // RMI Object reference... callType = ValueHandlerImpl.kRemoteType; } else { // REVISIT. If we don't have the local class, we should probably verify that it's an RMI type, query // the remote FVD, and use is_abstract. Our FVD seems to get NullPointerExceptions for any non-RMI // types. // This uses the local class in the same way as inputObjectField(ObjectStreamField) does. REVISIT // inputObjectField(ObjectStreamField)'s loadStubClass logic. Assumption is that the given type // cannot evolve to become a CORBA abstract interface or a RMI abstract interface. if (type != null && type.isInterface() && (vhandler.isAbstractBase(type) || ObjectStreamClassCorbaExt.isAbstractInterface(type))) { callType = ValueHandlerImpl.kAbstractType; } } } // Now that we have used the FVD of the field to determine the proper course of action, it is ok to use the // type (Class) from this point forward since the rep. id for this read will also follow on the wire. switch (callType) { case ValueHandlerImpl.kRemoteType : if (type != null) objectValue = Utility.readObjectAndNarrow(orbStream, type); else objectValue = orbStream.read_Object(); break; case ValueHandlerImpl.kAbstractType : if (type != null) objectValue = Utility.readAbstractAndNarrow(orbStream, type); else objectValue = orbStream.read_abstract_interface(); break; case ValueHandlerImpl.kValueType : if (type != null) objectValue = orbStream.read_value(type); else objectValue = orbStream.read_value(); break; default : // XXX I18N, logging needed. throw new StreamCorruptedException("Unknown callType: " + callType); } } return objectValue; } /** * Factored out of inputClassFields and reused in inputCurrentClassFieldsForReadFields. * * Reads the field (which of an Object type as opposed to a primitive) described by ObjectStreamField field and * returns it. */ private Object inputObjectField(ObjectStreamField field) throws InvalidClassException, StreamCorruptedException, ClassNotFoundException, IndirectionException, IOException { if (ObjectStreamClassCorbaExt.isAny(field.getTypeString())) { return javax.rmi.CORBA.Util.readAny(orbStream); } Object objectValue = null; // fields have an API to provide the actual class corresponding to the data type Class type = osc.forClass(); Class<?> fieldType = field.getType(); Class<?> actualType = fieldType; // This may change if stub loaded. // Decide what method call to make based on the fieldType. If it is a type for which we need to load a stub, // convert the type to the correct stub type. int callType = ValueHandlerImpl.kValueType; boolean narrow = false; if (fieldType.isInterface()) { boolean loadStubClass = false; if (java.rmi.Remote.class.isAssignableFrom(fieldType)) { // RMI Object reference... callType = ValueHandlerImpl.kRemoteType; } else if (org.omg.CORBA.Object.class.isAssignableFrom(fieldType)) { // IDL Object reference... callType = ValueHandlerImpl.kRemoteType; loadStubClass = true; } else if (vhandler.isAbstractBase(fieldType)) { // IDL Abstract Object reference... callType = ValueHandlerImpl.kAbstractType; loadStubClass = true; } else if (ObjectStreamClassCorbaExt.isAbstractInterface(fieldType)) { // RMI Abstract Object reference... callType = ValueHandlerImpl.kAbstractType; } if (loadStubClass) { try { String codebase = Util.getCodebase(fieldType); String repID = vhandler.createForAnyType(fieldType); Class<?> stubType = Utility.loadStubClass(repID, codebase, fieldType); actualType = stubType; } catch (ClassNotFoundException e) { narrow = true; } } else { narrow = true; } } switch (callType) { case ValueHandlerImpl.kRemoteType : if (!narrow) objectValue = orbStream.read_Object(actualType); else objectValue = Utility.readObjectAndNarrow(orbStream, actualType); break; case ValueHandlerImpl.kAbstractType : if (!narrow) objectValue = orbStream.read_abstract_interface(actualType); else objectValue = Utility.readAbstractAndNarrow(orbStream, actualType); break; case ValueHandlerImpl.kValueType : objectValue = orbStream.read_value(actualType); break; default : // XXX I18N, logging needed. throw new StreamCorruptedException("Unknown callType: " + callType); } return objectValue; } private final boolean mustUseRemoteValueMembers() { return defaultReadObjectFVDMembers != null; } void readFields(Map<String, Object> fieldToValueMap) throws InvalidClassException, StreamCorruptedException, ClassNotFoundException, IOException { if (mustUseRemoteValueMembers()) { inputRemoteMembersForReadFields(fieldToValueMap); } else inputCurrentClassFieldsForReadFields(fieldToValueMap); } private final void inputRemoteMembersForReadFields(Map<String, Object> fieldToValueMap) throws InvalidClassException, StreamCorruptedException, ClassNotFoundException, IOException { // Must have this local variable since defaultReadObjectFVDMembers may get mangled by recursion. ValueMember fields[] = defaultReadObjectFVDMembers; try { for (int i = 0; i < fields.length; i++) { switch (fields[i].type.kind().value()) { case TCKind._tk_octet : byte byteValue = orbStream.read_octet(); fieldToValueMap.put(fields[i].name, new Byte(byteValue)); break; case TCKind._tk_boolean : boolean booleanValue = orbStream.read_boolean(); fieldToValueMap.put(fields[i].name, new Boolean(booleanValue)); break; case TCKind._tk_char : // Backwards compatibility. Older Sun ORBs sent // _tk_char even though they read and wrote wchars // correctly. // // Fall through to the _tk_wchar case. case TCKind._tk_wchar : char charValue = orbStream.read_wchar(); fieldToValueMap.put(fields[i].name, new Character(charValue)); break; case TCKind._tk_short : short shortValue = orbStream.read_short(); fieldToValueMap.put(fields[i].name, new Short(shortValue)); break; case TCKind._tk_long : int intValue = orbStream.read_long(); fieldToValueMap.put(fields[i].name, new Integer(intValue)); break; case TCKind._tk_longlong : long longValue = orbStream.read_longlong(); fieldToValueMap.put(fields[i].name, new Long(longValue)); break; case TCKind._tk_float : float floatValue = orbStream.read_float(); fieldToValueMap.put(fields[i].name, new Float(floatValue)); break; case TCKind._tk_double : double doubleValue = orbStream.read_double(); fieldToValueMap.put(fields[i].name, new Double(doubleValue)); break; case TCKind._tk_value : case TCKind._tk_objref : case TCKind._tk_value_box : Object objectValue = null; try { objectValue = inputObjectField(fields[i], cbSender); } catch (IndirectionException cdrie) { // The CDR stream had never seen the given offset before, so check the recursion manager (it // will throw an IOException if it doesn't have a reference, either). objectValue = activeRecursionMgr.getObject(cdrie.offset); } fieldToValueMap.put(fields[i].name, objectValue); break; default : // XXX I18N, logging needed. throw new StreamCorruptedException("Unknown kind: " + fields[i].type.kind().value()); } } } catch (Throwable t) { StreamCorruptedException result = new StreamCorruptedException(t.getMessage()); result.initCause(t); throw result; } } /** * Called from InputStreamHook. * * Reads the fields of the current class (could be the ones queried from the remote FVD) and puts them in the given * Map, name to value. Wraps primitives in the corresponding java.lang Objects. */ private final void inputCurrentClassFieldsForReadFields(Map<String, Object> fieldToValueMap) throws InvalidClassException, StreamCorruptedException, ClassNotFoundException, IOException { ObjectStreamField[] fields = currentClassDesc.getFieldsNoCopy(); int primFields = fields.length - currentClassDesc.objFields; // Handle the primitives first for (int i = 0; i < primFields; ++i) { switch (fields[i].getTypeCode()) { case 'B' : byte byteValue = orbStream.read_octet(); fieldToValueMap.put(fields[i].getName(), new Byte(byteValue)); break; case 'Z' : boolean booleanValue = orbStream.read_boolean(); fieldToValueMap.put(fields[i].getName(), new Boolean(booleanValue)); break; case 'C' : char charValue = orbStream.read_wchar(); fieldToValueMap.put(fields[i].getName(), new Character(charValue)); break; case 'S' : short shortValue = orbStream.read_short(); fieldToValueMap.put(fields[i].getName(), new Short(shortValue)); break; case 'I' : int intValue = orbStream.read_long(); fieldToValueMap.put(fields[i].getName(), new Integer(intValue)); break; case 'J' : long longValue = orbStream.read_longlong(); fieldToValueMap.put(fields[i].getName(), new Long(longValue)); break; case 'F' : float floatValue = orbStream.read_float(); fieldToValueMap.put(fields[i].getName(), new Float(floatValue)); break; case 'D' : double doubleValue = orbStream.read_double(); fieldToValueMap.put(fields[i].getName(), new Double(doubleValue)); break; default : // XXX I18N, logging needed. throw new InvalidClassException(currentClassDesc.getName()); } } /* Read and set object fields from the input stream. */ if (currentClassDesc.objFields > 0) { for (int i = primFields; i < fields.length; i++) { Object objectValue = null; try { objectValue = inputObjectField(fields[i]); } catch (IndirectionException cdrie) { // The CDR stream had never seen the given offset before, so check the recursion manager (it will // throw an IOException if it doesn't have a reference, either). objectValue = activeRecursionMgr.getObject(cdrie.offset); } fieldToValueMap.put(fields[i].getName(), objectValue); } } } /* * Read the fields of the specified class from the input stream and set the values of the fields in the specified * object. If the specified object is null, just consume the fields without setting any values. If any * ObjectStreamField does not have a reflected Field, don't try to set that field in the object. * * REVISIT -- This code doesn't do what the comment says to when getField() is null! */ private void inputClassFields(Object o, Class<?> cl, ObjectStreamField[] fields, org.jboss.com.sun.org.omg.SendingContext.CodeBase sender) throws InvalidClassException, StreamCorruptedException, ClassNotFoundException, IOException { int primFields = fields.length - currentClassDesc.objFields; if (o != null) { for (int i = 0; i < primFields; ++i) { if (fields[i].getField() == null) continue; inputPrimitiveField(o, cl, fields[i]); } } /* Read and set object fields from the input stream. */ if (currentClassDesc.objFields > 0) { for (int i = primFields; i < fields.length; i++) { Object objectValue = null; try { objectValue = inputObjectField(fields[i]); } catch (IndirectionException cdrie) { // The CDR stream had never seen the given offset before, so check the recursion manager (it will // throw an IOException if it doesn't have a reference, either). objectValue = activeRecursionMgr.getObject(cdrie.offset); } if ((o == null) || (fields[i].getField() == null)) { continue; } try { bridge.putObject(o, fields[i].getFieldID(), objectValue); // reflective code: fields[i].getField().set( o, objectValue ) ; } catch (IllegalArgumentException e) { ClassCastException exc = new ClassCastException("Assigning instance of class " + objectValue.getClass().getName() + " to field " + currentClassDesc.getName() + '#' + fields[i].getField().getName()); exc.initCause(e); throw exc; } } // end : for loop } } /* * Read the fields of the specified class from the input stream and set the values of the fields in the specified * object. If the specified object is null, just consume the fields without setting any values. If any * ObjectStreamField does not have a reflected Field, don't try to set that field in the object. */ private void inputClassFields(Object o, Class<?> cl, ObjectStreamClass osc, ValueMember[] fields, org.jboss.com.sun.org.omg.SendingContext.CodeBase sender) throws InvalidClassException, StreamCorruptedException, ClassNotFoundException, IOException { try { for (int i = 0; i < fields.length; ++i) { try { switch (fields[i].type.kind().value()) { case TCKind._tk_octet : byte byteValue = orbStream.read_octet(); if ((o != null) && osc.hasField(fields[i])) setByteField(o, cl, fields[i].name, byteValue); break; case TCKind._tk_boolean : boolean booleanValue = orbStream.read_boolean(); if ((o != null) && osc.hasField(fields[i])) setBooleanField(o, cl, fields[i].name, booleanValue); break; case TCKind._tk_char : // Backwards compatibility. Older Sun ORBs sent _tk_char even though they read and wrote // wchars correctly. // // Fall through to the _tk_wchar case. case TCKind._tk_wchar : char charValue = orbStream.read_wchar(); if ((o != null) && osc.hasField(fields[i])) setCharField(o, cl, fields[i].name, charValue); break; case TCKind._tk_short : short shortValue = orbStream.read_short(); if ((o != null) && osc.hasField(fields[i])) setShortField(o, cl, fields[i].name, shortValue); break; case TCKind._tk_long : int intValue = orbStream.read_long(); if ((o != null) && osc.hasField(fields[i])) setIntField(o, cl, fields[i].name, intValue); break; case TCKind._tk_longlong : long longValue = orbStream.read_longlong(); if ((o != null) && osc.hasField(fields[i])) setLongField(o, cl, fields[i].name, longValue); break; case TCKind._tk_float : float floatValue = orbStream.read_float(); if ((o != null) && osc.hasField(fields[i])) setFloatField(o, cl, fields[i].name, floatValue); break; case TCKind._tk_double : double doubleValue = orbStream.read_double(); if ((o != null) && osc.hasField(fields[i])) setDoubleField(o, cl, fields[i].name, doubleValue); break; case TCKind._tk_value : case TCKind._tk_objref : case TCKind._tk_value_box : Object objectValue = null; try { objectValue = inputObjectField(fields[i], sender); } catch (IndirectionException cdrie) { // The CDR stream had never seen the given offset before, so check the recursion manager // (it will throw an IOException if it doesn't have a reference, either). objectValue = activeRecursionMgr.getObject(cdrie.offset); } if (o == null) continue; try { if (osc.hasField(fields[i])) { setObjectField(o, cl, fields[i].name, objectValue); } else { // REVISIT. Convert to a log message. This is a normal case when fields have been // added as part of evolution, but silently skipping can make it hard to debug if // there's an error // System.out.println("**** warning, not setting field: " // + fields[i].name // + " since not on class " // + osc.getName()); } } catch (IllegalArgumentException e) { // XXX I18N, logging needed. ClassCastException cce = new ClassCastException("Assigning instance of class " + objectValue.getClass().getName() + " to field " + fields[i].name); cce.initCause(e); throw cce; } break; default : // XXX I18N, logging needed. throw new StreamCorruptedException("Unknown kind: " + fields[i].type.kind().value()); } } catch (IllegalArgumentException e) { /* * This case should never happen. If the field types are not the same, InvalidClassException is * raised when matching the local class to the serialized ObjectStreamClass. */ // XXX I18N, logging needed. ClassCastException cce = new ClassCastException("Assigning instance of class " + fields[i].id + " to field " + currentClassDesc.getName() + '#' + fields[i].name); cce.initCause(e); throw cce; } } } catch (Throwable t) { // XXX I18N, logging needed. StreamCorruptedException sce = new StreamCorruptedException(t.getMessage()); sce.initCause(t); throw sce; } } private void skipCustomUsingFVD(ValueMember[] fields, org.jboss.com.sun.org.omg.SendingContext.CodeBase sender) throws InvalidClassException, StreamCorruptedException, ClassNotFoundException, IOException { readFormatVersion(); boolean calledDefaultWriteObject = readBoolean(); if (calledDefaultWriteObject) throwAwayData(fields, sender); if (getStreamFormatVersion() == 2) { ((ValueInputStream) getOrbStream()).start_value(); ((ValueInputStream) getOrbStream()).end_value(); } } /* * Read the fields of the specified class from the input stream throw data away. This must handle same switch logic * as above. */ private void throwAwayData(ValueMember[] fields, org.jboss.com.sun.org.omg.SendingContext.CodeBase sender) throws InvalidClassException, StreamCorruptedException, ClassNotFoundException, IOException { for (int i = 0; i < fields.length; ++i) { try { switch (fields[i].type.kind().value()) { case TCKind._tk_octet : orbStream.read_octet(); break; case TCKind._tk_boolean : orbStream.read_boolean(); break; case TCKind._tk_char : // Backwards compatibility. Older Sun ORBs sent _tk_char even though they read and wrote wchars // correctly. // // Fall through to the _tk_wchar case. case TCKind._tk_wchar : orbStream.read_wchar(); break; case TCKind._tk_short : orbStream.read_short(); break; case TCKind._tk_long : orbStream.read_long(); break; case TCKind._tk_longlong : orbStream.read_longlong(); break; case TCKind._tk_float : orbStream.read_float(); break; case TCKind._tk_double : orbStream.read_double(); break; case TCKind._tk_value : case TCKind._tk_objref : case TCKind._tk_value_box : Class<?> type = null; String id = fields[i].id; try { type = vhandler.getClassFromType(id); } catch (ClassNotFoundException cnfe) { // Make sure type = null type = null; } String signature = null; if (type != null) signature = ValueUtility.getSignature(fields[i]); // Read value try { if ((signature != null) && (signature.equals("Ljava/lang/Object;") || signature.equals("Ljava/io/Serializable;") || signature .equals("Ljava/io/Externalizable;"))) { javax.rmi.CORBA.Util.readAny(orbStream); } else { // Decide what method call to make based on the type. // // NOTE : Since FullValueDescription does not allow us to ask whether something is an // interface we do not have the ability to optimize this check. int callType = ValueHandlerImpl.kValueType; if (!vhandler.isSequence(id)) { FullValueDescription fieldFVD = sender.meta(fields[i].id); if (kRemoteTypeCode == fields[i].type) { // RMI Object reference... callType = ValueHandlerImpl.kRemoteType; } else if (fieldFVD.is_abstract) { // RMI Abstract Object reference... callType = ValueHandlerImpl.kAbstractType; } } // Now that we have used the FVD of the field to determine the proper course of action, // it is ok to use the type (Class) from this point forward since the rep. id for this // read will also follow on the wire. switch (callType) { case ValueHandlerImpl.kRemoteType : orbStream.read_Object(); break; case ValueHandlerImpl.kAbstractType : orbStream.read_abstract_interface(); break; case ValueHandlerImpl.kValueType : if (type != null) { orbStream.read_value(type); } else { orbStream.read_value(); } break; default : // XXX I18N, logging needed. throw new StreamCorruptedException("Unknown callType: " + callType); } } } catch (IndirectionException cdrie) { // Since we are throwing this away, don't bother handling recursion. continue; } break; default : // XXX I18N, logging needed. throw new StreamCorruptedException("Unknown kind: " + fields[i].type.kind().value()); } } catch (IllegalArgumentException e) { /* * This case should never happen. If the field types are not the same, InvalidClassException is raised * when matching the local class to the serialized ObjectStreamClass. */ // XXX I18N, logging needed. ClassCastException cce = new ClassCastException("Assigning instance of class " + fields[i].id + " to field " + currentClassDesc.getName() + '#' + fields[i].name); cce.initCause(e); throw cce; } } } private static void setObjectField(Object o, Class<?> c, String fieldName, Object v) { try { Field fld = c.getDeclaredField(fieldName); long key = bridge.objectFieldOffset(fld); bridge.putObject(o, key, v); } catch (Exception e) { throw utilWrapper.errorSetObjectField(e, fieldName, o.toString(), v.toString()); } } private static void setBooleanField(Object o, Class<?> c, String fieldName, boolean v) { try { Field fld = c.getDeclaredField(fieldName); long key = bridge.objectFieldOffset(fld); bridge.putBoolean(o, key, v); } catch (Exception e) { throw utilWrapper.errorSetBooleanField(e, fieldName, o.toString(), new Boolean(v)); } } private static void setByteField(Object o, Class<?> c, String fieldName, byte v) { try { Field fld = c.getDeclaredField(fieldName); long key = bridge.objectFieldOffset(fld); bridge.putByte(o, key, v); } catch (Exception e) { throw utilWrapper.errorSetByteField(e, fieldName, o.toString(), new Byte(v)); } } private static void setCharField(Object o, Class<?> c, String fieldName, char v) { try { Field fld = c.getDeclaredField(fieldName); long key = bridge.objectFieldOffset(fld); bridge.putChar(o, key, v); } catch (Exception e) { throw utilWrapper.errorSetCharField(e, fieldName, o.toString(), new Character(v)); } } private static void setShortField(Object o, Class<?> c, String fieldName, short v) { try { Field fld = c.getDeclaredField(fieldName); long key = bridge.objectFieldOffset(fld); bridge.putShort(o, key, v); } catch (Exception e) { throw utilWrapper.errorSetShortField(e, fieldName, o.toString(), new Short(v)); } } private static void setIntField(Object o, Class<?> c, String fieldName, int v) { try { Field fld = c.getDeclaredField(fieldName); long key = bridge.objectFieldOffset(fld); bridge.putInt(o, key, v); } catch (Exception e) { throw utilWrapper.errorSetIntField(e, fieldName, o.toString(), new Integer(v)); } } private static void setLongField(Object o, Class<?> c, String fieldName, long v) { try { Field fld = c.getDeclaredField(fieldName); long key = bridge.objectFieldOffset(fld); bridge.putLong(o, key, v); } catch (Exception e) { throw utilWrapper.errorSetLongField(e, fieldName, o.toString(), new Long(v)); } } private static void setFloatField(Object o, Class<?> c, String fieldName, float v) { try { Field fld = c.getDeclaredField(fieldName); long key = bridge.objectFieldOffset(fld); bridge.putFloat(o, key, v); } catch (Exception e) { throw utilWrapper.errorSetFloatField(e, fieldName, o.toString(), new Float(v)); } } private static void setDoubleField(Object o, Class<?> c, String fieldName, double v) { try { Field fld = c.getDeclaredField(fieldName); long key = bridge.objectFieldOffset(fld); bridge.putDouble(o, key, v); } catch (Exception e) { throw utilWrapper.errorSetDoubleField(e, fieldName, o.toString(), new Double(v)); } } /** * This class maintains a map of stream position to an Object currently being deserialized. It is used to handle the * cases where the are indirections to an object on the recursion stack. The CDR level handles indirections to * objects previously seen (and completely deserialized) in the stream. */ static class ActiveRecursionManager { private Map<Integer, Object> offsetToObjectMap; public ActiveRecursionManager() { // A hash map is unsynchronized and allows null values offsetToObjectMap = new HashMap<Integer, Object>(); } // Called right after allocating a new object. Offset is the starting position in the stream of the object. public void addObject(int offset, Object value) { offsetToObjectMap.put(new Integer(offset), value); } // If the given starting position doesn't refer to the beginning of an object currently being deserialized, this // throws an IOException. Otherwise, it returns a reference to the object. public Object getObject(int offset) throws IOException { Integer position = new Integer(offset); if (!offsetToObjectMap.containsKey(position)) // XXX I18N, logging needed. throw new IOException("Invalid indirection to offset " + offset); return offsetToObjectMap.get(position); } // Called when an object has been completely deserialized, so it should no longer be in this mapping. The CDR // level can handle further indirections. public void removeObject(int offset) { offsetToObjectMap.remove(new Integer(offset)); } // If the given offset doesn't map to an Object, then it isn't an indirection to an object currently being // deserialized. public boolean containsObject(int offset) { return offsetToObjectMap.containsKey(new Integer(offset)); } } }