Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 120642 - [refactoring] modifying a serializable inner class breaks (de)serialization
Summary: [refactoring] modifying a serializable inner class breaks (de)serialization
Status: RESOLVED DUPLICATE of bug 97411
Alias: None
Product: JDT
Classification: Eclipse Project
Component: UI (show other bugs)
Version: 3.2   Edit
Hardware: PC Windows XP
: P3 normal (vote)
Target Milestone: 3.2 M5   Edit
Assignee: Tobias Widmer CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2005-12-13 12:31 EST by Michael Moser CLA
Modified: 2005-12-14 11:24 EST (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Michael Moser CLA 2005-12-13 12:31:29 EST
I stumbled over a very peculiar bug:

I had refactored a static local class out into a file of its own. In the process eclipse obviously also modified the visibility of the two methods

	private void writeObject(ObjectOutputStream out) { ... } 
	private void readObject(ObjectInputStream in) { ... }

to package visibility, i.e.:

	void writeObject(ObjectOutputStream out) { ... } 
	void readObject(ObjectInputStream in) { ... }

But these two methods are used for (de)serialization and obviously, for proper (de)serialization, the *private* visibility of these methods is important! If the methods are not private, they will not be called at all. I thus ran into the strange problem that fields of a class were null, even though they had NOT been null before serializing them. And the (de-)serialization (at first) did not appear to be the culprit, since it had always worked perfectly before the refactoring.

I recall that eclipse popped up a list of possible problems, among them that it was going to change the visibility of the "constructors" writeObject() & readObject() to default, but those warnings were obviously not enough to make me aware of the consequence, that these methods were going to be turned into useless, dead code...

Summarizing: the refactoring MUST ABSOLUTELY NOT modify the visibility of these two very special methods!

Michael
Comment 1 Martin Aeschlimann CLA 2005-12-13 16:58:46 EST
I guess other refactorings have to be handling this case too. Markus, Tobias can you coordinate?
Comment 2 Tobias Widmer CLA 2005-12-14 04:09:46 EST
We can modify this behaviour (changing visibility) for all refactorings in the MemberVisibilityAdjustor by excluding special methods.

Other refactorings which probably should be more aware of special methods are:
- Rename method
- Change method signature

Those refactoring could issue a warning since they are not behavior preserving on special methods.
Comment 3 Markus Keller CLA 2005-12-14 05:26:00 EST
I don't see why moving a class should ever have to change the visibility of these private methods. Michael, could you please post a small example where this occurs and tell exactly which refactoring you applied?

Note: there are no static local classes in Java. Either a class is local (declared inside a method, always non-static), or it is static (either top-level or nested), but not both.

I filed bug 120836 for Rename Method, Change Method Signature, and Introduce
Parameter. Tobias, please also check Pull Up and Push Down.
Comment 4 Michael Moser CLA 2005-12-14 08:17:37 EST
Please find an example below.

To demonstrate the error:
compile and run A - you should see:
--------------
before: foo
after: foo
--------------

Then select class B and do a Refactor => Move Member Type to New File.

run A again - now you will see:
--------------
before: foo
after: null  <<<<<<<<<<<<<<
--------------

<<<<<<<<<<<  shows, that the non-private writeObject/readObject methods have not been called!

Note, that to demonstrate the effect the class to be serialized has to have one or more transient fields! The default (de-)serialization methods (which are called since the non-private methods are ignored) handle non-transient fields properly and then one would not even notice, that the refactored writeObject/readObject methods are not called any more...

/**
 * @author Michael Moser (mmo@zurich.ibm.com)
 * @since Dec. 15, 2005
 */

package test;

import java.io.*;

public class A
{		
	public static void main(String[] args) throws Exception {
		B b = new B("foo");
		System.out.println("before: "  + b);
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		ObjectOutputStream os = new ObjectOutputStream(baos);
		os.writeObject(b);
		os.close();
		ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
		ObjectInputStream is = new ObjectInputStream(bais);
		b = (B)is.readObject();
		System.out.println("after: "  + b);
	}
	
	static class B implements Serializable
	{
		final static long serialVersionUID = 1L;
		transient String str; // note: the transient is important to show the effect!
		
		B(String str) {
			this.str = str;
		}
		
		private void writeObject(final ObjectOutputStream out) 
		throws IOException {
			out.writeObject(this.str);
		}
	
		private void readObject(final ObjectInputStream in) 
			throws IOException {
			try {
				this.str = (String)in.readObject();
			} catch (ClassNotFoundException ex) {
				ex.printStackTrace();
				throw new IOException("exception: " + ex);
			}
		}

		public String toString() {
			return this.str;
		}
	}
}

Hope that helps!

Michael

Comment 5 Michael Moser CLA 2005-12-14 08:20:05 EST
> ... Note: there are no static local classes in Java...

You are absolutely right! Pardon my sloppy use of terms. 

Michael
Comment 6 Markus Keller CLA 2005-12-14 11:17:24 EST
> Pardon my sloppy use of terms. 
No problem, I just wanted to make sure we're talking about the same thing ;-)

Now, this looks like a dup of bug 97411.
Comment 7 Tobias Widmer CLA 2005-12-14 11:24:23 EST
Filed bug 120895 for pull up and push down

*** This bug has been marked as a duplicate of 97411 ***