views:

1121

answers:

5

I use SwingWorker in Java 6 to avoid running long-running code on the event dispatch thread.

If the call to get() in my done() method returns an exception, what is an appropriate way of handling the exception?

I'm particularly concerned about possible InterruptedExceptions. The JavaDoc example simply ignores the exception but I've learnt over the years that swallowing exceptions leads to hard-to-debug code.

A sample usage is as follows:

new SwingWorker<String, Void>() {

    @Override
    protected String doInBackground() throws Exception {
        // do long-running calculation
        return result;
    }

    @Override
    protected void done() {
        try {
            setTextField(get());
        } catch (InterruptedException e) {
            e.printStackTrace();  
        } catch (ExecutionException e) {
            e.printStackTrace();  
        }
    }
}.execute();
A: 

Assume this is a GUI application, you may want to provide a visual feedback about failed operation upon exception.

Journeyman Programmer
A: 

It depends very much on the type of errors that might result from the background job. If the job in doInBackground throws an exception, it will be propagated to the done method as a nested ExecutionException. The best practice in this case would be to handle the nested exception, rather than the ExecutionException itself.

For example: If the worker thread throws an exception indicating that the database connection has been lost, you'd probably want to reconnect and restart the job. If the work to be done depends on some kind of resource that turns out to already be in use, it would be a good idea to offer the use a retry or cancel choice. If the exception thrown doesn't have any implications for the user, just log the error and continue.

From what I can remember, I believe that the InterruptedException won't be an issue here since you make the get method call in the done method, since the InterruptedException will only be thrown if the get call is interrupted while waiting for the background job to finish. If an unexpected event like that were to occur you'd probably want to display an error message and exit the application.

Emil H
A: 

I guess you don't get many of these question with C#. You need to understand what the exception is and deal with it appropriately (which is usually to let it go further up the stack).

InterruptedException - Thrown when a thread is interrupted (by Thread.interrupt) when waiting (roughly). Why would you want a thread to be interrupted? Usually you want the thread to stop what it's doing - reset the interrupt state and exit. For instance, the PlugIn will interrupt applet threads if they continue for much longer after an applet should be gone. However, in this case provided done is called correctly you shouldn't be waiting at all. Therefore, it would be appropriate to wrap the exception in an IllegalStateException (and the API docs should probably state that). It's a really bad API. The publish/process mode probably makes more sense.

ExecutionException - You need to deal with the wrapped exception. If you are not expecting a particular type of exception, wrap it in an unchecked exception.

Generally I would suggest a clear separation between what happens on the EDT and what happens off the EDT. Therefore, avoid SwingWorker in production code.

Tom Hawtin - tackline
Why avoid SwingWorker in production code? Sun promotes it http://java.sun.com/docs/books/tutorial/uiswing/concurrency/index.html
Eddie
Avoid SwingWorker because it is atrocious design. Don't use something just because a company tells you to.
Tom Hawtin - tackline
I rely on SwingWorker - what's a good alternative?
Steve McLeod
This answers my question perfectly: "Therefore, it would be appropriate to wrap the [InterruptedException] in an IllegalStateException (and the API docs should probably state that)"
Steve McLeod
Alternatives to SwingWorker: java.util.concurrent.ThreadPoolExecutor for running off EDT, java.awt.EventQueue.invokeLater for running on EDT. In some cases you might want to be a bit smarter with invokeLater, taking more than one item at a time (but only if that is determined to be necessary).
Tom Hawtin - tackline
Tom, the new framework in Java 6 was implemented in order to add greater flexibility to the existing framework. For example, in Java 6 you can actually populate a JTable with massive amount of data as the data is being loaded. Although it should be used when appropriate its kind of far fetch to recommend to avoid it. None of your suggested alternatives can do what the Java 6 was designed for.
Jeach
You could always retrieve data in the background (although in Java 1.1 there was no EventQueue.invokeLater). SwingWorker allows trivial examples to be written with few lines of code. Real code tends to get more complex very quickly. SwingWorker imposes a bad design that tightly couple EDT and non-EDT work.
Tom Hawtin - tackline
I think you are wrong. You do want to know if the thread was interrupted. For example, the background task got canceled. Thus, when done is called, you won't be able to retrieve the result, but instead you get an InterruptedException.It's not an illegal state. It is legal for someone to cancel the background task, and you have to deal with it however it feels appropriate.I think in the catch clause for the InterruptedException, you should write the handling code considering you were not able to retrieve the data the task was supposed to fetch.
Andrei Vajna II
+2  A: 

This is as much an interface question as it is an error handling question. A lot of apps add some small table that lists the running background jobs. An exception in one of them might flash the table row that produced an error, or do something as disruptive as present an alert. It depends on the severity of the exception. I think the harder question you'll probably have to answer is how many potentially different types of exceptions are we talking about, and what is their relative severity.

I think a simple compromise might be to present a modal alert for your most severe errors, and anything else, simply record the occurrence until it a) blocks the user from proceeding furhter or b) the user closes the document/window, at which time you can show a list of exceptions that happened during background processing tasks at the same time that you ask if you want to save any unsaved buffers for example.

stinkymatt
+1  A: 

What I would recommend is to let the error propagate all the way up to where the action was started.

For example, if a user clicks on a button to fetch data from a data-source. If a problem occurs, whether it being a credential error, a network error, a database error, or whatever else, it wouldn't be appropriate for you to have logic within that worker thread to try to solve it right there.

But if you let it propagate to where the task was started, from there you can take the appropriate error corrections, such as popping the credentials dialog once more, showing a "try again" dialog or even showing an error message.

Jeach
How would I do this, seeing as the SwingWorker could throw the exception at a later stage? The action handler invoked by the button would have already returned.
Steve McLeod