Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4950972

LTP: Long-term persistence bugs (swing, Encoder, XMLEncoder)

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: P4 P4
    • None
    • 1.4.2
    • client-libs
    • Cause Known
    • x86
    • windows_2000

      Name: gm110360 Date: 11/07/2003


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


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

      A DESCRIPTION OF THE PROBLEM :
      I like the idea of the new long-term persistence, but I've
      found the implementation to have some bugs.For example:

      1. JInternalFrame does not persist properly (the bounds and
      visibility are not set).
      2. BorderLayout doesn't persist properly without a center
      component (last component is always placed in the center).
      3. JPopupMenu.add(Action) doesn't persist properly
      (exception raised) (workaround: popup.add(new JMenuItem
      (Action)) does work.)
      4. Inner classes can't persist (no public constructor).
      (Yet inner classes are Sun's argument against c# delegates.
      The new EventHandler is a poor approximation to delegates
      with no compile-time checks.)

      For the BorderLayout, the problem is that this:

        c.add(comp, BorderLayout.EAST)

      is not quite the same as:

        c.add(comp);
        c.getLayout().addLayoutComponent(BorderLayout.EAST, be);

      The latter is what the XMLEncoder creates for the former.
      The latter is equivalent to:

        c.add(comp, BorderLayout.CENTER);
        c.getLayout().addLayoutComponent(BorderLayout.EAST, be);

      The problem is that the same component is both the CENTER
      and the EAST component in the BorderLayout! BorderLayout
      assigns bounds to the EAST first and then to the CENTER;
      our poor component gets its bounds set twice and lands in
      the center, with space taken in the east for a phantom
      version.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. Use the XMLEncoder to write a JFrame with a JDesktopPane
      and a JInternalFrame. Use XMLDecoder to reload it. The
      JInternal will not be visible because its bounds are not
      set and setVisible(true) is not called.
      2. Use the XMLEncoder to write a component with
      BorderLayout and one child in the NORTH (any of N,S,E,W
      will do). No child should be in the center. Use the
      XMLDecoder to reload it. The component will be placed in
      the center (and a phantom will be in the North).

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      The XMLEncoder is not writing the bounds and visibility of
      a JInternalFrame. The frame should appear in the same
      place it was before saving.

      The XMLEncoder does not write save BorderLayouts properly.
      The component should live in the proper location.

      ERROR MESSAGES/STACK TRACES THAT OCCUR :
      c:\Documents and Settings\kbeyer\My Documents\src\dax\test>java BadEncoder
      java BadEncoder

      writing

      java.lang.InstantiationException: javax.swing.JPopupMenu$2
      Continuing ...
      java.lang.Exception: discarding statement JPopupMenu0.add(JPopupMenu$20);
      Continuing ...



      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      BadEncoder.java:
      ----------------

      import java.awt.*;
      import java.awt.image.*;
      import javax.swing.*;
      import java.awt.event.*;
      import java.beans.*;
      import java.io.*;
      import dax.awt.*;

      // MY OWN PersistenceDelegate for a JInternalFrame

      class javax_swing_JInternalFrame_PersistenceDelegate extends
      DefaultPersistenceDelegate {
          protected void initialize(Class type, Object oldInstance, Object
      newInstance, Encoder out)
          {
              super.initialize(type, oldInstance, newInstance, out);
              JInternalFrame oldC = (JInternalFrame)oldInstance;
              JInternalFrame newC = (JInternalFrame)newInstance;

              // bounds
              Rectangle oldB = oldC.getBounds();
              Rectangle newB = newC.getBounds();
              if( ! oldB.equals(newB) ) {
                  out.writeStatement(new Statement(oldInstance, "setBounds", new
      Object[]{oldB}));
              }

              // visible
              boolean oldV = oldC.isVisible();
              boolean newV = newC.isVisible();
              if (newV != oldV) {
                  out.writeStatement(new Statement(oldInstance, "setVisible", new
      Object[]{Boolean.valueOf(oldV)}));
              }

          }
      }

      public class BadEncoder
      {
          private static boolean fixPopup = false;
          private static boolean fixJInternalFrame = false;
          private static boolean fixBorderLayout = false;

          public static void decode() throws IOException
          {
              XMLDecoder in = new XMLDecoder(
                  new BufferedInputStream(new FileInputStream("test.xml")));
              in.readObject();
              in.close();
          }

          public static void encode() throws Exception
          {
              JFrame topFrame = new JFrame();
              topFrame.setBounds(0,0,500,500);
              JDesktopPane desktop = new JDesktopPane();
              topFrame.getContentPane().add(desktop);
              JInternalFrame frame = new JInternalFrame("my
      frame",true,true,true,true);
              frame.setVisible(true);
              frame.setBounds(10,10,100,100);
              JComponent c = (JComponent) frame.getContentPane();
              c.add(new JButton("button"), BorderLayout.NORTH);
              if( fixBorderLayout ) {
                  c.add(new JPanel(), BorderLayout.CENTER);
              }
              desktop.add(frame);

              JPopupMenu popup = new JPopupMenu();
              //this does not persist properly (exception due to JPopupMenu inner
      class):
              if( !fixPopup ) {
                  popup.add(new ExitAction());
              } else {
                  popup.add(new JMenuItem(new ExitAction()));
              }

              // try{ popup.add(new SimpleAction("Exit",
      SimpleAction.class, "doExit")); } catch(Exception ex) {}

              //Add listener to components that can bring up popup menus.
              MouseListener popupListener = new PopupListener(popup);
              desktop.addMouseListener(popupListener);

              topFrame.setVisible(true);

              Thread.sleep(2000);
              System.err.println("\nwriting\n");
              XMLEncoder e = new XMLEncoder(
                  new BufferedOutputStream(new FileOutputStream("test.xml")));
              if( fixJInternalFrame ) {
                  e.setPersistenceDelegate(JInternalFrame.class,
                                           new
      javax_swing_JInternalFrame_PersistenceDelegate());
              }
              e.writeObject(topFrame);
              e.close();

          }

          public static void main(String args[]) throws Exception
          {
              boolean doDecode = false;

              for(int i = 0 ; i < args.length ; i++) {
                  if( "-decode".equals(args[i]) ) {
                      doDecode = true;
                  }
                  else if( "-fixPopup".equals(args[i]) ) {
                      fixPopup = true;
                  }
                  else if( "-fixJInternalFrame".equals(args[i]) ) {
                      fixJInternalFrame = true;
                  }
                  else if( "-fixBorderLayout".equals(args[i]) ) {
                      fixBorderLayout = true;
                  }
                  else {
                      System.err.println("unknown argument: "+args[i]);
                  }
              }

              if( doDecode ) {
                  decode();
              } else {
                  encode();
                  System.exit(0);
              }
          }
      }


      PopupListener.java
      ------------------

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

      // NOTE THE EXTRA FILES FOR THE MOUSEADAPTER AND THE ACTION!
      // THESE ARE NEEDED FOR PERSISTENCE BEFORE THEY WERE INNER CLASSES.
      // (EventHandler might be a usable substitute.)
      public class PopupListener extends MouseAdapter
      {
          JPopupMenu popup;

          public PopupListener()
          {
          }
          public PopupListener(JPopupMenu popup)
          {
              this.popup = popup;
          }

          public JPopupMenu getPopup() { return popup; }
          public void setPopup(JPopupMenu popup) { this.popup = popup; }

          public void mousePressed(MouseEvent e)
          {
              maybeShowPopup(e);
          }

          public void mouseReleased(MouseEvent e)
          {
              maybeShowPopup(e);
          }

          private void maybeShowPopup(MouseEvent e)
          {
              if (e.isPopupTrigger()) {
                  popup.show(e.getComponent(),
                             e.getX(), e.getY());
              }
          }
      }


      ExitAction.java
      ---------------

      import javax.swing.AbstractAction;
      import java.awt.event.ActionEvent;

      public class ExitAction extends AbstractAction
      {
          public ExitAction()
          {
              super("Exit");
          }
          
          public void actionPerformed(ActionEvent e)
          {
              System.exit(0);
          }
      }


        To run:

      javac BadEncoder.java PopupListener.java ExitAction.java

      java BadEncoder
      java BadEncoder -decode

      java BadEncoder -fixPopup
      java BadEncoder -decode

      java BadEncoder -fixPopup -fixJInternalFrame
      java BadEncoder -decode

      java BadEncoder -fixPopup -fixJInternalFrame
      java BadEncoder -decode

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

      CUSTOMER WORKAROUND :
      workarounds shown in source code
      (Incident Review ID: 179580)
      ======================================================================

            malenkov Sergey Malenkov (Inactive)
            gmanwanisunw Girish Manwani (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Imported:
              Indexed: