Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error handling in SwingWorker

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.

like image 880
ryvantage Avatar asked Oct 02 '22 04:10

ryvantage


1 Answers

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.

like image 108
trashgod Avatar answered Oct 12 '22 21:10

trashgod