23

Consider this code:

Thread.setDefaultUncaughtExceptionHandler((Thread t, Throwable e) -> {
    System.out.println("An exception occurred!");
});

// set the exception handler for the JavaFX application thread
Thread.currentThread().setUncaughtExceptionHandler((Thread t, Throwable e) -> {
    System.out.println("An exception occurred!");
});

Task<?> task = new Task() {
    @Override
    public Void call() throws Exception {
        throw new RuntimeException("foobar");
    };
};

new Thread(task).start();

If we run the code the runtime exception never triggers the default exception handler but is instead consumed by the task. The only way to counteract this that I found is to rethrow the exception in task.setOnFailed:

task.setOnFailed((WorkerStateEvent t) -> {
    throw new RuntimeException(task.getException());
});

Since JavaFX 8 now has support for the UncaughtExceptionHandler why isn't the exception propagated to the exception handler?

user555
  • 1,564
  • 2
  • 15
  • 29

4 Answers4

11

Inside the Task.call() method just throw the exception and add a ChangeListener to the task like this:

task.exceptionProperty().addListener((observable, oldValue, newValue) ->  {
  if(newValue != null) {
    Exception ex = (Exception) newValue;
    ex.printStackTrace();
  }
});

Then, after the task failed with the exception, you get notified by the listener which exception was thrown during execution. You can easily exchange the line ex.printStackTrace(); with an Alert if you are in the JavaFX Execution Thread.

akmonius
  • 185
  • 1
  • 11
10

Feature, Task maintains an exception property itself. The idea is, that when an exception is thrown, the tasks fails, and one may ask which exception was thrown. In that respect Task was conceived as a quasi batch job, running in the background, and possibly silently failing.

That reflects also a bit of the asynchrone behaviour; where the exception could be handled. Not at the place where start was called.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • 1
    Is there a way to rethrow the exception without wrapping it in a RuntimeException? I tried `throw task.getException();` but the compiler just screamed. – user555 Sep 18 '14 at 16:35
  • Not to my - very limited - knowledge on JavaFX. – Joop Eggen Sep 19 '14 at 07:43
  • 2
    No, you can not. As you can see in the docs (https://docs.oracle.com/javafx/2/api/javafx/event/EventHandler.html), the handle-method of the Eventhandler does not throw any Exception. You also can not give the Exception to the surrounding block to rethrow, becaus that block has already been leaved because of the asynchronous call. – Franz Deschler Mar 27 '15 at 16:50
6

A bit late probably but you can print the throwable itself:

task.setOnFailed(new EventHandler<WorkerStateEvent>() {
    @Override
    public void handle(WorkerStateEvent arg0) {
        Throwable throwable = task.getException(); 
        throwable.printStackTrace();
    }
}

The exceptions gets thrown anyway, but you can use this to display it to the user or log it.

Peter
  • 1,844
  • 2
  • 31
  • 55
2

I know that this question is old, but I searched for an answer and have not found very much about this topic. I think you can rethrow the exception if you like to do so. Here is the sample code:

public class Main extends Application {

@Override
public void start(Stage stage) {

    Task<Void> task = new Task<Void>() {
        @Override
        protected Void call() throws Exception {
            throw new IndexOutOfBoundsException();
        }
    };
    task.setOnSucceeded(evt -> System.out.println("Task succeeded!"));
    task.setOnCancelled(evt -> System.out.println("Task cancelled!"));
    task.setOnFailed(evt -> {
        System.out.println("Task failed!");
        if (task.getException() instanceof IndexOutOfBoundsException) {
            System.out.println("...with an IndexOutOfBoundsException");
        } else if (task.getException() instanceof NumberFormatException) {
            System.out.println("...with a NumberFormatException");
        } else {
            System.out.println("...with another, unexpected execption");
        }
    });

    VBox box = new VBox();
    Scene scene = new Scene(box, 200, 200);
    stage.setScene(scene);
    stage.setTitle("Thread Example");
    stage.show();

    new Thread(task).start();

}

public static void main(String[] args) {
    launch(args);
}
}

Console-Output: Task failed! ...with a IndexOutOfBoundsException

If an exception is thrown inside a task, then the task ends up in the state 'failed'. In the setOnFailed method you can handle the failure of the task. All code inside this method is on the application thread of JavaFX but you can rely on the exception of the task by task.getException(). Besides, this code only works with JavaFX (I tried to get the same output in a normal java app, but it did not work out).

  • 1
    no good solution because I have to "guess" the exception...ok, if you know your code, you probably know what exception it is, but that's not always the case. – Ahmed Hasn. Jan 02 '17 at 09:29