1

I am writing a program to collect information from two text files to add to tables in a database. In order to permit the user to select his own files I created a non-static method called chooseFile() that uses JFileChooser class to present a showOpenDialog (I've also tried it as a static method with the same results. If this sounds like I'm guessing, you're correct - I'm only so-so with programming).

My understanding is that calls to Swing classes in main() should be done using invokelater. This all worked fine (the JVM exits successfully) for one call to chooseFile(), but when I added a second call to chooseFile() the JVM keeps running indefinitely. However, the program exits normally with both calls to chooseFile() if I do so without using invokelater. Adding System.exit(0) after the second call in invokelater also allows the program to exit normally.

From what I have been able to gather, the program won't exit until all the (non-daemon) threads are closed. However, I thought that the purpose for using invokelater was to push all non-thread safe Swing related activities onto the EDT. Is using System.exit(0) the best practice here or is there something else I should know about?

Here is my SSCCE:

package ptMngr;

import java.io.IOException;
import java.nio.file.Path;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileNameExtensionFilter;

public class ParticipantManager {

    private Path chooseFile() throws IOException{
        JFileChooser chooser = new JFileChooser();
        FileNameExtensionFilter filter = new FileNameExtensionFilter("Text Files", "txt","csv");
        chooser.setFileFilter(filter);
        int returnVal = chooser.showOpenDialog(null);
        if(returnVal == JFileChooser.APPROVE_OPTION) {
            System.out.println("You chose to open this file: " +
                chooser.getSelectedFile().getName());
        }
        Path path = chooser.getSelectedFile().toPath();
        return path;
    }
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args){
    //        ParticipantManager pm =  new ParticipantManager();
    //        try {
    //            pm.chooseFile();
    //            pm.chooseFile();
    //
    //        } catch (IOException ex) {
    //            Logger.getLogger(ParticipantManager.class.getName()).log(Level.SEVERE, null, ex);
    //        }

        java.awt.EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {

                ParticipantManager pm =  new ParticipantManager();
                try {

                    pm.chooseFile();
                    pm.chooseFile();
                    System.exit(0);
                } catch (IOException ex) {
                    Logger.getLogger(ParticipantManager.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        });
    }
}
Ram
  • 3,092
  • 10
  • 40
  • 56
John Jones
  • 57
  • 4
  • 2
    @Ram Thank you for fixing the hanging bracket. – John Jones Nov 19 '15 at 11:22
  • 1
    @Andrew I call the method the second time because I want the user to be able to open two separate files. I presume that for general application programming something like JFileChooser() would be designed to be called whenever a user wants to open/save a file and that any additional thread (if that is the correct term) created by the class would be handled. Hence, my question about System.exit(0). – John Jones Nov 19 '15 at 11:30
  • *"I call the method the second time because I want the user to be able to open two separate files"* Thanks for clearing that up. Note that if the two files are in the same directory, it is easy to offer the user a single file chooser that allows selecting more than one file at a time (multi file selection). Of course, that strategy might not fit for this user requirement (only you can tell that for sure). – Andrew Thompson Nov 19 '15 at 11:53
  • 'Design' is not my strong point; I'm piecing it together as I go. The functionality of this program will be only to open these two files and create a DB with two tables. I have another program that prompts the user for an ID number and then queries the DB. I've been avoiding `JFrame` and using `JOptionPane`s so I don't have to bother with layout. One downside to this approach, however, is that there is no cue to the user that the program is still running, so I may need a `JFrame` anyways which should obviate the problem. But I think the question still stands. – John Jones Nov 19 '15 at 12:22

1 Answers1

1

In your call to showOpenDialog(null) you pass null as the parent component for the JFileChooser. It's been some time since I worked with JFileChooser, but I seem to remember, that a "magic" JFrame may be constructed, when null is passed in. It may be possible, that in this use case two "magic" JFrames are created and they prevent termination.

You may want to try to create a JFrame which serves as parent for both JFileChoosers so it does not create the automatic frames.

EDIT:

My first take would be this:

public static class ParticipantManager {
    JFrame frame;

    private JFrame getFrame() {
        if (frame==null) {
            frame = new JFrame();
            frame.setSize(600, 400);
            frame.setLocationRelativeTo(null);
            // frame.setVisible(true); // <---- worked for me without this call too
        }
        return frame;
    }

    private void close() {
        frame.dispose();
    }

    private Path chooseFile() throws IOException{
        JFrame f = getFrame();
        ...
        int returnVal = chooser.showOpenDialog(f);
    ...

    ...
                pm.chooseFile();
                pm.chooseFile();
                pm.close(); // <----- close the frame
    ...
Rainer Schwarze
  • 4,725
  • 1
  • 27
  • 49
  • I've come across suggestions like this as answers to related questions. I've tried it two different ways. Prior to posting this question I tried making the class extend JComponent and called `chooser.showOpenDialog(ParticipantManager.this)` which compiles but doesn't solve the issue. Based on your post I created a JFrame in the invokelater block and passed it with `pm.chooseFile(frame)` and then used that frame as the parent with the same results. Am I interpreting your suggestions correctly? `System.exit(0)` would seem to be a simpler solution. – John Jones Nov 19 '15 at 12:00
  • I adjusted my answer. I would create a JFrame, use that and dispose it when the file choosers are not needed any more. In my test it worked without making the frame visible (on Mac OS X El Capitan). – Rainer Schwarze Nov 19 '15 at 12:39
  • Or just call `JOptionPane.getRootFrame().dispose();` when you are done with `JFileChooser`s, because they internally use that returned frame too. – gthanop Dec 20 '21 at 10:12