5

My question is a theory-based question, but it does meet a specific need I have.

What do you do when your SwingWorker throws an Exception that you a) can anticipate and b) need to recover from and continue, but you want to notify the user that this error has happened? How do you grab the expected exception and notify the user without violating the "No Swing code from doInBackground()" rule?

I have, in consideration of this problem, developed a SSCCE that I would like to put forth with the questions below it.

SSCCE:

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.ExecutionException;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;

public class Test {

    public static void main(String args[]) {
        final JFrame frame = new JFrame();
        JButton go = new JButton("Go.");
        go.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                new Task(frame);
            }
        });
        frame.add(go);
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    static class Task extends SwingWorker<Void, Void> {
        JFrame parent;
        JDialog dialog;
        public Task(JFrame parent) {
            this.parent = parent;
            dialog = new JDialog(parent);
            JProgressBar jpb = new JProgressBar();
            jpb.setIndeterminate(true);
            dialog.add(jpb);
            dialog.setLocationRelativeTo(null);
            dialog.setVisible(true);
            execute();
        }

        @Override
        protected Void doInBackground() throws Exception {
            for(int i = 0; i < 100000; i++) {
                System.out.println(i);
                try {
                    if(i == 68456) throw new IllegalStateException("Yikes! i = 68456.");
                } catch (final IllegalStateException e) {
                    SwingUtilities.invokeAndWait(new Runnable() {
                        @Override
                        public void run() {
                            JOptionPane.showMessageDialog(parent, "Error: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
                        }
                    });
                }
            }
            return null;
        }

        @Override
        protected void done() {
            if (!isCancelled()) {
                try {
                    get();
                } catch (ExecutionException | InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("done");
            dialog.dispose();
        }

    }
}

Is it valid to call SwingUtilities.invokeAndWait() within the doInBackground() method? I did some Thread profiling on this and the results were thus:

enter image description here

Once the "Go" button is pressed, the SwingWorker-pool-1-thread-1 thread goes Green. THEN, when the if condition is met, the error is thrown, and the error dialog is displayed, the thread goes yellow, and there is indeed a green "blip" on the AWT-EventQueue-0 thread, indicating that the EDT was invoked. I waited about 10 seconds, pressed "ok," and the SwingWorker thread went green again.

Are there any potential pitfalls to doing something like this? Does anyone have experience with notifying users of computation errors in real time from the SwingWorker?

I'll be honest, this approach has me leery. It seems unorthodox but I cannot say for certain whether this is a bad idea.

ryvantage
  • 13,064
  • 15
  • 63
  • 112
  • If it's a handled exception you can instead of returning `Void` in `doInBackground` return some bean with `errorCode` `description`.. or when exception happen call `publish` to get `process` called and handle exception, or you can use `firePropertyChange` that an error happen – nachokk Dec 13 '13 at 15:18
  • Well sure I can return something other than `Void` but it is my SSCCE that is returning `Void`. My real application might need to return interim results. – ryvantage Dec 13 '13 at 15:27
  • SwingWorker isn't correctly used doInBackground(workers thread, not place for invokeAndWait, only if isn't EDT after Initial Tread, caused an exception if EDT is active), use publish(done on EDT) for JOptionPane, – mKorbel Dec 13 '13 at 20:15
  • what do you really want to do or know about – mKorbel Dec 13 '13 at 20:15
  • @mKorbel, according to [this answer](http://stackoverflow.com/a/5500371/963076), this is a valid use of `invokeAndWait` because 1) it is not called from the EDT, 2) I need to update the GUI, 3) I need my thread to wait until the user hits "ok" before continuing. That, afaict, is impossible with `publish()` and `process()`. – ryvantage Dec 13 '13 at 20:30
  • do not take as attack to your person, your knowledge are missinterpreted by *** from wrong threads..... 1) wrong forgot about, 2) wrong is not called from the EDT - wrong to test whats happend in the case that invokeAndWait is called to EDT without test for isEventDispathThread 3) I need to update the GUI - use setProcess, progress, publish or done(is used for exceptions handling in your case, because returns result only or exception only) 4) I need my thread to wait until - logics is correct, but SwingWorker isn't reusable, you need to create a new instance, – mKorbel Dec 13 '13 at 21:03
  • 5) That, afaict, is impossible with publish() and process() - yes is possible, to create a new instance, SwingWorker isn't reusable. 6) search in my questions, there is logics for multithreading in one SwingWorkers code body, – mKorbel Dec 13 '13 at 21:03

1 Answers1

4

I see no problem with using invokeAndWait() when the user actually needs to approve. If not, as shown in this example, a SwingWorker<Void, String> can simply call publish() with data and error messages interleaved. A suitable message appended in done() would allow the user to review the accumulated output if necessary.

Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045