JDK-6446627 : Reading Serialized arrays of objects throws ClassNotFoundException
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.io:serialization
  • Affected Version: 6
  • Priority: P2
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2006-07-06
  • Updated: 2011-02-16
  • Resolved: 2006-07-06
Related Reports
Duplicate :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
C:\home\projects\squirrel-sql\sql12\test\src>c:\tools\jdk1.6.0\bin\java -version
java version "1.6.0-beta2"
Java(TM) SE Runtime Environment (build 1.6.0-beta2-b86)
Java HotSpot(TM) Client VM (build 1.6.0-beta2-b86, mixed mode, sharing)

ADDITIONAL OS VERSION INFORMATION :
WinXP Pro SP2

A DESCRIPTION OF THE PROBLEM :
This test code works fine in 1.5.0_06, but fails in 1.6.0 beta 2.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile the code and run it.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Expecting the program to exit without error.
ACTUAL -
I see the following stack trace:

Attempting to load class [Ljava.lang.String;
Exception in thread "main" java.lang.RuntimeException: java.lang.ClassNotFoundException: [Ljava.lang.String;
        at squirrel_sql.test.SerializationTest.cloneObject(SerializationTest.java:66)
        at squirrel_sql.test.SerializationTest.main(SerializationTest.java:24)
Caused by: java.lang.ClassNotFoundException: [Ljava.lang.String;
        at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:276)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
        at squirrel_sql.test.SerializationTest$1.resolveClass(SerializationTest.java:55)
        at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1575)
        at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1496)
        at java.io.ObjectInputStream.readArray(ObjectInputStream.java:1624)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1323)
        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
        at squirrel_sql.test.SerializationTest.cloneObject(SerializationTest.java:59)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;

public class SerializeTest {

    public static void main(String[] args) throws Exception {
        String[] objs = new String[10];
        objs[0] = new String();
        String[] objs2 =
            (String[])cloneObject(objs, SerializeTest.class.getClassLoader());
    }
    
    public static Object cloneObject(Object toClone,
                                     final ClassLoader classLoader)
        throws Exception
    {
        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
        ObjectOutputStream oOut = new ObjectOutputStream(bOut);
        oOut.writeObject(toClone);
        oOut.close();
        ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray());
        bOut.close();
        ObjectInputStream oIn = new ObjectInputStream(bIn)
        {
            protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException
            {
                System.out.println("Attempting to load class "+desc.getName());
                return classLoader.loadClass(desc.getName());
            }
        };
        bIn.close();
        Object copy = oIn.readObject();
        oIn.close();
        return copy;
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Don't serialize classes that have arrays of objects as member variables.

Release Regression From : 5.0u7
The above release value was the last known release where this 
bug was not reproducible. Since then there has been a regression.

Comments
EVALUATION Just to be clear, this is not a bug in ObjectInputStream-- the problem is that when the user's anonymous subclass of ObjectInputStream overrides resolveClass to load deserialized classes from the specific class loader, it uses the ClassLoader.loadClass instance method, which does not (necessarily) support loading array classes, instead of using the static three-argument Class.forName method (like ObjectInputStream's own implementation of resolveClass uses). The Class.forName methods do support loading array classes using the same array class name syntax returned by Class.getName and thus also by ObjectStreamClass.getName. [When you want to reflectively load a class by name initiated using a specific class loader, you should not invoke that loader's public loadClass method directly-- instead, you should always use the static three-argument Class.forName method. The ClassLoader.loadClass instance method is more intended for delegation from one class loader to another within a class loading operation (although this is a common confusion and not well described in the documentation). In other words, replace L.loadClass(N) with Class.forName(N,false,L). The Class.forName invocation may eventually end up invoking loadClass on the specified loader, but only after taking care of some other aspects of the VM's standard symbolic class name resolution process-- the significant bit in this case being the support for loading/creation of array classes.] The fact that the ClassLoader.loadClass usage sometimes worked with array class names in previous versions was considered a bug (unintentional behavior) that got fixed in Mustang-- and it did not previously actually "work" fully reliably anyway, which is part of the reason that this "bug" was fixed. There is some related discussion in the text of CR 4976356, and for some further discussion related to serialization usage, see this JINI-USERS post: http://archives.java.sun.com/cgi-bin/wa?A2=ind0604&L=jini-users&P=1084 Even though this issue is not a bug in ObjectInputStream, the fact of the difference in behavior of ClassLoader.loadClass is currently open as 6434149, so this CR has been marked as a duplicate of that one.
10-07-2006

SUGGESTED FIX Use the Class.forName(s, false, cl) method instead of the ClassLoader.loadClass(s), where "s" is a name of array type, "cl" is a class loader.
06-07-2006