diff options
author | Andrew John Hughes <gnu_andrew@member.fsf.org> | 2006-12-24 20:45:05 +0000 |
---|---|---|
committer | Andrew John Hughes <gnu_andrew@member.fsf.org> | 2006-12-24 20:45:05 +0000 |
commit | b4c1698e7f63acc1d4bd86ec2aa2848dd357d020 (patch) | |
tree | 44a94a6a6555519e4f37f4d8dff05edbe259cac1 /java/io | |
parent | 2e0b02cecfd8fc6dbe93f6b9fdc36229ec2e5104 (diff) | |
download | classpath-b4c1698e7f63acc1d4bd86ec2aa2848dd357d020.tar.gz |
2006-12-24 Andrew John Hughes <gnu_andrew@member.fsf.org>
* gnu/classpath/Pair.java: New class.
* java/io/ObjectInputStream.java:
(readUnshared()): Implemented.
(readObject(boolean)): Renamed from readObject
in order to handle unshared serialization.
(parseContent(byte,boolean)): Added unshared parameter.
(assignNewHandle(Object,boolean)): Likewise.
(rememberHandle(Object,boolean,int)): Likewise.
(lookupHandle(int)): Added handling of invalidated handles.
(processResolution(ObjectStreamClass,Object,int,boolean)):
Added unshared parameter.
* java/io/ObjectOutputStream.java:
(writeUnshared()): Implemented.
(writeObject(Object,boolean)): Renamed from
writeObject(Object) in order to handle
unshared serialization.
Diffstat (limited to 'java/io')
-rw-r--r-- | java/io/ObjectInputStream.java | 173 | ||||
-rw-r--r-- | java/io/ObjectOutputStream.java | 88 |
2 files changed, 214 insertions, 47 deletions
diff --git a/java/io/ObjectInputStream.java b/java/io/ObjectInputStream.java index d6c1406ea..219ed01fa 100644 --- a/java/io/ObjectInputStream.java +++ b/java/io/ObjectInputStream.java @@ -39,6 +39,7 @@ exception statement from your version. */ package java.io; +import gnu.classpath.Pair; import gnu.classpath.VMStackWalker; import java.lang.reflect.Array; @@ -50,10 +51,11 @@ import java.lang.reflect.Modifier; import java.lang.reflect.Proxy; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; +import java.util.Map; import java.util.TreeSet; -import java.util.Vector; /** * @author Tom Tromey (tromey@redhat.com) @@ -104,7 +106,7 @@ public class ObjectInputStream extends InputStream this.blockDataInput = new DataInputStream(this); this.realInputStream = new DataInputStream(in); this.nextOID = baseWireHandle; - this.objectLookupTable = new Vector<Object>(); + handles = new HashMap<Integer,Pair<Boolean,Object>>(); this.classLookupTable = new Hashtable<Class,ObjectStreamClass>(); setBlockDataMode(true); readStreamHeader(); @@ -132,6 +134,70 @@ public class ObjectInputStream extends InputStream public final Object readObject() throws ClassNotFoundException, IOException { + return readObject(true); + } + + /** + * <p> + * Returns the next deserialized object read from the + * underlying stream in an unshared manner. Any object + * returned by this method will not be returned by + * subsequent calls to either this method or {@link #readObject()}. + * </p> + * <p> + * This behaviour is achieved by: + * </p> + * <ul> + * <li>Marking the handles created by successful calls to this + * method, so that future calls to {@link #readObject()} or + * {@link #readUnshared()} will throw an {@link ObjectStreamException} + * rather than returning the same object reference.</li> + * <li>Throwing an {@link ObjectStreamException} if the next + * element in the stream is a reference to an earlier object.</li> + * </ul> + * + * @return a reference to the deserialized object. + * @throws ClassNotFoundException if the class of the object being + * deserialized can not be found. + * @throws StreamCorruptedException if information in the stream + * is inconsistent. + * @throws ObjectStreamException if the next object has already been + * returned by an earlier call to this + * method or {@link #readObject()}. + * @throws OptionalDataException if primitive data occurs next in the stream. + * @throws IOException if an I/O error occurs from the stream. + * @since 1.4 + * @see #readObject() + */ + public Object readUnshared() + throws IOException, ClassNotFoundException + { + return readObject(false); + } + + /** + * Returns the next deserialized object read from the underlying stream. + * + * This method can be overriden by a class by implementing + * <code>private void readObject (ObjectInputStream)</code>. + * + * If an exception is thrown from this method, the stream is left in + * an undefined state. This method can also throw Errors and + * RuntimeExceptions if caused by existing readResolve() user code. + * + * @param shared true if handles created by this call should be shared + * with later calls. + * @return The object read from the underlying stream. + * + * @exception ClassNotFoundException The class that an object being + * read in belongs to cannot be found. + * + * @exception IOException Exception from underlying + * <code>InputStream</code>. + */ + private final Object readObject(boolean shared) + throws ClassNotFoundException, IOException + { if (this.useSubclassMethod) return readObjectOverride(); @@ -146,7 +212,7 @@ public class ObjectInputStream extends InputStream try { - ret_val = parseContent(marker); + ret_val = parseContent(marker, shared); } finally { @@ -163,13 +229,15 @@ public class ObjectInputStream extends InputStream * byte indicating its type. * * @param marker the byte marker. + * @param shared true if handles created by this call should be shared + * with later calls. * @return an object which represents the parsed content. * @throws ClassNotFoundException if the class of an object being * read in cannot be found. * @throws IOException if invalid data occurs or one is thrown by the * underlying <code>InputStream</code>. */ - private Object parseContent(byte marker) + private Object parseContent(byte marker, boolean shared) throws ClassNotFoundException, IOException { Object ret_val; @@ -207,6 +275,9 @@ public class ObjectInputStream extends InputStream int oid = realInputStream.readInt(); if(dump) dumpElementln(Integer.toHexString(oid)); ret_val = lookupHandle(oid); + if (!shared) + throw new + InvalidObjectException("References can not be read unshared."); break; } @@ -215,7 +286,7 @@ public class ObjectInputStream extends InputStream if(dump) dumpElementln("CLASS"); ObjectStreamClass osc = (ObjectStreamClass)readObject(); Class clazz = osc.forClass(); - assignNewHandle(clazz); + assignNewHandle(clazz,shared); ret_val = clazz; break; } @@ -250,7 +321,7 @@ public class ObjectInputStream extends InputStream new InternalError("Object ctor missing").initCause(x); } } - assignNewHandle(osc); + assignNewHandle(osc,shared); if (!is_consumed) { @@ -290,7 +361,8 @@ public class ObjectInputStream extends InputStream if(dump) dumpElement("STRING="); String s = this.realInputStream.readUTF(); if(dump) dumpElementln(s); - ret_val = processResolution(null, s, assignNewHandle(s)); + ret_val = processResolution(null, s, assignNewHandle(s,shared), + shared); break; } @@ -303,12 +375,12 @@ public class ObjectInputStream extends InputStream int length = this.realInputStream.readInt(); if(dump) dumpElementln (length + "; COMPONENT TYPE=" + componentType); Object array = Array.newInstance(componentType, length); - int handle = assignNewHandle(array); + int handle = assignNewHandle(array,shared); readArrayElements(array, componentType); if(dump) for (int i = 0, len = Array.getLength(array); i < len; i++) dumpElementln(" ELEMENT[" + i + "]=" + Array.get(array, i)); - ret_val = processResolution(null, array, handle); + ret_val = processResolution(null, array, handle, shared); break; } @@ -326,7 +398,7 @@ public class ObjectInputStream extends InputStream { Externalizable obj = osc.newInstance(); - int handle = assignNewHandle(obj); + int handle = assignNewHandle(obj,shared); boolean read_from_blocks = ((osc.getFlags() & SC_BLOCK_DATA) != 0); @@ -344,14 +416,14 @@ public class ObjectInputStream extends InputStream throw new IOException("No end of block data seen for class with readExternal (ObjectInputStream) method."); } - ret_val = processResolution(osc, obj, handle); + ret_val = processResolution(osc, obj, handle,shared); break; } // end if (osc.realClassIsExternalizable) Object obj = newObject(clazz, osc.firstNonSerializableParentConstructor); - int handle = assignNewHandle(obj); + int handle = assignNewHandle(obj,shared); Object prevObject = this.currentObject; ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass; TreeSet<ValidatorAndPriority> prevObjectValidators = @@ -393,7 +465,7 @@ public class ObjectInputStream extends InputStream byte writeMarker = this.realInputStream.readByte(); while (writeMarker != TC_ENDBLOCKDATA) { - parseContent(writeMarker); + parseContent(writeMarker, shared); writeMarker = this.realInputStream.readByte(); } if(dump) dumpElementln("yes"); @@ -408,7 +480,7 @@ public class ObjectInputStream extends InputStream this.currentObject = prevObject; this.currentObjectStreamClass = prevObjectStreamClass; - ret_val = processResolution(osc, obj, handle); + ret_val = processResolution(osc, obj, handle, shared); if (currentObjectValidators != null) invokeValidators(); this.currentObjectValidators = prevObjectValidators; @@ -442,7 +514,7 @@ public class ObjectInputStream extends InputStream dumpElementln("CONSTANT NAME = " + constantName); Class clazz = osc.forClass(); Enum instance = Enum.valueOf(clazz, constantName); - assignNewHandle(instance); + assignNewHandle(instance,shared); ret_val = instance; break; } @@ -543,7 +615,7 @@ public class ObjectInputStream extends InputStream ObjectStreamField[] fields = new ObjectStreamField[field_count]; ObjectStreamClass osc = new ObjectStreamClass(name, uid, flags, fields); - assignNewHandle(osc); + assignNewHandle(osc,true); for (int i = 0; i < field_count; i++) { @@ -1544,13 +1616,15 @@ public class ObjectInputStream extends InputStream * Assigns the next available handle to <code>obj</code>. * * @param obj The object for which we want a new handle. + * @param shared True if the handle should be shared + * with later calls. * @return A valid handle for the specified object. */ - private int assignNewHandle(Object obj) + private int assignNewHandle(Object obj, boolean shared) { int handle = this.nextOID; this.nextOID = handle + 1; - rememberHandle(obj,handle); + rememberHandle(obj,shared,handle); return handle; } @@ -1558,40 +1632,44 @@ public class ObjectInputStream extends InputStream * Remember the object associated with the given handle. * * @param obj an object - * + * @param shared true if the reference should be shared + * with later calls. * @param handle a handle, must be >= baseWireHandle * * @see #lookupHandle */ - private void rememberHandle(Object obj, int handle) + private void rememberHandle(Object obj, boolean shared, + int handle) { - Vector olt = this.objectLookupTable; - handle = handle - baseWireHandle; - - if (olt.size() <= handle) - olt.setSize(handle + 1); - - olt.set(handle, obj); + handles.put(handle, new Pair<Boolean,Object>(shared, obj)); } /** * Look up the object associated with a given handle. * * @param handle a handle, must be >= baseWireHandle - * * @return the object remembered for handle or null if none. - * + * @throws StreamCorruptedException if the handle is invalid. + * @throws InvalidObjectException if the reference is not shared. * @see #rememberHandle */ private Object lookupHandle(int handle) + throws ObjectStreamException { - Vector olt = this.objectLookupTable; - handle = handle - baseWireHandle; - Object result = handle < olt.size() ? olt.get(handle) : null; - return result; + Pair<Boolean,Object> result = handles.get(handle); + if (result == null) + throw new StreamCorruptedException("The handle, " + + Integer.toHexString(handle) + + ", is invalid."); + if (!result.getLeft()) + throw new InvalidObjectException("The handle, " + + Integer.toHexString(handle) + + ", is not shared."); + return result.getRight(); } - private Object processResolution(ObjectStreamClass osc, Object obj, int handle) + private Object processResolution(ObjectStreamClass osc, Object obj, int handle, + boolean shared) throws IOException { if (osc != null && obj instanceof Serializable) @@ -1622,13 +1700,34 @@ public class ObjectInputStream extends InputStream if (this.resolveEnabled) obj = resolveObject(obj); - rememberHandle(obj, handle); + rememberHandle(obj, shared, handle); + if (!shared) + { + if (obj instanceof byte[]) + return ((byte[]) obj).clone(); + if (obj instanceof short[]) + return ((short[]) obj).clone(); + if (obj instanceof int[]) + return ((int[]) obj).clone(); + if (obj instanceof long[]) + return ((long[]) obj).clone(); + if (obj instanceof char[]) + return ((char[]) obj).clone(); + if (obj instanceof boolean[]) + return ((boolean[]) obj).clone(); + if (obj instanceof float[]) + return ((float[]) obj).clone(); + if (obj instanceof double[]) + return ((double[]) obj).clone(); + if (obj instanceof Object[]) + return ((Object[]) obj).clone(); + } return obj; } private void clearHandles() { - this.objectLookupTable.clear(); + handles.clear(); this.nextOID = baseWireHandle; } @@ -1955,7 +2054,7 @@ public class ObjectInputStream extends InputStream private boolean useSubclassMethod; private int nextOID; private boolean resolveEnabled; - private Vector<Object> objectLookupTable; + private Map<Integer,Pair<Boolean,Object>> handles; private Object currentObject; private ObjectStreamClass currentObjectStreamClass; private TreeSet<ValidatorAndPriority> currentObjectValidators; diff --git a/java/io/ObjectOutputStream.java b/java/io/ObjectOutputStream.java index c3c3df9a3..3a60433f3 100644 --- a/java/io/ObjectOutputStream.java +++ b/java/io/ObjectOutputStream.java @@ -170,6 +170,7 @@ public class ObjectOutputStream extends OutputStream * If an exception is thrown from this method, the stream is left in * an undefined state. * + * @param obj the object to serialize. * @exception NotSerializableException An attempt was made to * serialize an <code>Object</code> that is not serializable. * @@ -178,9 +179,71 @@ public class ObjectOutputStream extends OutputStream * * @exception IOException Exception from underlying * <code>OutputStream</code>. + * @see #writeUnshared(Object) */ public final void writeObject(Object obj) throws IOException { + writeObject(obj, true); + } + + /** + * Writes an object to the stream in the same manner as + * {@link #writeObject(Object)}, but without the use of + * references. As a result, the object is always written + * to the stream in full. Likewise, if an object is written + * by this method and is then later written again by + * {@link #writeObject(Object)}, both calls will write out + * the object in full, as the later call to + * {@link #writeObject(Object)} will know nothing of the + * earlier use of {@link #writeUnshared(Object)}. + * + * @param obj the object to serialize. + * @throws NotSerializableException if the object being + * serialized does not implement + * {@link Serializable}. + * @throws InvalidClassException if a problem occurs with + * the class of the object being + * serialized. + * @throws IOException if an I/O error occurs on the underlying + * <code>OutputStream</code>. + * @since 1.4 + * @see #writeObject(Object) + */ + public void writeUnshared(Object obj) + throws IOException + { + writeObject(obj, false); + } + + /** + * Writes a representation of <code>obj</code> to the underlying + * output stream by writing out information about its class, then + * writing out each of the objects non-transient, non-static + * fields. If any of these fields are other objects, + * they are written out in the same manner. + * + * This method can be overriden by a class by implementing + * <code>private void writeObject (ObjectOutputStream)</code>. + * + * If an exception is thrown from this method, the stream is left in + * an undefined state. + * + * @param obj the object to serialize. + * @param shared true if the serialized object should be + * shared with later calls. + * @exception NotSerializableException An attempt was made to + * serialize an <code>Object</code> that is not serializable. + * + * @exception InvalidClassException Somebody tried to serialize + * an object which is wrongly formatted. + * + * @exception IOException Exception from underlying + * <code>OutputStream</code>. + * @see #writeUnshared(Object) + */ + private final void writeObject(Object obj, boolean shared) + throws IOException + { if (useSubclassMethod) { if (dump) @@ -212,7 +275,7 @@ public class ObjectOutputStream extends OutputStream } int handle = findHandle(obj); - if (handle >= 0) + if (handle >= 0 && shared) { realOutput.writeByte(TC_REFERENCE); realOutput.writeInt(handle); @@ -243,7 +306,8 @@ public class ObjectOutputStream extends OutputStream writeObject(osc.getSuper()); } - assignNewHandle(obj); + if (shared) + assignNewHandle(obj); break; } @@ -263,7 +327,8 @@ public class ObjectOutputStream extends OutputStream /* TC_ENUM classDesc newHandle enumConstantName */ realOutput.writeByte(TC_ENUM); writeObject(osc); - assignNewHandle(obj); + if (shared) + assignNewHandle(obj); writeObject(((Enum) obj).name()); break; } @@ -299,7 +364,8 @@ public class ObjectOutputStream extends OutputStream if (obj instanceof String) { realOutput.writeByte(TC_STRING); - assignNewHandle(obj); + if (shared) + assignNewHandle(obj); realOutput.writeUTF((String)obj); break; } @@ -308,7 +374,8 @@ public class ObjectOutputStream extends OutputStream { realOutput.writeByte(TC_ARRAY); writeObject(osc); - assignNewHandle(obj); + if (shared) + assignNewHandle(obj); writeArraySizeAndElements(obj, clazz.getComponentType()); break; } @@ -316,11 +383,12 @@ public class ObjectOutputStream extends OutputStream realOutput.writeByte(TC_OBJECT); writeObject(osc); - if (replaceDone) - assignNewHandle(replacedObject); - else - assignNewHandle(obj); - + if (shared) + if (replaceDone) + assignNewHandle(replacedObject); + else + assignNewHandle(obj); + if (obj instanceof Externalizable) { if (protocolVersion == PROTOCOL_VERSION_2) |