Bug 203241 - [compiler] Missing warning when a serializable class without serialVersionUID is also abstract
Summary: [compiler] Missing warning when a serializable class without serialVersionUID...
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.3   Edit
Hardware: PC Windows 2000
: P3 normal (vote)
Target Milestone: 3.4 M2   Edit
Assignee: Olivier Thomann CLA
QA Contact:
URL:
Whiteboard:
Keywords:
: 225339 (view as bug list)
Depends on:
Blocks:
 
Reported: 2007-09-13 05:58 EDT by Mauro Molinari CLA
Modified: 2008-04-02 10:38 EDT (History)
6 users (show)

See Also:


Attachments
Proposed fix + updated regression tests (8.07 KB, patch)
2007-09-13 10:30 EDT, Olivier Thomann CLA
no flags Details | Diff
Proposed fix + updated regression tests (more tests) (17.24 KB, patch)
2007-09-13 12:50 EDT, Olivier Thomann CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Mauro Molinari CLA 2007-09-13 05:58:52 EDT
Build ID: I20070625-1500

Steps To Reproduce:
1. make sure the "Serializable class without serialVersionUID" potential programming problem is set to "warning" or "error" in Eclipse (or project specific) preferences
2. create a new class like this:

import java.io.Serializable;

public abstract class A implements Serializable
{

}

EXPECTED BEHAVIOUR:
Eclipse should show a warning/error because A is serializable and does not define a serialVersionUID

OBSERVED BEHAVIOUR:
Eclipse does not show any warning/error.

More information:
The absence of any error/warning might lead to a programming problem as soon as you are defining a hierarchy of serializable classes (where any of the ancestor is abstract) and you want to make any of the leaf classes safe (with respect to serialization) in the long-term.
Comment 1 Martin Aeschlimann CLA 2007-09-13 06:39:49 EDT
I don't think it makes sense to add a serial version UID to a abstract class.
It can't be instantiated and therefore also not serializable. The concrete
subclasses however should have a serial version UID.

Moving to jdt.core for the last word.
Comment 2 Philipe Mulet CLA 2007-09-13 07:13:38 EDT
I would agree with Martin. Basically, serialVersionUID is not an inherited property. So adding one on the abstract class is not going to buy anything for subtypes. For itself, as it cannot be instantiated, what is the point of defining one ?

Also checked that subtypes are properly warned when missing serialVersionUID.
e.g.
import java.io.Serializable;
abstract class A implements Serializable {
}
class X extends A {} // X should define a serialVersionUID


Marking as INVALID.
No further action planned.
Comment 3 Philipe Mulet CLA 2007-09-13 07:22:21 EDT
Added SerialVersionUIDTests#test007
Comment 4 Mauro Molinari CLA 2007-09-13 08:19:20 EDT
I do not agree with this.

Once you deserialize a non-abstract serializable class that extends an abstract serializable class, the process of creating the instance of the deserialized object involves the initialization of the abstract class code, too.

If you have

A abstract, serializable
B non-abstract, serializable, extends A

when you de-serialize an instance of B, the deserialization process checks not only B serialVersionUID, but A's one, too! If A's serialVersionUID is not defined and A.class has changed, the JRE throws an InvalidClassException when trying to deserialize an instance of B, even if B does have a fixed serialVersionUID!

I recently encountered this exact problem and it was not easy to solve.
Comment 5 Benno Baumgartner CLA 2007-09-13 08:34:39 EDT
Btw we had this discussion before: bug 116733, bug 94352 and probably more
Comment 6 Mauro Molinari CLA 2007-09-13 08:45:59 EDT
Try this.

Given the following classes:

---
import java.io.Serializable;

public abstract class A implements Serializable
{
  public void a()
  {
    System.out.println("a");
  }
}

public class B extends A
{
  private static final long serialVersionUID = -4759527637665705469L;

  public void b()
  {
    System.out.println("b");
  }
}

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;


public class WriteTest
{
  public static void main(String[] args) throws FileNotFoundException, IOException
  {
    B b = new B();
    ObjectOutputStream out =
        new ObjectOutputStream(new FileOutputStream(new File(System
            .getProperty("user.home"), "b.bin")));
    out.writeObject(b);
    out.flush();
    out.close();
  }
}

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;


public class ReadTest
{
  public static void main(String[] args) throws FileNotFoundException,
      IOException, ClassNotFoundException
  {
    ObjectInputStream in =
        new ObjectInputStream(new FileInputStream(new File(System
            .getProperty("user.home"), "b.bin")));
    final B b = (B) in.readObject();
    b.b();
    in.close();
  }
}

---

Run WriteTest.
Then run ReadTest: you should see "b" on the console.
Then modify A so that it then is:

import java.io.Serializable;

public abstract class A implements Serializable
{
  public void a()
  {
    System.out.println("a");
  }
  
  public void aa()
  {
    System.out.println("aa");
  }
}

Run ReadTest again:

Exception in thread "main" java.io.InvalidClassException: A; local class incompatible: stream classdesc serialVersionUID = 3944066497804655483, local class serialVersionUID = 7050343047014399967
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:546)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1552)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1466)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1552)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1466)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1699)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1305)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:348)
	at ReadTest.main(ReadTest.java:16)
Comment 7 Mauro Molinari CLA 2007-09-13 09:43:15 EDT
Due to my comment #6, I think this bug is not invalid, so I'm reopening it.
Comment 8 Olivier Thomann CLA 2007-09-13 10:30:48 EDT
Created attachment 78311 [details]
Proposed fix + updated regression tests
Comment 9 Olivier Thomann CLA 2007-09-13 10:34:24 EDT
javac seems to have the same problem.
See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6356530
Comment 10 Philipe Mulet CLA 2007-09-13 10:44:49 EDT
Re: comment 6
I now believe you are right. Thanks for reopening the bug.
Olivier - pls go ahead and fix it.
Comment 11 Olivier Thomann CLA 2007-09-13 12:50:49 EDT
Created attachment 78341 [details]
Proposed fix + updated regression tests (more tests)

This patch simply fixes more regression tests.
Comment 12 Olivier Thomann CLA 2007-09-13 12:51:15 EDT
Released for 3.4M2.
Updated regression tests.
Comment 13 David Audel CLA 2007-09-18 04:47:39 EDT
Verified for 3.4M2 using build I20070917-0010
Comment 14 Olivier Thomann CLA 2008-04-02 10:38:42 EDT
*** Bug 225339 has been marked as a duplicate of this bug. ***