/*
* 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));
}
}
}