1

Can anyone explain why the following program does not terminate when closing the child window?

I have created a simple application with a parent JFrame that instantiates a child class with a JDialog. When I pass the parent reference into the child class so that the JDialog can be created with a parent (ie: new JDialog(parent);), the window listener that is added to it from the parent class causes the program to never terminate once the child window has been closed. The windows do indeed close (in terms of visibility), but the program itself still runs. The default close operation for the parent is set to JFrame.EXIT_ON_CLOSE. The default close operation for the child is set to JDialog.DISPOSE_ON_CLOSE.

If I pass no parent reference to the child class so that the JDialog is instantiated without a parent window, the example does terminate when the child window is closed. For instance, the program terminates if the following is used: ChildWindow prompt = new ChildWindow(null);

Parent Class:

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

public class ParentWindow {
    private JFrame frame;
    private JPanel contentPane = new JPanel(new BorderLayout(0, 0));
    private JButton btnNewButton = new JButton("Open Child Window!");

    public ParentWindow() {
        frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setContentPane(contentPane);

        btnNewButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                ChildWindow prompt = new ChildWindow(frame); // or use null for no parent

                prompt.getDialog().addWindowListener(new WindowAdapter() {
                    @Override
                    public void windowClosed(WindowEvent e){
                        // .. get some information from the child before disposing 
                        System.out.println("Window closed."); // does not terminate when passing frame as parent
                        frame.dispose();
                    }
                });
            }
        });

        contentPane.add(btnNewButton);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    new ParentWindow();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

Child Class:

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

public class ChildWindow {
    private JDialog dialog;
    private JButton okButton = new JButton("Close");

    public ChildWindow(Window parent) {
        dialog = new JDialog(parent);
        dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);

        dialog.getContentPane().setLayout(new BorderLayout());
        dialog.getContentPane().add(okButton, BorderLayout.CENTER);
        okButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                dialog.dispose();
            }
        });

        dialog.pack();
        dialog.setVisible(true);
    }

    public JDialog getDialog() {
        return dialog;
    }

}

Output:

Window closed.
Window closed.
Window closed.
... (does not terminate)
mKorbel
  • 109,525
  • 20
  • 134
  • 319
David Yee
  • 3,515
  • 25
  • 45
  • Works just fine for me... – MadProgrammer Jun 10 '14 at 03:38
  • @MadProgrammer Did you try closing the child window? – David Yee Jun 10 '14 at 03:50
  • Yep, opened 3, closed the first...waited a second or two and the JVM terminated... – MadProgrammer Jun 10 '14 at 03:51
  • @MadProgrammer When I run it the program definitely terminates when the parent window is closed but if I close any child window it gets stuck and does not terminate (using either the child window's close button or the `JButton` close button). – David Yee Jun 10 '14 at 03:56
  • What OS, what Java version? – MadProgrammer Jun 10 '14 at 03:57
  • Windows 8.1 (x64) and initially tried on JDK1.7u55 (x64). I get the same results on JDK1.7u60 x64 and x86. – David Yee Jun 10 '14 at 03:58
  • Well, I can tell you works fine on Windows 7 and Java 8...buuuutttt...not with Java 7.... – MadProgrammer Jun 10 '14 at 04:00
  • It doesn't work for me either, even when I use null as the parent. I use Windows 7, but am confused about my Java version. I thought I just updated 3 days ago to the current version. When I go to the "Control Panel" and click on the "Java 32 bit" Icon and then click the "About" button on the "General" tab it says I am using "Java Version 7 - Update 60" which makes sense. But when I run Java from the command line and use "java -version" it still says I'm using 'java version "1.7.0_10"'. So I'm confused? How do you know you are running "JDK1.7u55 (x64)."? What command to you use for that info? – camickr Jun 10 '14 at 04:01
  • I have my IDE setup that way ;) – MadProgrammer Jun 10 '14 at 04:03
  • @MadProgrammer You're right, it does work in Java 8 (JDK1.8.0u05)! – David Yee Jun 10 '14 at 04:04
  • @camickr I am using Eclipse and it says in the Project's Build Path and directly above the Console window which JDK I'm using. – David Yee Jun 10 '14 at 04:05
  • @CanadianDavid, thanks. I guess I'll have to learn how to use an IDE one of these days. – camickr Jun 10 '14 at 04:17

1 Answers1

3

Tested using Windows 7 and Java 8 and had not issues, when I tried Java 7 or Java 6, it simply kept on printing out Window Closed....

So, I updated the windowClosed method using a test for frame.isVisible

btnNewButton.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        ChildWindow prompt = new ChildWindow(frame); // or use null for no parent

        prompt.getDialog().addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosed(WindowEvent e) {
                // .. get some information from the child before disposing 
                System.out.println("Window closed."); // does not terminate when passing frame as parent
                if (frame.isVisible()) {
                    frame.dispose();
                }
            }
        });
    }
});

And got it to work...now to see if I can find out why...

I "think" what's happening is this...

  1. okButton is triggered, dialog.dispose is called
  2. dialog.dispose is setting up the first windowClosed event
  3. The WindowListener is been notified of the windowClosed event, which is disposing of the frame...
  4. The frame is trying to close it's child windows, which is causing the dialogs to notify the WindowListener AGAIN that they are closing and so forth...goto 3 and repeat...

This is a bug, which appears to be fixed in Java 8...Because the closing events are been pushed onto the Event Queue, you won't get a StackOverflowException...

In this case, another option would be to remove the WindowListener from the Window that triggered the windowClosed event...

btnNewButton.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        ChildWindow prompt = new ChildWindow(frame); // or use null for no parent

        prompt.getDialog().addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosed(WindowEvent e) {
                e.getWindow().removeWindowListener(this);
                frame.dispose();
            }
        });
    }
});
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • Works here too, except in my environment with JDK1.7u60 it prints it out twice. With JDK1.8u5 it works as expected and prints only once. – David Yee Jun 10 '14 at 04:11
  • Interesting find, thanks. Since it's a modeless dialog that is created, is there a better way to get information (externally from another class) from the dialog when it has closed? – David Yee Jun 10 '14 at 05:02
  • issue is that last turn of the light (JVM) – mKorbel Jun 10 '14 at 06:45
  • 1
    @CanadianDavid [those two threads](http://stackoverflow.com/questions/10880326/jpanel-which-one-of-listeners-is-proper-for-visibility-is-changed) would be [interesting...](http://stackoverflow.com/questions/6309407/remove-top-level-container-on-runtime) – mKorbel Jun 10 '14 at 06:49