Community
Participate
Working Groups
The following test class reproduces the problem with the serialization compatibility between the JDT compiler and the JDK 1.4 and JDK 1.3 compilers. import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; /** * This is a test class that illustrates a serialization incompatibility * between the eclipse and standard JDK compiler implementations. The problem * manifests itself when trying to serialize bytecode compiled with one * compiler and then trying to deserialize it with bytecode compiled from * another compiler * * @author eldonm */ public class SerializationTest implements Serializable { // (1) The SerializationTest.class reference is what is causing the problem private static String className = SerializationTest.class.getName(); /** * Constructor for SerializationTest. */ public SerializationTest() { super(); } public static void main(String[] args) throws Exception { if (args.length != 2) { printUsage(); System.exit(1); } if ("write".equals(args[0])) { SerializationTest test = new SerializationTest(); ObjectOutputStream out = new ObjectOutputStream( new FileOutputStream(args[1])); out.writeObject(test); out.close(); } else if ("read".equals(args[0])) { ObjectInputStream in = new ObjectInputStream( new FileInputStream(args[1])); SerializationTest test = (SerializationTest) in.readObject(); in.close(); } else { printUsage(); } } static void printUsage() { System.out.println( "Usage: java SerializationTest write|read filename"); } } To reproduce the problem, add this class to a java natured Eclipse project and execute it as a application after adding the parameters "write" followed by the desired serialized file name of the test serialized file (e.g. "c:\\test.ser") Then compile the same code with the JDK 1.3 or 1.4 compiler and execute the application with the read argument on the same test filename (e.g. java read c:\\test.ser"). You will then see a stack trace similar to the following: java.io.InvalidClassException: SerializationTest; local class incomp atible: stream classdesc serialVersionUID = -6933307971724216872, local class se rialVersionUID = -5182857768810774092 at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java :459) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream. java:1521) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.jav a:1435) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStrea m.java:1626) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java: 1274) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:3 24) at SerializationTest.main(Unknown Source) The (1) comment in the code above is the cause of the problem. The static string is assigned a value from the class using the java.lang.Class.getName() method. This triggers the eclipse compiler to synthesize additional class structure as shown below (taken from the decompiled version of the bytecode for the class listing shown above): // JDT private static String className; static Class class$0; /* synthetic field */ static { className = SerializationTest.class.getName(); } // JDK 1.4 static Class class$(String s) { return Class.forName(s); ClassNotFoundException classnotfoundexception; classnotfoundexception; throw new NoClassDefFoundError(classnotfoundexception.getMessage()); } private static String className; static Class class$SerializationTest; /* synthetic field */ static { className = (class$SerializationTest != null ? class$SerializationTest : (class$SerializationTest = class$("SerializationTest"))).getName(); } This difference between the two results in a different class signature, thus resulting in the serialization incompatibility included at the beginning of this report.
Unfortunately, the Java language specs does specify one particular implementation for the class literal. Our implementation is different from others, causing the serialization issues you are seeing; but none is more right than the other. Ours is generating more compact code (one method less). If the specs were enforcing one particular implementation to be the proper one, then we would follow it. On the other end, serialization should ignore synthetics, and it doesn't thus this is a dead-end. Your best take is to use the same binaries on the way in and out. Either use binaries from JDT or from a different compiler on both ends. Similar issues arise with local innerclasses. At last, you could resort to using the serialVersionUID trick to workaround these issues.
Oops, my previous post should read: Unfortunately, the Java language specs does NOT specify one particular implementation for the class literal.
Thanks for the detailed information and rapid response! When we ran into this a few weeks ago, we reverted to just using JDT compiled code everywhere. Has anyone pushed on Sun to improve serialization to ignore synthetics, or to at least remove this ambiguity in the specification? It seems, from my perspective, that even though the JDT code is a bit more compact, it would be great (for portability) to mimic what Sun has been doing until the specification or serialization issue can be worked out. Our primary concern was ensuring automated nightly builds using ant produced the same bytecode the development machines produced so that a complex deployment of a multi-tier system where serialization is used to move data between tiers would succeed. We successfully worked around the issue by replicating the eclipse build process using ant with the build.compiler property set to org.eclipse.jdt.core.JDTCompilerAdapter. It would have been extremely beneficial if Eclipse included a Export to Ant build script feature or a way to execute Eclipse in headless mode from the command-line, as we had to replicate the eclipse setup manually in the ant build script.
Eclipse can certainly be run in headless mode, and in particular the JDT/Core component will run under these conditions (no dependency on UI code). You could setup a workspace in headless mode (our test suites are precisely doing it, you could check org.eclipse.jdt.core.tests.model project on dev.eclipse.org). As for getting Sun to spec either serialization or compiler internals, I remember hearing one of the language guru clearly saying no. This is very unfortunate, and trying to mimic some unspecified behavior is hard (adjusting on a moving target). I think using the same binaries on both ends is the only true answer. FYI, we recently made a couple fixes to our compiler which could result in some serialization issues with respect to serialized instances of local innerclasses (we posted it on jdt-core-dev mailing list). This to say that binaries have to be produced with the exact same compiler to be trustable (same vendor, same version) to be reliable. Hope this helps, and anyway thanks for your original diagnosis of this issue.
Won't change current implementation unless spec would.
*** Bug 30357 has been marked as a duplicate of this bug. ***
*** Bug 32227 has been marked as a duplicate of this bug. ***
*** Bug 37328 has been marked as a duplicate of this bug. ***
Should have been tagged as duplicate
*** This bug has been marked as a duplicate of 10104 ***