[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Some object serialization problems for discussion(3)-ObjectInputStr
From: |
Bryce McKinlay |
Subject: |
Re: Some object serialization problems for discussion(3)-ObjectInputStream |
Date: |
Fri, 03 Aug 2001 14:01:56 +1200 |
Hi Gansha,
I think that we have already fixed this (among other things) in the libjava
tree. Regrettably we
have not yet merged the changes back into classpath - the merge is non-trivial
due to differences in
the native methods etc in the serialization classes.
I've attached a diff of the changes in the libjava tree, along with a small
serialization test
program. If anyone could help out by merging and testing these changes into the
classpath tree I'd
appreciate it!
regards
Bryce.
"Wu, Gansha" wrote:
> Object serialization problems about ObjectInputStream, part 1:
>
> Consider read (...) here:
>
> public int read (byte data[], int offset, int length) throws IOException
> {
> if (this.readDataFromBlock)
> {
> - if (this.blockDataPosition + length >= this.blockDataBytes){
> + if (this.blockDataPosition + length > this.blockDataBytes){ <- Comment
> 1
> ? int remain = this.blockDataBytes - this.blockDataPosition;
> ? if(remain != 0){
> ? System.arraycopy (this.blockData, this.blockDataPosition,
> ? data, offset, remain);
> ? offset += remain;
> ? length -= remain; <- Comment 2
> }
> readNextBlock ();
> }
>
> System.arraycopy (this.blockData, this.blockDataPosition,
> data, offset, length);
> + this.blockDataPosition += length; <- Comment 3
> return length;
> }
> else
> return this.realInputStream.read (data, offset, length);
> }
>
> Comment 1:
> when "this.blockDataPosition + length == this.blockDataBytes", that
> means the data
> in block is just enough for read, readNextBlock() shouldn't be invoked,
> otherwise it will
> read more unmeaningful data into block which might be object data that must
> be read
> by realInputStream. So we should use ">" instead of ">=" here. In fact
> according to
> implementation, the following is also ok here:
> if (this.blockDataPosition == this.blockDataBytes){ ... ...
>
> Comment 2:
> This patch(beginning with '?' needn't be added actually, because
> implementation here
> won't lead to such a scenario (a read will across two blocks), but in general
> it seems necessary.
>
> Comment 3:
> When data in block is read here, blockDataPosition pointer should be
> updated.
>
> _______________________________________________
> Classpath mailing list
> address@hidden
> http://mail.gnu.org/mailman/listinfo/classpath
2001-04-25 Bryce McKinlay <address@hidden>
Fix PR libgcj/2237:
* java/io/ObjectStreamClass.java (setClass): Calculate
serialVersionUID for local class and compare it against the UID
from the Object Stream. Throw InvalidClassException upon mismatch.
(setUID): Renamed to...
(getClassUID): this. Return the calculated class UID rather than
setting uid field directly.
(getDefinedSUID): Removed.
* java/io/ObjectInputStream.java (resolveClass): Use the
three-argument Class.forName().
* java/io/InvalidClassException (toString): Don't include classname in
result if it is null.
2001-03-08 Tom Tromey <address@hidden>
* java/io/ObjectStreamClass.java (setUID): Don't write interface
info for array classes.
Fixes PR libgcj/1971.
2001-02-13 Bryce McKinlay <address@hidden>
* java/io/BlockDataException.java: Removed.
* java/io/ObjectInputStream.java (readObject): Throw
StreamCorruptedException, not BlockDataException.
* Makefile.am: Remove BlockDataException.
* Makefile.in: Rebuild.
2001-01-27 Bryce McKinlay <address@hidden>
* java/io/ObjectInputStream.java (read): AND byte with 0xff to make
result unsigned.
(read (byte[], int, int)): Only call readNextBlock() if the block
buffer would actually be overrun. Increment blockDataPosition.
(callReadMethod): Propagate exceptions from invocation target.
* java/io/ObjectOutputStream.java (callWriteMethod): Propagate
exceptions from invocation target.
2000-11-24 Bryce McKinlay <address@hidden>
* java/io/ObjectInputStream.java (ObjectInputStream): If DEBUG is set,
test for gcj.dumpobjects property and enable object stream dumping
if it is set.
(dumpElement): No longer native.
(dumpElementln): Ditto.
(setDump): Do not define.
* java/io/natObjectInputStream.cc (dumpElement): Removed.
(dumpElementln): Removed.
(setDump): Removed.
Index: ObjectInputStream.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/io/ObjectInputStream.java,v
retrieving revision 1.5
diff -u -r1.5 ObjectInputStream.java
--- ObjectInputStream.java 2000/11/26 01:48:04 1.5
+++ ObjectInputStream.java 2001/08/03 01:16:18
@@ -135,7 +135,7 @@
else
dumpElementln ("BLOCKDATA");
readNextBlock (marker);
- throw new BlockDataException (this.blockDataBytes);
+ throw new StreamCorruptedException ("Unexpected blockData");
case TC_NULL:
dumpElementln ("NULL");
@@ -199,8 +199,8 @@
(class_name));
}
- setBlockDataMode (true);
- osc.setClass (resolveClass (osc));
+ Class cl = resolveClass (osc);
+ osc.setClass (cl);
setBlockDataMode (false);
if (this.realInputStream.readByte () != TC_ENDBLOCKDATA)
@@ -487,28 +487,16 @@
protected Class resolveClass (ObjectStreamClass osc)
throws ClassNotFoundException, IOException
{
-// DEBUGln ("Resolving " + osc);
-
SecurityManager sm = System.getSecurityManager ();
-
- if (sm == null)
- sm = new SecurityManager () {};
+ // FIXME: currentClassLoader doesn't yet do anything useful. We need
+ // to call forName() with the classloader of the class which called
+ // readObject(). See SecurityManager.getClassContext().
ClassLoader cl = currentClassLoader (sm);
- if (cl == null)
- {
-// DEBUGln ("No class loader found");
- return Class.forName (osc.getName ());
- }
- else
- {
-// DEBUGln ("Using " + cl);
- return cl.loadClass (osc.getName ());
- }
+ return Class.forName (osc.getName (), true, cl);
}
-
/**
Allows subclasses to resolve objects that are read from the
stream with other objects to be returned in their place. This
@@ -577,21 +565,23 @@
{
if (this.blockDataPosition >= this.blockDataBytes)
readNextBlock ();
- return this.blockData[this.blockDataPosition++];
+ return (this.blockData[this.blockDataPosition++] & 0xff);
}
else
return this.realInputStream.read ();
}
- public int read (byte data[], int offset, int length) throws IOException
+ public int read (byte[] data, int offset, int length) throws IOException
{
if (this.readDataFromBlock)
{
- if (this.blockDataPosition + length >= this.blockDataBytes)
+ if (this.blockDataPosition + length > this.blockDataBytes)
readNextBlock ();
System.arraycopy (this.blockData, this.blockDataPosition,
data, offset, length);
+ blockDataPosition += length;
+
return length;
}
else
@@ -959,7 +949,7 @@
de serialization mechanism provided by
<code>ObjectInputStream</code>. To make this method be used for
writing objects, subclasses must invoke the 0-argument
- constructor on this class from there constructor.
+ constructor on this class from their constructor.
@see ObjectInputStream ()
*/
@@ -1359,16 +1349,29 @@
{
try
{
- Class classArgs[] = {Class.forName ("java.io.ObjectInputStream")};
+ Class classArgs[] = {ObjectInputStream.class};
Method m = getMethod (klass, "readObject", classArgs);
if (m == null)
return;
Object args[] = {this};
- m.invoke (obj, args);
+ m.invoke (obj, args);
+ }
+ catch (InvocationTargetException x)
+ {
+ /* Rethrow if possible. */
+ Throwable exception = x.getTargetException();
+ if (exception instanceof RuntimeException)
+ throw (RuntimeException) exception;
+ if (exception instanceof IOException)
+ throw (IOException) exception;
+
+ throw new IOException ("Exception thrown from readObject() on " +
+ klass + ": " + exception.getClass().getName());
}
- catch (Exception _)
+ catch (Exception x)
{
- throw new IOException ();
+ throw new IOException ("Failure invoking readObject() on " +
+ klass + ": " + x.getClass().getName());
}
}
Index: ObjectOutputStream.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/io/ObjectOutputStream.java,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- ObjectOutputStream.java 2000/10/05 23:57:16 1.7
+++ ObjectOutputStream.java 2001/01/27 06:04:29 1.8
@@ -633,7 +633,7 @@
/**
@see java.io.DataOutputStream#write (byte[])
*/
- public void write (byte b[]) throws IOException
+ public void write (byte[] b) throws IOException
{
write (b, 0, b.length);
}
@@ -642,7 +642,7 @@
/**
@see java.io.DataOutputStream#write (byte[],int,int)
*/
- public void write (byte b[], int off, int len) throws IOException
+ public void write (byte[] b, int off, int len) throws IOException
{
if (writeDataAsBlocks)
{
@@ -1175,22 +1175,35 @@
private void callWriteMethod (Object obj) throws IOException
{
+ Class klass = obj.getClass ();
try
{
- Class classArgs[] = {Class.forName ("java.io.ObjectOutputStream")};
- Class klass = obj.getClass ();
+ Class classArgs[] = {ObjectOutputStream.class};
Method m = getMethod (klass, "writeObject", classArgs);
if (m == null)
return;
Object args[] = {this};
m.invoke (obj, args);
}
- catch (Exception _)
+ catch (InvocationTargetException x)
{
- throw new IOException ();
+ /* Rethrow if possible. */
+ 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
@@ -1331,7 +1344,7 @@
private static native Field getField (Class klass, String name)
throws java.lang.NoSuchFieldException;
- private static native Method getMethod (Class klass, String name, Class
args[])
+ private static native Method getMethod (Class klass, String name, Class[]
args)
throws java.lang.NoSuchMethodException;
// this value comes from 1.2 spec, but is used in 1.1 as well
Index: ObjectStreamClass.java
===================================================================
RCS file: /cvs/gcc/gcc/libjava/java/io/ObjectStreamClass.java,v
retrieving revision 1.5
retrieving revision 1.8
diff -u -r1.5 -r1.8
--- ObjectStreamClass.java 2000/09/08 19:37:08 1.5
+++ ObjectStreamClass.java 2001/04/26 02:02:05 1.8
@@ -1,6 +1,6 @@
/* ObjectStreamClass.java -- Class used to write class information
about serialized objects.
- Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
+ Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -186,7 +186,7 @@
// Returns the <code>ObjectStreamClass</code> that represents the
// class that is the superclass of the class this
- // <code>ObjectStreamClass</cdoe> represents. If the superclass is
+ // <code>ObjectStreamClass</code> represents. If the superclass is
// not Serializable, null is returned.
ObjectStreamClass getSuper ()
{
@@ -246,13 +246,27 @@
this.fields = fields;
}
-
- void setClass (Class clazz)
+ void setClass (Class cl) throws InvalidClassException
{
- this.clazz = clazz;
+ this.clazz = cl;
+ long class_uid = getClassUID (cl);
+ if (uid == 0)
+ {
+ uid = class_uid;
+ return;
+ }
+
+ // Check that the actual UID of the resolved class matches the UID from
+ // the stream.
+ if (uid != class_uid)
+ {
+ String msg = cl +
+ ": Local class not compatible: stream serialVersionUID="
+ + uid + ", local serialVersionUID=" + class_uid;
+ throw new InvalidClassException (msg);
+ }
}
-
void setSuperclass (ObjectStreamClass osc)
{
superClass = osc;
@@ -308,7 +322,7 @@
name = cl.getName ();
setFlags (cl);
setFields (cl);
- setUID (cl);
+ uid = getClassUID (cl);
superClass = lookup (cl.getSuperclass ());
}
@@ -396,24 +410,24 @@
calculateOffsets ();
}
- // Sets uid to be serial version UID defined by class, or if that
+ // Returns the serial version UID defined by class, or if that
// isn't present, calculates value of serial version UID.
- private void setUID (Class cl)
+ private long getClassUID (Class cl)
{
try
{
Field suid = cl.getDeclaredField ("serialVersionUID");
int modifiers = suid.getModifiers ();
- if (Modifier.isStatic (modifiers)
- && Modifier.isFinal (modifiers))
- {
- uid = getDefinedSUID (cl);
- return;
- }
+ if (Modifier.isStatic (modifiers) && Modifier.isFinal (modifiers))
+ return suid.getLong (null);
}
catch (NoSuchFieldException ignore)
- {}
+ {
+ }
+ catch (IllegalAccessException ignore)
+ {
+ }
// cl didn't define serialVersionUID, so we have to compute it
try
@@ -443,12 +457,16 @@
modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL
| Modifier.INTERFACE | Modifier.PUBLIC);
data_out.writeInt (modifiers);
-
- Class[] interfaces = cl.getInterfaces ();
- Arrays.sort (interfaces, interfaceComparator);
- for (int i=0; i < interfaces.length; i++)
- data_out.writeUTF (interfaces[i].getName ());
+ // Pretend that an array has no interfaces, because when array
+ // serialization was defined (JDK 1.1), arrays didn't have it.
+ if (! cl.isArray ())
+ {
+ Class[] interfaces = cl.getInterfaces ();
+ Arrays.sort (interfaces, interfaceComparator);
+ for (int i=0; i < interfaces.length; i++)
+ data_out.writeUTF (interfaces[i].getName ());
+ }
Field field;
Field[] fields = cl.getDeclaredFields ();
@@ -530,7 +548,7 @@
for (int i=0; i < len; i++)
result += (long)(sha[i] & 0xFF) << (8 * i);
- uid = result;
+ return result;
}
catch (NoSuchAlgorithmException e)
{
@@ -541,31 +559,6 @@
{
throw new RuntimeException (ioe.getMessage ());
}
- }
-
-
- // Returns the value of CLAZZ's final static long field named
- // `serialVersionUID'.
- private long getDefinedSUID (Class clazz)
- {
- long l = 0;
- try
- {
- // Use getDeclaredField rather than getField, since serialVersionUID
- // may not be public AND we only want the serialVersionUID of this
- // class, not a superclass or interface.
- Field f = clazz.getDeclaredField ("serialVersionUID");
- l = f.getLong (null);
- }
- catch (java.lang.NoSuchFieldException e)
- {
- }
-
- catch (java.lang.IllegalAccessException e)
- {
- }
-
- return l;
}
// Returns the value of CLAZZ's private static final field named
import java.util.*;
import java.io.*;
public class SerTest
{
static void showUsage()
{
System.err.println("Usage: SerTest <read|write>");
System.exit(1);
}
public static void main(String[] args)
throws IOException, FileNotFoundException, ClassNotFoundException
{
if (args.length == 0)
showUsage();
HashMap hm = null;
if (args[0].equals("write"))
{
hm = new HashMap();
hm.put("one", "1");
hm.put("two", "2");
hm.put("three", "3");
ObjectOutputStream out =
new ObjectOutputStream(new FileOutputStream("test.ser"));
out.writeObject(hm);
out.close();
}
else if (args[0].equals("read"))
{
ObjectInputStream in =
new ObjectInputStream(new FileInputStream("test.ser"));
hm = (HashMap) in.readObject();
}
else
showUsage();
System.out.println(hm);
}
}