|
Evaluation
|
xxxxx@xxxxx 1998-01-08
Here's an explanation of how and why AWT listeners are serialized from
the horses, uh, mouth. A short version should be plugged into the javadoc for java.awt.Component.
Date: Wed, 22 Jan 1997 14:13:21 -0800
Subject: request for a change to the AWT implementation
This is a proposal to make a change to the AWT implementation
that affects how GUI components are serialized. It doesn't change
the public programmers API however it does change the format
of serialized AWT objects.
---
I've made a systematic change to the way JDK1.1 AWT does serialization
to correct bug #4027305, and to provide more carefully thought out and
rational behaviour for AWT serialization in general. I've also provided
conformance tests that cover this, see:
file:/home/java_sqe/workspaces/jck11-tests-new/tests/api/java_awt_new/serialization/index.html
If we do not integrate these changes, customers will not be able to use
serialization to store Java Beans (in general) and it's unlikely that
AWT implementations from other vendors will correctly interoperate with
ours. Without the changes and the conformance tests licensees will not
be required to make their support for persistence interoperate with ours.
Currently, storing more than one event listener on an AWT component will
render the component not serializable. This is because the
AWTEventMulticaster class used to represent lists of (more than one)
listener is not serializable (see bug #4027305). There's a more general
problem: listener objects tend to contain references to objects
throughout an application, so serializing a subset of a graph of AWT
objects often has the unintended consequence of dragging along the
entire graph and more. A similar problem was encountered when
serialization was first supported in the AWT several months ago: the
persistent Component parent property made it impossible to serialize
just a subset of an AWT graph. We solved the problem by making the
parent field transient, and reconstructing it, if neccessary, at
readObject() time. A similar change is needed for event listeners:
listener fields should be transient. However serilizable listener
objects should be restored at readObject() time. For example:
/* Serialization support.
*/
private int buttonSerializedDataVersion = 1;
private void writeObject(ObjectOutputStream s)
throws IOException
{
s.defaultWriteObject();
// Save the listeners that implement serializable.
AWTEventMulticaster.save(s, actionListenerK, actionListener);
s.writeObject(null);
}
private void readObject(ObjectInputStream s)
throws ClassNotFoundException, IOException
{
s.defaultReadObject();
// Restore the listeners we saved in ReadObject
Object keyOrNull;
while(null != (keyOrNull = s.readObject())) {
String key = ((String)keyOrNull).intern();
if (actionListenerK == key)
addActionListener((ActionListener)(s.readObject()));
else // skip value for unrecognized key
s.readObject();
}
}
Serializable listeners are stored (by AWTEventMulticaster.save()) in
tuples like this: "actionL" xxxxx@xxxxx . Note that
AWTEventMulticaster.save() does NOT store listeners that aren't marked
serializable. The string key identifies the list that the (serializable)
listener belongs to, the value is the listener object itself. I use
interned strings for the keys because the serialization protocol knows
about them (well, it knows about String objects anyway). The package
private fooListenerK String constants are in Component.java.
The following files contain new readObject/writeObject methods
like the one above:
Button.java
Checkbox.java
CheckboxMenuItem
Choice.java
Component.java
Container.java
Dialog.java
Frame.java
List.java
MenuItem.java
Scrollbar.java
TextComponent.java
TextField.java
The changes I've made have several implications for developers:
- Saving graphs of AWT objects will now work, even if an object
has more than one listener.
- If an AWT object has listeners that are marked serializable, they will
be saved and restored automatically. For example, beans interconnected
with and saved to a file with the BeanBox. Note that the BeanBox always
code-generates serializable listener implementations.
- If an AWT object has listeners that aren't marked serializable they
will be dropped at writeObject() time.
- Developers will need, as always, to consider the implications of
making an object serializable. One idiom to watch out for is this:
import java.awt.*;
import java.awt.event.*;
import java.io.Serializable;
class MyApp implements ActionListener, Serializable
{
BigObjectThatShouldNotBeSeralizedWithAButton bigOne;
Button aButton = new Button();
MyApp()
{
// Oops, now aButton has a listener with a reference to bigOne!
aButton.addActionListener(this);
}
public void actionPerformed(ActionEvent e)
{
System.out.println("Hello There");
}
}
In this example, serializing aButton by itself will cause MyApp and
everthing it refers to to be serialized as well. The problem
is that the listener is serializable by coincidence, not by design.
To separate the decisions about MyApp and the ActionListener being
serializable one can use a nested class, e.g.:
import java.awt.*;
import java.awt.event.*;
import java.io.Serializable;
class MyApp java.io.Serializable
{
BigObjectThatShouldNotBeSeralizedWithAButton bigOne;
Button aButton = new Button();
class MyActionListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
System.out.println("Hello There");
}
}
MyApp()
{
aButton.addActionListener(new MyActionListener());
}
}
Fixed for merlin. Worth 20 CQI points!
xxxxx@xxxxx 2000-05-10
|