JDK-4631471 : serialization problem of JTree using XML long term persistence
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.beans
  • Affected Version: 1.4.0
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_2000
  • CPU: x86
  • Submitted: 2002-01-31
  • 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: 01/31/2002


FULL PRODUCT VERSION :
java version "1.4.0-rc"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.0-rc-b91)
Java HotSpot(TM) Client VM (build 1.4.0-rc-b91, mixed mode)

FULL OPERATING SYSTEM VERSION :
Microsoft Windows 2000 [Version 5.00.2195]




A DESCRIPTION OF THE PROBLEM :
when javax.swing.JTree is serialized using XML's long term
persistence, it is not deserialized properly. On
deserialization we get tree in it's default state.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
run testcase TCJTree.java provided in Source code column

EXPECTED VERSUS ACTUAL BEHAVIOR :
Tree should be deserialize in the state in which we
serialize it, but we get only default tree every time we
deserialize it.

This bug can be reproduced always.

---------- BEGIN SOURCE ----------

import javax.swing.*;
import java.beans.*;
import javax.swing.tree.*;
import java.awt.event.*;
import java.awt.*;


public class TCJTree {
    private JTree tree;
    private JFrame frame;
    private JScrollPane scrollpane;
    private JButton btn_save, btn_retrieve;
	private String filePath;

    public TCJTree() {
		filePath = "c:"+System.getProperties().get("file.separator")
+"tree.xml";

        scrollpane = new JScrollPane();
        frame = new JFrame();
		frame.setDefaultCloseOperation(frame.DISPOSE_ON_CLOSE);
        JPanel panel = new JPanel();
        btn_save = new JButton("Save");
        btn_save.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent evt){
                save();
            }
        });
        btn_retrieve = new JButton("Retrieve");
        btn_retrieve.setEnabled(false);
        btn_retrieve.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent evt){
                retrieve();
            }
        });
        panel.add(btn_save);
        panel.add(btn_retrieve);
        frame.getContentPane().add(panel, BorderLayout.SOUTH);
        createTree();
        scrollpane.setViewportView(tree);
        frame.getContentPane().add(scrollpane);
        frame.setVisible(true);
        frame.setSize(new Dimension(400,350));
    }

    private void createTree(){
        DefaultMutableTreeNode node = new DefaultMutableTreeNode("root");
        node.add(new DefaultMutableTreeNode("first"));
        node.add(new DefaultMutableTreeNode("second"));
        tree = new JTree(node);
    }

    public static void main(String str[]){
        new TCJTree();
    }

    private void save(){
        try{
            XMLEncoder encoder = new XMLEncoder(new java.io.FileOutputStream
(filePath));
            encoder.writeObject(scrollpane.getViewport().getView());
            encoder.flush();
            encoder.close();
            scrollpane.setViewportView(null);
            frame.invalidate();
            frame.validate();
            frame.repaint();
            btn_retrieve.setEnabled(true);
            btn_save.setEnabled(false);
        }catch(Exception e){e.printStackTrace();}
    }

    private Object retrieve(){
        Object obj = null;
        try{
            XMLDecoder decoder = new XMLDecoder(new java.io.FileInputStream
(filePath));
            obj = decoder.readObject();
            decoder.close();
            tree = (JTree)obj;
            scrollpane.setViewportView(tree);
            frame.invalidate();
            frame.validate();
            frame.repaint();
            btn_retrieve.setEnabled(false);
            btn_save.setEnabled(true);
        }catch(Exception e){e.printStackTrace();}
        return obj;
    }
 


}

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

CUSTOMER WORKAROUND :
constructor of DefaultTreeModel should be register in
java.beans.MetaData.java class, so that xml encoder can
make its new instance while serialization, and also
delegate for TreeModel should be provided in order to save
root node
(Review ID: 138903) 
======================================================================

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

WORK AROUND Use the DefaultTreeModelPD in the suggested fix field. Set the persistence delegate for the DefaultTreeModel class on an instance of the XMLEncoder: encoder.setPersistenceDelegate(javax.swing.tree.DefaultTreeModel.class, new DefaultTreeModelPD()); ###@###.### 2003-01-29
29-01-2003

SUGGESTED FIX Must write a persistence delegate for the DeafultTreeModel. It would look something like this: class DefaultTreeModelPD extends DefaultPersistenceDelegate { protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) { super.initialize(type, oldInstance, newInstance, out); DefaultTreeModel model = (DefaultTreeModel)oldInstance; // This should be recursive TreeNode node = (TreeNode)model.getRoot(); if (node != null) { for (int i = 0; i < model.getChildCount(node); i++) { out.writeStatement(new Statement(oldInstance, "getChild", new Object[] { node, new Integer(i) })); } } } } Note: This solution hasn't been fully tested yet. This may not work for deeply nested trees. In that case a recursive function to encode the children may be required or this may already be solved by the DefaultMutableTreeNode internal persistence delegate. ###@###.### 2003-01-29
29-01-2003

EVALUATION I'm not sure why the DefaultTreeModel registered constructor has been commented out in MetaData. Perhaps a beter solution would be to add a default constructor to DefaultTreeModel. This should be fixed. ###@###.### 2002-02-06 The problem is that the default model is not the same as the model that gets created. In MetaData, register constructor method for the DefaultTreeModel has been commented out. Another part of the problem is that the DefaultTreeModel isn't really beans like. For example, the seRoot(TreeNode) is not equivalent to Object getRoot(). Perhaps if TreeNode getRoot() is added then it will correctly persist. One possible solution is to write a persistence delegate for the DefaultTreeModel. All the data is in the TreeNodes. A persistence delegate for a DefaultMutableTreeNode already exists so there should be some way of extracting the nodes from the DefaultTreeModel. Note: the solution should be generalized for TreeModel and TreeNode. ###@###.### 2003-01-29
29-01-2003