classpath
[Top][All Lists]
Advanced

[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);
  }
}

reply via email to

[Prev in Thread] Current Thread [Next in Thread]