Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should I handle exceptions when using SwingWorker?

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();
like image 504
Steve McLeod Avatar asked Apr 26 '09 17:04

Steve McLeod


People also ask

Where should exceptions be handled?

You should handle the exception at the lowest possible level. If method can't handle the exception properly you should throw it.

When to use SwingWorker?

SwingWorker is designed for situations where you need to have a long running task run in a background thread and provide updates to the UI either when done, or while processing. Subclasses of SwingWorker must implement the doInBackground() method to perform the background computation.


4 Answers

It's an old post, but I want to do some clarification:

SwingWorker.get throws InterruptedException, ExecutionException as checked exceptions.

Plus it throws a very specific unchecked exception that is CancellationException. Of course it could potentially throw any unchecked exception, but CancellationException is not an "exceptional" and unexpected one. It is thrown when you try to call get method after that has been called cancel.

ExecutedException is thrown when an Exception is thrown inside doInBackground. The original exception is wrapped inside an ExecutionException. When the get() method will be called, the ExecutionException will be thrown. The idea of take out the original exception and manage that is good. (As Emil H pointed out).

CancellationException is unchecked and in my opinion should be checked. The only excuse for the API implementation not to have it as checked is that it has a status method isCancelled().
You can either:
- test isCancelled() and if is true do NOT call get() since it will throw CancellationException
- surround get() with try-catch and add the CancellationException, that since unchecked will not requested by compiler
- the fact that CancellationException is not checked leave you free to forget all that stuff and get a nice surprise.
- do anything because you won't cancel the worker

InterruptedException. If you cancel a SwingThread with cancel(true) the first interruptable method call (for sure Thread.sleep, this.wait, maybe some IO methods) in doInBackground will throw InterruptException. But this exception is not wrapped in an ExecuteException. The doInBackground is left to terminate with interrupted exception. If it is catched and converted to some other exception, those will be ignored because by this moment cancel has already invoked SwingThread.done on the EDT, and if done has called get, it has get just the standard CancellationException. Not an InterruptedException!

If you cancel with cancel(false) no InterruptException is raised inside doInBackground. The same if you cancel with cancel(true) but there are no interruptable method calls inside doInBackground. In these cases, the doInBackground will follow its natural loop. This loop should test the isCancelled method and exit gracefully. If the doInBackground doesn't do so, it will run forever. I've not tested for the presence of timeouts, but I not belive so.

For me, it remains just a gray area. In which cases is InterruptedException thrown by get? I'd like to see some short code, since I couldn't produce a similar exception. :-)

P.S. I've documented in another Question&Answer that the done and state change listener in case of cancellation are called before the doInBackground exits. Since this is true, this -that is not a bug- requires a special attention when designing doInBackground method. If you are intrested in this, see SwingWorker: when exactly is called done method?

like image 198
AgostinoX Avatar answered Oct 03 '22 01:10

AgostinoX


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.

like image 26
Emil H Avatar answered Oct 03 '22 02:10

Emil H


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.

like image 31
stinkymatt Avatar answered Oct 03 '22 00:10

stinkymatt


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.

like image 34
Jeach Avatar answered Oct 03 '22 01:10

Jeach