1

I couldn't find a better way to express the problem in the title of the question, but hopefully you'll understand...

I have the following code...

[...]
final JDialog waitingDialog = optionPane.createDialog(optionPane, "Processing in backgroung");

waitingDialog.setLocationRelativeTo(homeFrame);
waitingDialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);


SwingWorker<Void, Void> backgroundThread = new SwingWorker<Void, Void>() {

    @Override
    protected Void doInBackground() throws Exception {
        // Background processing is done here
        System.out.println("[DEBUG] -- Background processing has started");
        MyCustomClass.initParameters();
        System.out.println("DEBUG] -- initParameters() has finished. Starting main processing now...");
        waitingDialog.setVisible(true);
        MyCustomClass.run();
        return null;
    }

    // This is called when background thread above has completed
    @Override
    protected void done() {
        waitingDialog.dispose();
    };
};
backgroundThread.execute();

[and does other things after this...]

... which executes just fine until it hits MyCustomClass.run(). When it is supposed to start such method, it simply doesn't do it. The application doesn't pause, stop or crash... it just keeps on waiting to continue after the background processing is done, which is not happening for some unknown reason.

MyCustomClass.run() is a static method over at MyCustomClass. I suspected the problem could be with its name - run - so I changed it to some random thing like "doIt()", but the issue persisted. The very first line on the method is a simple System.out.println("I got here"), but not even that gets printed. I added breakpoints just before and right after the method invocation and tried debugging, but that didn't help either. Everything seemed fine and I couldn't find out the problem.


Edit
I ask: how do I make the dialog show only when I wanted it to show? In other words, the script is:

  1. show the "waiting" dialog when SwingWorker is started
  2. hide/stop/disable/dispose the dialog when the initParameter() issues the option dialog that needs user input (press of a button);
  3. show the waiting dialog again as the processing resumed
  4. dispose of the dialog because the background processing has completed
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
Nick Fanelli
  • 265
  • 1
  • 6
  • 19

3 Answers3

5

Immediately off the bat I see that you're making Swing calls from within the SwingWorker's doInBackground, something which completely defeats the purpose of using a SwingWorker or background thread. Also, the call you're making, one that displays a modal dialog:

waitingDialog.setVisible(true); // **** here ****
MyCustomClass.run();   // **** not sure what this does or if this makes Swing calls or not.

will pause all code flow below it until the dialog is no longer visible, as that is how modal dialogs function. This is what is blocking your SwingWorker. You must try to separate your code so that Swing calls are only made on the Swing event thread, and use the worker for only the background work.

Instead

  • Create your SwingWorker
  • Execute your SwingWorker
  • Then call setVisible(true) on your modal dialog

In pseudocode:

create modal dialog
create SwingWorker
Attach any PropertyChangeListeners to the SwingWorker (if needed)
Execute the SwingWorker
Display the modal dialog

So, the first quick change I would make in your code above would be to remove waitingDialog.setVisible(true); from your SwingWorker doInBackground() method, and then call the waitingDialog.setVisible(true); method AFTER executing the SwingWorker, i.e., after backgroundThread.execute(); in particular. I know it seems counter-intuitive to call dispose on it seemingly before making it visible, but trust me, it will work better this way, since the dispose call will be blocked by the time it takes to run the background thread.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Forgive my ignorance, @Hovercrafter Full of Eels, but I'm very new to this and I don't quite understand what you mean. It's my first time using SwingWorker and, in fact, I extracted the idea from an example when I was looking for a way to run my thing without blocking the app's graphic interface. Would you mind posting an example? – Nick Fanelli Nov 15 '14 at 19:14
  • @NickFanelli: You can find many examples already created by me by searching on my name and the term SwingWorker, for example [this link](http://stackoverflow.com/search?tab=votes&q=user%3a522444%20%5bswingworker%5d). If you want a more specific example, then consider creating and posting your own [mcve](http://stackoverflow.com/help/mcve), and I'll adapt it. – Hovercraft Full Of Eels Nov 15 '14 at 19:16
  • @NickFanelli: have a look at [this example](http://stackoverflow.com/a/10240173/522444) for a sample program created by me. – Hovercraft Full Of Eels Nov 15 '14 at 19:17
  • @NickFanelli: so the first quick change I would make in your code above would be to remove `waitingDialog.setVisible(true);` from your SwingWorker, and then call it ***AFTER*** executing the SwingWorker, after `backgroundThread.execute();` in particular. I know it seems counterintuitive to call dispose on it above making it visible, but trust me, it will work better this way. – Hovercraft Full Of Eels Nov 15 '14 at 19:29
  • I think I get it now, @Hovercraft Full Of Eels. Simply replacing the `setVisible(true)` from within the SwingWorker to right after its definition made the program flow as expected. The reason I wanted the dialog to be visible only at that point is because the `initParameters()` method triggers a `JOptionPane.showOptionDialog()` to ask the user whether or not to use one particular parameter, thus I didn't the waiting dialog to be visible (giving a w̲a̲i̲t̲i̲n̲g̲ message) while at the same time there is another dialog asking for input. – Nick Fanelli Nov 15 '14 at 20:00
  • 1
    @NickFanelli: if all `initParameters()` does is call a JOptionPane, then it to should be called on the Swing event thread and out of the background thread. JOptionPanes are Swing components and need to be handled on the event thread. Again, if still stuck consider creating that [small, compilable, runnable test program](http://stackoverflow.com/help/mcve). – Hovercraft Full Of Eels Nov 15 '14 at 20:03
  • I ask how do I make the dialog show only when I wanted it to show? In other words, the script is: 1. show the "waiting" dialog when SwingWorker is started 2. hide/stop/disable/dispose the dialog when the `initParameter()` issues the option dialog that needs user input (press of a button); 3. show the waiting dialog again as the processing resumed 4. dispose of the dialog because the background processing has completed. – Nick Fanelli Nov 15 '14 at 20:07
  • @NickFanelli: One solution is to use multiple SwingWorkers or simple background threads, but to do it all in one SwingWorker would require the worker to change state and notify the GUI of this change. Please hang on, this will take some thought and for me to create a small functioning test program. In the meantime, consider creating your own small example/test program. – Hovercraft Full Of Eels Nov 15 '14 at 20:12
  • No, @Hovercraft Full Of Eels. The `initParameters()` doesn't only call a JOptionPane. It also fetches some data from a database and initializes a few objects. If you take a look at my last comment you'll understand what I need (sorry that I didn't know how to format it nicely as a list). – Nick Fanelli Nov 15 '14 at 20:16
  • @NickFanelli: no problem. To format a list of information, or to post new or changed code, or to make significant additions to your question, edit your original questions like I have done for you. – Hovercraft Full Of Eels Nov 15 '14 at 20:20
  • Ok, @Hovercraft Full Of Eels. I will wait for your reply. I'm fixing a few other things. I would be happy to write my own working example program, but I just have no clue how to do it as I need. If I did, I would have already done so in my code. Thank you for your patience and for being so helpful thus far. – Nick Fanelli Nov 15 '14 at 20:21
  • @HovercraftFullOfEels "You can find many examples already created by me by searching on my name and the term SwingWorker, for example... " ~ That's how I found this post ;-) – Edward J Beckett Feb 03 '15 at 09:03
2

First things first, I accepted @Hovercraft Full Of Eels answer because it indeed helped me. Like I said in the comments "Simply replacing the setVisible(true) from within the SwingWorker to right after its definition made the program flow as expected", which was done thanks to his explanation.

However, I needed a little more (which is described as a script in the "Edit" section of the original question). I managed to achieve the desired behavior with the following code...

final JOptionPane optionPane = new JOptionPane("<html>Please wait...<br>Initializing parameters</html>", JOptionPane.INFORMATION_MESSAGE, JOptionPane.DEFAULT_OPTION,
                    new ImageIcon(Home.class.getResource("/path/to/a/gif/loading.gif")), new Object[]{}, null);

            final JDialog waitingDialog = optionPane.createDialog(optionPane, "MyApp");
            waitingDialog.setLocationRelativeTo(homeFrame);
            waitingDialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);


            SwingWorker<Void, Void> backgroundThread1 = new SwingWorker<Void, Void>() {
                @Override
                protected Void doInBackground() throws Exception {
                    System.out.println("[DEBUG] -- Background processing of initParameters() has started");
                    MyCustomClass.initParameters();
                    System.out.println("[DEBUG] -- initParameters() has finished. Moving on...");
                    return null;
                }

                @Override
                protected void done() {
                    waitingDialog.setVisible(false);
                    optionPane.setMessage("<html>Please wait... <br>Now working on your thing.</html>");
                };
            };
            backgroundThread1.execute();
            waitingDialog.setVisible(true);

            SwingWorker<Void, Void> backgroundThread2 = new SwingWorker<Void, Void>() {
                @Override
                protected Void doInBackground() throws Exception {
                    System.out.println("[DEBUG] -- Start now the main processing in background");
                    MyCustomClass.run();
                    return null;
                }

                @Override
                protected void done() {
                    waitingDialog.dispose();
                };
            };
            backgroundThread2.execute();
            waitingDialog.setVisible(true);

Basically (and pretty obviously) all I did was create a new SwingWorker and have each of the routines run in one of them. When the first one is done, all it does is hide (setVisible(false)) the "waitingDialog" - so the main thread isn't blocked on it - and changes the JOptionPane's message to be used by the dialog when it is made visible again after the second SwingWorker has started.

I don't know whether this is a good or even best / most clever way to do it, but it is working. If there is a better way, I would be very glad to learn about it.

There is only one issue which I haven't been able to solve yet. Somewhere and somehow during the MyCustomClass.run() routine, the second SwingWorker, which is the one executing it, is being killed/terminated. How I know it is happening? Well, for other debugging reasons, there is a loop in my routine printing a certain value as it is updated and then all of a sudden it stops printing (no, the loop hasn't reached its end), the "waitingDialog" disappears and nothing else happens. The app doesn't crash or anything. I am able to go about using it as if nothing had happened. Not a single exception is thrown.

I don't know how or why this is happening. Any help would be greatly appreciated.

Nick Fanelli
  • 265
  • 1
  • 6
  • 19
1

Swingworker is known to swallow runtime exceptions in the background thread. You can detect them by performing a "get()" in the done() method of the Swingworker. Do a search for "SimpleSwingWorker" and you will find examples of how to do this.

DaveB
  • 1,836
  • 1
  • 15
  • 13
  • Thank you, @Dave. I have looked at the questions returned by the "SimpleSwingWorker" search and the answers to those indeed are meaningful to me. I haven't yet applied either to my app, but as soon as I can do that, I return here to let you know whether I could resolve the issue or not. – Nick Fanelli Nov 18 '14 at 13:45