United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-6522514 Extending Arc2D.Double and serializing the object causes InvalidClassException
JDK-6522514 : Extending Arc2D.Double and serializing the object causes InvalidClassException

Details
Type:
Bug
Submit Date:
2007-02-07
Status:
Closed
Updated Date:
2011-05-25
Project Name:
JDK
Resolved Date:
2011-05-23
Component:
client-libs
OS:
windows_xp
Sub-Component:
2d
CPU:
x86
Priority:
P3
Resolution:
Fixed
Affected Versions:
6
Fixed Versions:

Related Reports
Duplicate:
Relates:

Sub Tasks

Description
FULL PRODUCT VERSION :
java version "1.6.0"
Java(TM) SE Runtime Environment (build 1.6.0-b105)
Java HotSpot(TM) Client VM (build 1.6.0-b105, mixed mode, sharing)

On both OS's

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600 italian]
Debian Etch

A DESCRIPTION OF THE PROBLEM :
There seems to be a major problem introduced with serialization in version 1.6.0. The problem here described only happens with Java 1.6.0, and it has been tried with Java 1.5.07 and 1.5.09 both on Debian and Windows XP, and causes no problem on these releases.

We have an application that has classes that extend both java.awt.geom.Line2D.Double and java.awt.geom.Arc2D.Double, which in J2SE5 are NOT serializable. These classes, that in our case are called Line and Arc implement the Serializable interface. Please note that in Java 6 Line2D.Double and Arc2D.Double have been fixed and are now natively serializable.

When you try to serialize the Arc class (extends Arc2D.Double implements Serializable), the JVM causes completely unexpected results, throwing an InvalidClassException with message "No valid constructor", thus making it impossible to serialize anything that extends Arc2D.Double.

Please note that a similar class Line, that does nothing but extending Line2D.Double and implements Serializable, will serialize with no problem whatsoever.

This behaviour is observable only with Java 6. We have run several case studies and the following results apply:

Compiling the source with Java 5 and executing with Java 5 leads exatcly what expected with no error.
Compiling with Java 5 and executing with Java 6 causes the unexpected exception.
Compiling with Java 6 and executing with Java 6 causes the unexpected exception.

Therefore, it seems that the problem resides within the JVM code, not within the compiler. The above behaviour is identical in both Windows XP Home edition and Debian Etch.

Please note that the code below, used to reproduce the bug, also exposes another bug (6317435 - http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6317435) in the stack trace it produces. Don't be confused by the stack trace.

Please note that this seems to be a major issue as the behaviour is at the very heart of one of the most commonly used technologies of Java. We noticed it first when upgrading from Java 5 to Java 6 on a Hibernate-based application on a very complex class hierarchy, and by running several tests to narrow down the cause, it resulted to be one of those dreaded bugs that is exposed by a couple of lines of code.

Refer to the code below to reproduce the bug.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Create one simple class that extends Arc2D.Double and implements Serializable. Write another simple class to try to serialize it to a file. Compile and run using Java 6. See the results.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The expected result is to serialize the simple class to a file, and to be able to deserialize it.
ACTUAL -
The application crashed claiming an InvalidClassException - "No valid constructor" message.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.io.InvalidClassException: Arc; Arc; no valid constructor
	at java.io.ObjectStreamClass.checkDeserialize(Unknown Source)
	at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
	at java.io.ObjectInputStream.readObject0(Unknown Source)
	at java.io.ObjectInputStream.readObject(Unknown Source)
	at SerializationTest.main(SerializationTest.java:20)
Caused by: java.io.InvalidClassException: Arc; no valid constructor
	at java.io.ObjectStreamClass.<init>(Unknown Source)
	at java.io.ObjectStreamClass.lookup(Unknown Source)
	at java.io.ObjectOutputStream.writeObject0(Unknown Source)
	at java.io.ObjectOutputStream.writeObject(Unknown Source)
	at SerializationTest.main(SerializationTest.java:16)

Please note that this stack trace is incorrect, probably due to bug 6317435.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
Create one simple class that extends Arc2D.Double and implements Serializable

------------------------------

import java.awt.geom.Arc2D;
import java.io.Serializable;

public class Arc extends Arc2D.Double implements Serializable {
	public Arc() {
	}
}

------------------------------

Now create the following test class to try to serialize and de-serialize the above class it to a file:

------------------------------

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class SerializationTest {

	public static void main(String args[]) {

		Arc a = new Arc();
		try {
			FileOutputStream fos = new FileOutputStream(new File("object.bin"));
			ObjectOutputStream out = new ObjectOutputStream(fos);
			out.writeObject(a);
			fos.close();
			FileInputStream fis = new FileInputStream(new File("object.bin"));
			ObjectInputStream in = new ObjectInputStream(fis);
			Object o = in.readObject();
			fis.close();
			System.err.println(o);
		}
		catch (Throwable ex) {
			ex.printStackTrace();
		}
		
	}
	
}

------------------------------

Compile using Java 6.

Run the SerializationTest main program. The program will cause an InvalidClassException with "no valid constructor" message.
---------- END SOURCE ----------

                                    

Comments
EVALUATION

[First off, 6317435 was fixed in JDK 6, and the stack trace chain in the Description of this bug report is correct: the stack trace of the top-level exception correctly reflects the fact that the exception is thrown by SerializationTest.main's invocation of ObjectInputStream.readObject, whereas the stack trace of the chained "cause" exception reflects the (earlier) point at which the condition that caused the failure (described below) was actually detected, for use by someone trying to debug the detection of the cause in the serialization implementation.]

The reported failure is caused by the fact that the first non-serializable superclass of the class of the object being deserialized does not have a no-arg constructor that is accessible from the class of the object being deserialized.  Such an accessible no-arg constructor is a requirement of the serialization specification:

http://java.sun.com/javase/6/docs/platform/serialization/spec/serial-arch.html#4539

	A Serializable class must...
	Have access to the no-arg constructor of its first nonserializable superclass

http://java.sun.com/javase/6/docs/platform/serialization/spec/exceptions.html

	InvalidClassException
	The Serializable class can not access the no-arg constructor of its closest
	non-Serializable superclass.

The class of the object being deserialized (in the submitted test case) is "Arc".  Arc extends java.awt.geom.Arc2D.Double, which (in JDK 6) directly implements java.io.Serializable.  java.awt.geom.Arc2D.Double extends java.awt.geom.Arc2D, which does not implement java.io.Serializable (directly or indirectly).  Therefore, java.awt.geom.Arc2D is the first non-serializable superclass of the test class Arc.  java.awt.geom.Arc2D does actually have a no-arg constructor, but it only has "default" package access-- therefore, it is not accessible to the indirect subclass Arc that is the class of the object being deserialized.

This test case worked with JDK 5.0 because in JDK 5.0 java.awt.geom.Arc2D.Double did not implement java.io.Serializable itself, so it was then the first non-serializable superclass of Arc, and it has a no-arg constructor that is public and therefore accessible to Arc.  Making java.awt.geom.Arc2D.Double serializable effectively introduced a requirement on its (non-serializable) direct superclass, java.awt.geom.Arc2D, that did not previously exist.

I'm recategorizing this CR to the classes_2d subcategory for further evaluation.

[One might ask why doesn't the accessibility requirement on this no-arg constructor only apply to the direct subclass of the first non-serializable superclass, more like the language rules, instead of also to the possibly indirect subclass that is the actual class of the object being deserialized-- in which case default package accessibility from java.awt.geom.Arc2D to java.awt.geom.Arc2D.Double would have been sufficient.  I can't answer that question offhand, but the existing requirement has always been the specification requirement (see above text excerpts, which are pretty clear about not just referring to accessibility from the direct subclass) and what the serialization implementation has always enforced.  Typically these no-arg constructors are at least protected if not public, in which case they suffice for all indirect subclasses.]
                                     
2007-02-07
EVALUATION

Fixed by making the constructor protected.  (Since the class is abstract it doesn't really make much of a difference between making it public or protected).
                                     
2011-04-29



Hardware and Software, Engineered to Work Together