ObjectOutputStream.java [plain text]
package java.io;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.util.Hashtable;
import gnu.java.io.ObjectIdentityWrapper;
import gnu.java.lang.reflect.TypeSignature;
import gnu.classpath.Configuration;
public class ObjectOutputStream extends OutputStream
implements ObjectOutput, ObjectStreamConstants
{
public ObjectOutputStream (OutputStream out) throws IOException
{
realOutput = new DataOutputStream (out);
blockData = new byte[ BUFFER_SIZE ];
blockDataCount = 0;
blockDataOutput = new DataOutputStream (this);
setBlockDataMode (true);
replacementEnabled = false;
isSerializing = false;
nextOID = baseWireHandle;
OIDLookupTable = new Hashtable ();
protocolVersion = defaultProtocolVersion;
useSubclassMethod = false;
writeStreamHeader ();
}
public final void writeObject (Object obj) throws IOException
{
if (useSubclassMethod)
{
writeObjectOverride (obj);
return;
}
boolean was_serializing = isSerializing;
boolean old_mode = setBlockDataMode (false);
try
{
isSerializing = true;
boolean replaceDone = false;
Object replacedObject = null;
while (true)
{
if (obj == null)
{
realOutput.writeByte (TC_NULL);
break;
}
Integer handle = findHandle (obj);
if (handle != null)
{
realOutput.writeByte (TC_REFERENCE);
realOutput.writeInt (handle.intValue ());
break;
}
if (obj instanceof Class)
{
Class cl = (Class)obj;
ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject (cl);
assignNewHandle (obj);
realOutput.writeByte (TC_CLASS);
if (!osc.isProxyClass)
{
writeObject (osc);
}
else
{
realOutput.writeByte (TC_PROXYCLASSDESC);
Class[] intfs = cl.getInterfaces();
realOutput.writeInt(intfs.length);
for (int i = 0; i < intfs.length; i++)
realOutput.writeUTF(intfs[i].getName());
boolean oldmode = setBlockDataMode (true);
annotateProxyClass(cl);
setBlockDataMode (oldmode);
realOutput.writeByte(TC_ENDBLOCKDATA);
writeObject (osc.getSuper());
}
break;
}
if (obj instanceof ObjectStreamClass)
{
ObjectStreamClass osc = (ObjectStreamClass)obj;
realOutput.writeByte (TC_CLASSDESC);
realOutput.writeUTF (osc.getName ());
realOutput.writeLong (osc.getSerialVersionUID ());
assignNewHandle (obj);
int flags = osc.getFlags ();
if (protocolVersion == PROTOCOL_VERSION_2
&& osc.isExternalizable ())
flags |= SC_BLOCK_DATA;
realOutput.writeByte (flags);
ObjectStreamField[] fields = osc.fields;
realOutput.writeShort (fields.length);
ObjectStreamField field;
for (int i=0; i < fields.length; i++)
{
field = fields[i];
realOutput.writeByte (field.getTypeCode ());
realOutput.writeUTF (field.getName ());
if (! field.isPrimitive ())
writeObject (field.getTypeString ());
}
boolean oldmode = setBlockDataMode (true);
annotateClass (osc.forClass ());
setBlockDataMode (oldmode);
realOutput.writeByte (TC_ENDBLOCKDATA);
if (osc.isSerializable ())
writeObject (osc.getSuper ());
else
writeObject (null);
break;
}
if ((replacementEnabled || obj instanceof Serializable)
&& ! replaceDone)
{
replacedObject = obj;
if (obj instanceof Serializable)
{
Method m = null;
try
{
Class classArgs[] = {};
m = obj.getClass ().getDeclaredMethod ("writeReplace",
classArgs);
obj = m.invoke (obj, new Object[] {});
}
catch (NoSuchMethodException ignore)
{
}
catch (IllegalAccessException ignore)
{
}
catch (InvocationTargetException ignore)
{
}
}
if (replacementEnabled)
obj = replaceObject (obj);
replaceDone = true;
continue;
}
if (obj instanceof String)
{
realOutput.writeByte (TC_STRING);
assignNewHandle (obj);
realOutput.writeUTF ((String)obj);
break;
}
Class clazz = obj.getClass ();
ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject (clazz);
if (osc == null)
throw new NotSerializableException (clazz.getName ());
if (clazz.isArray ())
{
realOutput.writeByte (TC_ARRAY);
writeObject (osc);
assignNewHandle (obj);
writeArraySizeAndElements (obj, clazz.getComponentType ());
break;
}
realOutput.writeByte (TC_OBJECT);
writeObject (osc);
if (replaceDone)
assignNewHandle (replacedObject);
else
assignNewHandle (obj);
if (obj instanceof Externalizable)
{
if (protocolVersion == PROTOCOL_VERSION_2)
setBlockDataMode (true);
((Externalizable)obj).writeExternal (this);
if (protocolVersion == PROTOCOL_VERSION_2)
{
setBlockDataMode (false);
realOutput.writeByte (TC_ENDBLOCKDATA);
}
break;
}
if (obj instanceof Serializable)
{
currentObject = obj;
ObjectStreamClass[] hierarchy =
ObjectStreamClass.getObjectStreamClasses (clazz);
boolean has_write;
for (int i=0; i < hierarchy.length; i++)
{
currentObjectStreamClass = hierarchy[i];
fieldsAlreadyWritten = false;
has_write = currentObjectStreamClass.hasWriteMethod ();
writeFields (obj, currentObjectStreamClass.fields,
has_write);
}
currentObject = null;
currentObjectStreamClass = null;
currentPutField = null;
break;
}
throw new NotSerializableException (clazz.getName ());
} }
catch (ObjectStreamException ose)
{
throw ose;
}
catch (IOException e)
{
realOutput.writeByte (TC_EXCEPTION);
reset (true);
setBlockDataMode (false);
try
{
writeObject (e);
}
catch (IOException ioe)
{
throw new StreamCorruptedException ("Exception " + ioe + " thrown while exception was being written to stream.");
}
reset (true);
}
finally
{
isSerializing = was_serializing;
setBlockDataMode (old_mode);
}
}
public void defaultWriteObject ()
throws IOException, NotActiveException
{
markFieldsWritten ();
writeFields (currentObject, currentObjectStreamClass.fields, false);
}
private void markFieldsWritten () throws IOException
{
if (currentObject == null || currentObjectStreamClass == null)
throw new NotActiveException ("defaultWriteObject called by non-active class and/or object");
if (fieldsAlreadyWritten)
throw new IOException ("Only one of putFields and defaultWriteObject may be called, and it may only be called once");
fieldsAlreadyWritten = true;
}
public void reset () throws IOException
{
reset (false);
}
private void reset (boolean internal) throws IOException
{
if (!internal)
{
if (isSerializing)
throw new IOException ("Reset called while serialization in progress");
realOutput.writeByte (TC_RESET);
}
clearHandles ();
}
public void useProtocolVersion (int version) throws IOException
{
if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2)
throw new IOException ("Invalid protocol version requested.");
protocolVersion = version;
}
public static void setDefaultProtocolVersion (int version)
throws IOException
{
if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2)
throw new IOException ("Invalid protocol version requested.");
defaultProtocolVersion = version;
}
protected void annotateClass (Class cl) throws IOException
{}
protected void annotateProxyClass(Class cl) throws IOException
{}
protected Object replaceObject (Object obj) throws IOException
{
return obj;
}
protected boolean enableReplaceObject (boolean enable)
throws SecurityException
{
if (enable)
{
SecurityManager sm = System.getSecurityManager ();
if (sm != null)
sm.checkPermission (new SerializablePermission ("enableSubstitution"));
}
boolean old_val = replacementEnabled;
replacementEnabled = enable;
return old_val;
}
protected void writeStreamHeader () throws IOException
{
realOutput.writeShort (STREAM_MAGIC);
realOutput.writeShort (STREAM_VERSION);
}
protected ObjectOutputStream () throws IOException, SecurityException
{
SecurityManager sec_man = System.getSecurityManager ();
if (sec_man != null)
sec_man.checkPermission (SUBCLASS_IMPLEMENTATION_PERMISSION);
useSubclassMethod = true;
}
protected void writeObjectOverride (Object obj) throws NotActiveException,
IOException
{
throw new NotActiveException ("Subclass of ObjectOutputStream must implement writeObjectOverride");
}
public void write (int data) throws IOException
{
if (writeDataAsBlocks)
{
if (blockDataCount == BUFFER_SIZE)
drain ();
blockData[ blockDataCount++ ] = (byte)data;
}
else
realOutput.write (data);
}
public void write (byte[] b) throws IOException
{
write (b, 0, b.length);
}
public void write (byte[] b, int off, int len) throws IOException
{
if (writeDataAsBlocks)
{
if (len < 0)
throw new IndexOutOfBoundsException ();
if (blockDataCount + len < BUFFER_SIZE)
{
System.arraycopy (b, off, blockData, blockDataCount, len);
blockDataCount += len;
}
else
{
drain ();
writeBlockDataHeader (len);
realOutput.write (b, off, len);
}
}
else
realOutput.write (b, off, len);
}
public void flush () throws IOException
{
drain ();
realOutput.flush ();
}
protected void drain () throws IOException
{
if (blockDataCount == 0)
return;
if (writeDataAsBlocks)
writeBlockDataHeader (blockDataCount);
realOutput.write (blockData, 0, blockDataCount);
blockDataCount = 0;
}
public void close () throws IOException
{
flush ();
realOutput.close ();
}
public void writeBoolean (boolean data) throws IOException
{
blockDataOutput.writeBoolean (data);
}
public void writeByte (int data) throws IOException
{
blockDataOutput.writeByte (data);
}
public void writeShort (int data) throws IOException
{
blockDataOutput.writeShort (data);
}
public void writeChar (int data) throws IOException
{
blockDataOutput.writeChar (data);
}
public void writeInt (int data) throws IOException
{
blockDataOutput.writeInt (data);
}
public void writeLong (long data) throws IOException
{
blockDataOutput.writeLong (data);
}
public void writeFloat (float data) throws IOException
{
blockDataOutput.writeFloat (data);
}
public void writeDouble (double data) throws IOException
{
blockDataOutput.writeDouble (data);
}
public void writeBytes (String data) throws IOException
{
blockDataOutput.writeBytes (data);
}
public void writeChars (String data) throws IOException
{
dataOutput.writeChars (data);
}
public void writeUTF (String data) throws IOException
{
dataOutput.writeUTF (data);
}
public static abstract class PutField
{
public abstract void put (String name, boolean value)
throws IOException, IllegalArgumentException;
public abstract void put (String name, byte value)
throws IOException, IllegalArgumentException;
public abstract void put (String name, char value)
throws IOException, IllegalArgumentException;
public abstract void put (String name, double value)
throws IOException, IllegalArgumentException;
public abstract void put (String name, float value)
throws IOException, IllegalArgumentException;
public abstract void put (String name, int value)
throws IOException, IllegalArgumentException;
public abstract void put (String name, long value)
throws IOException, IllegalArgumentException;
public abstract void put (String name, short value)
throws IOException, IllegalArgumentException;
public abstract void put (String name, Object value)
throws IOException, IllegalArgumentException;
public abstract void write (ObjectOutput out) throws IOException;
}
public PutField putFields () throws IOException
{
markFieldsWritten ();
currentPutField = new PutField ()
{
private byte[] prim_field_data
= new byte[currentObjectStreamClass.primFieldSize];
private Object[] objs
= new Object[currentObjectStreamClass.objectFieldCount];
public void put (String name, boolean value)
throws IOException, IllegalArgumentException
{
ObjectStreamField field
= currentObjectStreamClass.getField (name);
checkType (field, 'Z');
prim_field_data[field.getOffset ()] = (byte)(value ? 1 : 0);
}
public void put (String name, byte value)
throws IOException, IllegalArgumentException
{
ObjectStreamField field
= currentObjectStreamClass.getField (name);
checkType (field, 'B');
prim_field_data[field.getOffset ()] = value;
}
public void put (String name, char value)
throws IOException, IllegalArgumentException
{
ObjectStreamField field
= currentObjectStreamClass.getField (name);
checkType (field, 'C');
int off = field.getOffset ();
prim_field_data[off++] = (byte)(value >>> 8);
prim_field_data[off] = (byte)value;
}
public void put (String name, double value)
throws IOException, IllegalArgumentException
{
ObjectStreamField field
= currentObjectStreamClass.getField (name);
checkType (field, 'D');
int off = field.getOffset ();
long l_value = Double.doubleToLongBits (value);
prim_field_data[off++] = (byte)(l_value >>> 52);
prim_field_data[off++] = (byte)(l_value >>> 48);
prim_field_data[off++] = (byte)(l_value >>> 40);
prim_field_data[off++] = (byte)(l_value >>> 32);
prim_field_data[off++] = (byte)(l_value >>> 24);
prim_field_data[off++] = (byte)(l_value >>> 16);
prim_field_data[off++] = (byte)(l_value >>> 8);
prim_field_data[off] = (byte)l_value;
}
public void put (String name, float value)
throws IOException, IllegalArgumentException
{
ObjectStreamField field
= currentObjectStreamClass.getField (name);
checkType (field, 'F');
int off = field.getOffset ();
int i_value = Float.floatToIntBits (value);
prim_field_data[off++] = (byte)(i_value >>> 24);
prim_field_data[off++] = (byte)(i_value >>> 16);
prim_field_data[off++] = (byte)(i_value >>> 8);
prim_field_data[off] = (byte)i_value;
}
public void put (String name, int value)
throws IOException, IllegalArgumentException
{
ObjectStreamField field
= currentObjectStreamClass.getField (name);
checkType (field, 'I');
int off = field.getOffset ();
prim_field_data[off++] = (byte)(value >>> 24);
prim_field_data[off++] = (byte)(value >>> 16);
prim_field_data[off++] = (byte)(value >>> 8);
prim_field_data[off] = (byte)value;
}
public void put (String name, long value)
throws IOException, IllegalArgumentException
{
ObjectStreamField field
= currentObjectStreamClass.getField (name);
checkType (field, 'J');
int off = field.getOffset ();
prim_field_data[off++] = (byte)(value >>> 52);
prim_field_data[off++] = (byte)(value >>> 48);
prim_field_data[off++] = (byte)(value >>> 40);
prim_field_data[off++] = (byte)(value >>> 32);
prim_field_data[off++] = (byte)(value >>> 24);
prim_field_data[off++] = (byte)(value >>> 16);
prim_field_data[off++] = (byte)(value >>> 8);
prim_field_data[off] = (byte)value;
}
public void put (String name, short value)
throws IOException, IllegalArgumentException
{
ObjectStreamField field
= currentObjectStreamClass.getField (name);
checkType (field, 'S');
int off = field.getOffset ();
prim_field_data[off++] = (byte)(value >>> 8);
prim_field_data[off] = (byte)value;
}
public void put (String name, Object value)
throws IOException, IllegalArgumentException
{
ObjectStreamField field
= currentObjectStreamClass.getField (name);
if (field == null)
throw new IllegalArgumentException ();
if (value != null &&
! field.getType ().isAssignableFrom (value.getClass ()))
throw new IllegalArgumentException ();
objs[field.getOffset ()] = value;
}
public void write (ObjectOutput out) throws IOException
{
boolean oldmode = setBlockDataMode (false);
out.write (prim_field_data);
for (int i = 0; i < objs.length; ++ i)
out.writeObject (objs[i]);
setBlockDataMode (oldmode);
}
private void checkType (ObjectStreamField field, char type)
throws IllegalArgumentException
{
if (TypeSignature.getEncodingOfClass (field.getType ()).charAt (0) != type)
throw new IllegalArgumentException ();
}
};
return currentPutField;
}
public void writeFields () throws IOException
{
if (currentPutField == null)
throw new NotActiveException ("writeFields can only be called after putFields has been called");
currentPutField.write (this);
}
private void writeBlockDataHeader (int size) throws IOException
{
if (size < 256)
{
realOutput.writeByte (TC_BLOCKDATA);
realOutput.write (size);
}
else
{
realOutput.writeByte (TC_BLOCKDATALONG);
realOutput.writeInt (size);
}
}
private Integer findHandle (Object obj)
{
return (Integer)OIDLookupTable.get (new ObjectIdentityWrapper (obj));
}
private int assignNewHandle (Object obj)
{
OIDLookupTable.put (new ObjectIdentityWrapper (obj),
new Integer (nextOID));
return nextOID++;
}
private void clearHandles ()
{
nextOID = baseWireHandle;
OIDLookupTable.clear ();
}
private void writeArraySizeAndElements (Object array, Class clazz)
throws IOException
{
int length = Array.getLength (array);
if (clazz.isPrimitive ())
{
if (clazz == Boolean.TYPE)
{
boolean[] cast_array = (boolean[])array;
realOutput.writeInt (length);
for (int i=0; i < length; i++)
realOutput.writeBoolean (cast_array[i]);
return;
}
if (clazz == Byte.TYPE)
{
byte[] cast_array = (byte[])array;
realOutput.writeInt (length);
realOutput.write(cast_array, 0, length);
return;
}
if (clazz == Character.TYPE)
{
char[] cast_array = (char[])array;
realOutput.writeInt (length);
for (int i=0; i < length; i++)
realOutput.writeChar (cast_array[i]);
return;
}
if (clazz == Double.TYPE)
{
double[] cast_array = (double[])array;
realOutput.writeInt (length);
for (int i=0; i < length; i++)
realOutput.writeDouble (cast_array[i]);
return;
}
if (clazz == Float.TYPE)
{
float[] cast_array = (float[])array;
realOutput.writeInt (length);
for (int i=0; i < length; i++)
realOutput.writeFloat (cast_array[i]);
return;
}
if (clazz == Integer.TYPE)
{
int[] cast_array = (int[])array;
realOutput.writeInt (length);
for (int i=0; i < length; i++)
realOutput.writeInt (cast_array[i]);
return;
}
if (clazz == Long.TYPE)
{
long[] cast_array = (long[])array;
realOutput.writeInt (length);
for (int i=0; i < length; i++)
realOutput.writeLong (cast_array[i]);
return;
}
if (clazz == Short.TYPE)
{
short[] cast_array = (short[])array;
realOutput.writeInt (length);
for (int i=0; i < length; i++)
realOutput.writeShort (cast_array[i]);
return;
}
}
else
{
Object[] cast_array = (Object[])array;
realOutput.writeInt (length);
for (int i=0; i < length; i++)
writeObject (cast_array[i]);
}
}
private void writeFields (Object obj,
ObjectStreamField[] fields,
boolean call_write_method) throws IOException
{
if (call_write_method)
{
setBlockDataMode (true);
callWriteMethod (obj);
setBlockDataMode (false);
realOutput.writeByte (TC_ENDBLOCKDATA);
return;
}
boolean oldmode = setBlockDataMode (false);
String field_name;
Class type;
for (int i=0; i < fields.length; i++)
{
field_name = fields[i].getName ();
type = fields[i].getType ();
if (type == Boolean.TYPE)
realOutput.writeBoolean (getBooleanField (obj, field_name));
else if (type == Byte.TYPE)
realOutput.writeByte (getByteField (obj, field_name));
else if (type == Character.TYPE)
realOutput.writeChar (getCharField (obj, field_name));
else if (type == Double.TYPE)
realOutput.writeDouble (getDoubleField (obj, field_name));
else if (type == Float.TYPE)
realOutput.writeFloat (getFloatField (obj, field_name));
else if (type == Integer.TYPE)
realOutput.writeInt (getIntField (obj, field_name));
else if (type == Long.TYPE)
realOutput.writeLong (getLongField (obj, field_name));
else if (type == Short.TYPE)
realOutput.writeShort (getShortField (obj, field_name));
else
writeObject (getObjectField (obj, field_name,
fields[i].getTypeString ()));
}
setBlockDataMode (oldmode);
}
private boolean setBlockDataMode (boolean on) throws IOException
{
if (on == writeDataAsBlocks)
return on;
drain();
boolean oldmode = writeDataAsBlocks;
writeDataAsBlocks = on;
if (on)
dataOutput = blockDataOutput;
else
dataOutput = realOutput;
return oldmode;
}
private void callWriteMethod (Object obj) throws IOException
{
Class klass = obj.getClass ();
try
{
Class classArgs[] = {ObjectOutputStream.class};
Method m = getMethod (klass, "writeObject", classArgs);
if (m == null)
return;
Object args[] = {this};
m.invoke (obj, args);
}
catch (InvocationTargetException x)
{
Throwable exception = x.getTargetException();
if (exception instanceof RuntimeException)
throw (RuntimeException) exception;
if (exception instanceof IOException)
throw (IOException) exception;
throw new IOException ("Exception thrown from writeObject() on " +
klass + ": " + exception.getClass().getName());
}
catch (Exception x)
{
throw new IOException ("Failure invoking writeObject() on " +
klass + ": " + x.getClass().getName());
}
}
private boolean getBooleanField (Object obj, String field_name)
throws IOException
{
try
{
Class klass = obj.getClass ();
Field f = getField (klass, field_name);
boolean b = f.getBoolean (obj);
return b;
}
catch (Exception _)
{
throw new IOException ();
}
}
private byte getByteField (Object obj, String field_name) throws IOException
{
try
{
Class klass = obj.getClass ();
Field f = getField (klass, field_name);
byte b = f.getByte (obj);
return b;
}
catch (Exception _)
{
throw new IOException ();
}
}
private char getCharField (Object obj, String field_name) throws IOException
{
try
{
Class klass = obj.getClass ();
Field f = getField (klass, field_name);
char b = f.getChar (obj);
return b;
}
catch (Exception _)
{
throw new IOException ();
}
}
private double getDoubleField (Object obj, String field_name)
throws IOException
{
try
{
Class klass = obj.getClass ();
Field f = getField (klass, field_name);
double b = f.getDouble (obj);
return b;
}
catch (Exception _)
{
throw new IOException ();
}
}
private float getFloatField (Object obj, String field_name)
throws IOException
{
try
{
Class klass = obj.getClass ();
Field f = getField (klass, field_name);
float b = f.getFloat (obj);
return b;
}
catch (Exception _)
{
throw new IOException ();
}
}
private int getIntField (Object obj, String field_name) throws IOException
{
try
{
Class klass = obj.getClass ();
Field f = getField (klass, field_name);
int b = f.getInt (obj);
return b;
}
catch (Exception _)
{
throw new IOException ();
}
}
private long getLongField (Object obj, String field_name) throws IOException
{
try
{
Class klass = obj.getClass ();
Field f = getField (klass, field_name);
long b = f.getLong (obj);
return b;
}
catch (Exception _)
{
throw new IOException ();
}
}
private short getShortField (Object obj, String field_name)
throws IOException
{
try
{
Class klass = obj.getClass ();
Field f = getField (klass, field_name);
short b = f.getShort (obj);
return b;
}
catch (Exception _)
{
throw new IOException ();
}
}
private Object getObjectField (Object obj, String field_name,
String type_code) throws IOException
{
try
{
Class klass = obj.getClass ();
Field f = getField (klass, field_name);
Object o = f.get (obj);
return o;
}
catch (Exception _)
{
throw new IOException ();
}
}
private static Field getField (Class klass, String name)
throws java.lang.NoSuchFieldException
{
return klass.getDeclaredField(name);
}
private static Method getMethod (Class klass, String name, Class[] args)
throws java.lang.NoSuchMethodException
{
return klass.getDeclaredMethod(name, args);
}
private final static int BUFFER_SIZE = 1024;
private static int defaultProtocolVersion = PROTOCOL_VERSION_2;
private DataOutputStream dataOutput;
private boolean writeDataAsBlocks;
private DataOutputStream realOutput;
private DataOutputStream blockDataOutput;
private byte[] blockData;
private int blockDataCount;
private Object currentObject;
private ObjectStreamClass currentObjectStreamClass;
private PutField currentPutField;
private boolean fieldsAlreadyWritten;
private boolean replacementEnabled;
private boolean isSerializing;
private int nextOID;
private Hashtable OIDLookupTable;
private int protocolVersion;
private boolean useSubclassMethod;
static
{
if (Configuration.INIT_LOAD_LIBRARY)
{
System.loadLibrary ("javaio");
}
}
}