Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 4085659
Votes 5
Synopsis need to document serialization of AWT listeners
Category java:classes_awt
Reported Against 1.1 , 1.2 , 1.1.3
Release Fixed 1.4(merlin-beta)
State 10-Fix Delivered, bug
Priority: 4-Low
Related Bugs 4072419 , 4085359
Submit Date 12-OCT-1997
Description
AWT plays some games during serialization in the way it handles event
listeners.  It only saves those event listeners that are serializable
and ignores the others.

We need to document this behaviour as part of the java.awt.Component class.
It is a fairly important part of the class's serialization behaviour,
but isn't obvious from either the docs or the code and tends to surprise
people.  See also the comments section.

KGH 10/12/97
Work Around
N/A
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
Comments
  
  Include a link with my name & email   


PLEASE NOTE: JDK6 is formerly known as Project Mustang