3

I have a main class Gui that handles my GUI and calls a few SwingWorkers (calls to DB and other data sources) that have been put in their own class TablesDataManager. My issue is that when one of those swingworkers throws an exception within its done90 method (i.e. on the EDT), I want to be able to catch it somehow in the main class and act accordingly (call showErrorAndExit in my SSCCE below).

Any ideas would be welcome.

Gui.java

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

public class Gui extends JFrame {

    private final JLabel waitLabel = new JLabel();
    private final JPanel panel = new JPanel();
    private final TablesDataManager tblData = new TablesDataManager();

    /**
    * @param args the command line arguments
    */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                createAndShowGui();
            }
        });
    }

    private static void createAndShowGui() {
        Gui frame = new Gui("My Great GUI");
        frame.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }

    public Gui(String title) {
        super(title);
        panel.add(waitLabel);
        setContentPane(panel);
        try {
            initData();
        } catch (Exception e) {
            showErrorAndExit(e);
        }
    }

    //I WANT TO CALL THIS METHOD IF done() THROWS AN EXCEPTION
    private void showErrorAndExit(Exception e) {
        JOptionPane.showMessageDialog(this,
                "An unexpected error occured while initialising the tables. The application will close.\n"
                + (e.getMessage() == null ? "" : e.getMessage()),
                "Unrecoverable error",
                JOptionPane.ERROR_MESSAGE);
        this.dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING));
    }

    private void initData() {
        waitLabel.setText("Loading Data");
        tblData.initData(new Runnable() {

            public void run() {
                initTables();
            }
        });
    }

    private void initTables() {
        waitLabel.setText("Loading Tables");
        tblData.initTables(new Runnable() {

            public void run() {
                finishComponentsSetup();
            }
        });
    }

    private void finishComponentsSetup() {
        waitLabel.setText("We are done");
    }
}

TablesDataManager.java

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.IOException;
import java.util.concurrent.ExecutionException;
import javax.swing.SwingWorker;

class TablesDataManager {

    private final InitData initData = new InitData();
    private final InitTables initTables = new InitTables();

    void initData(final Runnable runAfterInit) {
        launchWorker(initData, runAfterInit);
    }

    void initTables(final Runnable runAfterInit) {
        launchWorker(initTables, runAfterInit);
    }

    private void launchWorker(final SimpleSwingWorker worker, final Runnable runAfterWorkerDone) {
        worker.addPropertyChangeListener(new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                if (evt.getPropertyName().equals("progress")) {
                    if (worker.getProgress() == 100) { //update finished
                        runAfterWorkerDone.run();
                    }
                }
            }
        });
        worker.execute();
    }

    private class InitData extends SimpleSwingWorker {

        @Override
        protected Void doInBackground() throws Exception {
            //do something in the background
            //unfortunately there is a connection problem
            throw new IOException("Can't connect to database...");
        }
    }

    private class InitTables extends SimpleSwingWorker {

        @Override
        protected Void doInBackground() throws Exception {
            //do something else in the background
            return null;
        }
    }


    private abstract class SimpleSwingWorker extends SwingWorker<Void, Void>  {

        @Override
        protected abstract Void doInBackground() throws Exception;

        @Override
        public void done() {
            try {
                get();
                setProgress(100);
            } catch (ExecutionException | InterruptedException e) {
                System.out.println("Got that?");
                //WHAT DO I DO WITH IT???
                throw new RuntimeException(e);
            }
        }
    }
}
assylias
  • 321,522
  • 82
  • 660
  • 783
  • [somehow I disregarded this question](http://stackoverflow.com/questions/7053865/cant-get-arrayindexoutofboundsexception-from-future-and-swingworker-if-threa), naming threads, naming, naming depends of Exceptions type otherwise you can lost reference, hmmm I disagreed with answers here – mKorbel Mar 06 '12 at 18:34
  • use [Executor](http://stackoverflow.com/a/6174462/714968) with limited number of concurently running thread – mKorbel Mar 06 '12 at 18:43
  • @mKorbel thanks for the link(s) - I'll have a look. – assylias Mar 06 '12 at 18:44

2 Answers2

8

Keep a reference to your workers in your GUI and in your workes add a try catch. In the catch assign the exception to a variable and add a getter to it. IN your GUI, when your worker is done, just check if there is an exception in the worker or not.

Guillaume Polet
  • 47,259
  • 4
  • 83
  • 117
2

An alternative (with slightly lower coupling) I'm nowadays playing with is to let the worker fire a propertyChange with the exception as newValue

@Override
protected void done() {
    try {
        get();
    } catch (Exception e) {
        firePropertyChange("done-exception", null, e);
    }
}
kleopatra
  • 51,061
  • 28
  • 99
  • 211
  • I know I tried that but could not find a way to catch the change properly when an Exception actually occured. I'll have a look again. Thanks. – assylias Mar 06 '12 at 15:05
  • hmm ... you mean like it wasn't fired to the PropertyChangeListener? If so, I would be interested in the outcome of your looking again, could well be that I'm overlooking something :-) – kleopatra Mar 06 '12 at 15:07
  • Something like that yes - but I did not spent much time on it as Guillaume proposed his solution which was simple and efficient. – assylias Mar 06 '12 at 15:09