JDK-4903007 : XML serialization of javax.swing.Box fails
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.beans
  • Affected Version: 1.4.1,1.4.2
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: linux,windows_xp
  • CPU: x86
  • Submitted: 2003-08-07
  • Updated: 2003-09-26
  • Resolved: 2003-09-26
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
5.0 tigerFixed
Related Reports
Relates :  
Description

Name: jk109818			Date: 08/07/2003


FULL PRODUCT VERSION :
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_03-b02)
Java HotSpot(TM) Client VM (build 1.4.1_03-b02, mixed mode)


FULL OS VERSION :
Linux checkmate 2.4.18-19.8.0 #1 Thu Dec 12 05:39:29 EST 2002 i686 i686 i386 GNU/Linux
(bug appears on many other 1.4.1 JDK versions)

A DESCRIPTION OF THE PROBLEM :
Serializing a JFrame constructed from Boxes (using the Swing Box class) with the java.beans XML serialization does not work properly.  Serialization works if the JFrame contains panels, but not if the JFrame contains boxes.  When the JFrame containing a box is written, InstantiationExceptions are thrown

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Use the attached code

Run
  java SimpleFrame

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
A properly serialized Frame object in the file SimpleFrame.xml
ACTUAL -
Frame not saved properly.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
% java SimpleFrame

java.lang.InstantiationException: javax.swing.Box
Continuing ...
java.lang.Exception: discarding statement JPanel0.add(Box0);
Continuing ...
java.lang.InstantiationException: javax.swing.Box
Continuing ...
java.lang.Exception: discarding statement JRootPane$10.addLayoutComponent(Box0, "Center");
Continuing ...


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import javax.swing.*;
import java.awt.event.ActionListener;
import java.beans.*;
import java.io.*;

public class SimpleFrame {
  public void doOne() {
    System.out.println("One button pushed");
  }

  public void doTwo() {
    System.out.println("Two button pushed");
  }

  public JFrame makeUI() {
    JFrame f;
    JButton b1, b2;
    JLabel l3;
    Box vBox;

    f = new JFrame("SimpleFrame");
    f.setSize(400, 300);
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    vBox = Box.createVerticalBox();
    b1 = new JButton("One");
    b1.addActionListener((ActionListener)
        EventHandler.create(ActionListener.class, this, "doOne"));
    b2 = new JButton("Two");
    b2.addActionListener((ActionListener)
        EventHandler.create(ActionListener.class, this, "doTwo"));
    l3 = new JLabel("Separator");
    vBox.add(b1); vBox.add(l3); vBox.add(b2);
    f.getContentPane().add(vBox);
    f.setVisible(true);
    return f;
  }

  public static void main(String args[]) {
    SimpleFrame me = new SimpleFrame();
        try {
            XMLEncoder e = new XMLEncoder(new BufferedOutputStream (
                new FileOutputStream("SimpleFrame.xml")));
            e.writeObject(me.makeUI());
            e.close();
        } catch (IOException e) { e.printStackTrace(); }
  }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Don't use Boxes.  Not a good workaround.
(Incident Review ID: 193799) 
======================================================================

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: tiger FIXED IN: tiger INTEGRATED IN: tiger tiger-b22
14-06-2004

WORK AROUND The javax.swing.Box and javax.swing.Box$Filler require persistence delegates. Fox Box, it's a little complex since the PD must look at the "axis" private field: public static class BoxPersistenceDelegate extends PersistenceDelegate { protected Expression instantiate(Object oldInstance, Encoder out) { BoxLayout lm = (BoxLayout)((Box)oldInstance).getLayout(); Object[] args = new Object[1]; // get the value of the "axis" private field in BoxLayout // NOTE: This will not work in a sandboxed deployment try { Field f = lm.getClass().getDeclaredField("axis"); f.setAccessible(true); args[0] = f.get(lm); } catch (Exception e) { out.getExceptionListener().exceptionThrown(e); } return new Expression(oldInstance, oldInstance.getClass(), "new", args); } } Associate the BoxPersistenceDelegate Box.class in the XMLEncoder: e.setPersistenceDelegate(javax.swing.Box.class, new BoxPersistenceDelegate()); The Box$Filler persistence delegate is a little easier. It just needs to know the context of the arguents on the constructor: e.setPersistenceDelegate(javax.swing.Box$Filler.class, new DefaultPersistenceDelegate(new String[]{"minimumSize", "preferredSize", "maximumSize"}); For details about writing persistence delegates, see http://java.sun.com/products/jfc/tsc/articles/persistence4/#instantiate ###@###.### 2003-09-03
03-09-2003

EVALUATION javax.swing.Box doesn't have a no arg constructor so it's not a JavaBean. When the XMLEncoder attempts to instantiate a Box, it gets an InstantiationException: java.lang.InstantiationException: javax.swing.Box at java.lang.Class.newInstance0(Class.java:293) at java.lang.Class.newInstance(Class.java:261) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:324) at java.beans.Statement.invoke(Statement.java:434) javax.swing.Box doesn't have a no-arg constructor so it can't be instantiated by the JavaBeans mechanisms. A Box is just a component which has a Box layout. Unfortunately, BoxLayout doesn't have any public methods to retrieve the values of axis and target but the DefaultPersistenceDelegate in MetaData will use reflection to get the values of these private fields. There are two solutions: 1. A persistence delegate for Box must be written so that it reflectively gets the value of the axis field in the BoxLayout. This value could be passed to the constructor. 2. javax.swing.Box and BoxLayout should be fixed so that they have no-arg constructors and getter methods. This will turn these classes into JavaBeans and the persistence delegates will be unneccessary. ###@###.### 2003-08-13
13-08-2003

SUGGESTED FIX public static class BoxPersistenceDelegate extends PersistenceDelegate { protected Expression instantiate(Object oldInstance, Encoder out) { BoxLayout lm = (BoxLayout)((Box)oldInstance).getLayout(); Object[] args = new Object[1]; // get the value of the "axis" private field in BoxLayout // NOTE: This will not work in a sandboxed deployment try { Field f = lm.getClass().getDeclaredField("axis"); f.setAccessible(true); args[0] = f.get(lm); } catch (Exception e) { out.getExceptionListener().exceptionThrown(e); } return new Expression(oldInstance, oldInstance.getClass(), "new", args); } } ###@###.### 2003-08-13
13-08-2003