2

Below is the sample code I'm using to understand exception handling in completablefuture in java8. If we make use of exceptionally method as per doc, exceptionally method catches even runtime exception as well and proceeds to last block in the pipeline.

if we don't use exceptionally method then, it justs prints running and exits.

Correct me if my understanding isn't correct.

Question is Lets say if i want to throw runtime exception and want application to stop. Basically if i throw Runtime exception , it shouldn't proceed to next block in pipeline. How should i do that. Any pointers are helpful.

public static void main(String[] args) {
    final CompletableFuture<String> retrieveName = CompletableFuture.supplyAsync(() -> {
        System.out.println("running");
        int i = 0;
        if(i == 0) {
            throw new RuntimeException("ding");
        }
        return "test";
    }).exceptionally(it -> {
        System.out.println(it.getMessage());
        return "empty";
    }).thenApply(it -> {

        System.out.println("last block" + it);
        return "dummy";
    });
}
Hearen
  • 7,420
  • 4
  • 53
  • 63
user3514641
  • 87
  • 2
  • 12
  • 1
    Possible duplicate of [Surprising behavior of Java 8 CompletableFuture exceptionally method](https://stackoverflow.com/questions/27430255/surprising-behavior-of-java-8-completablefuture-exceptionally-method) – pvpkiran Jan 08 '18 at 14:52
  • 1
    I don’t get your question. The behavior of not executing depend stages on exceptions is already the default. It changes only when you explicitly add exception handlers. – Holger Jan 08 '18 at 16:22
  • Need to handle many exceptions, so used exception handlers and found even RuntimeException cant stop application. Did i answer now – user3514641 Jan 08 '18 at 18:00
  • 1
    So handle explicitly whatever you want to handle, e.g. `.exceptionally(x -> { if(x instanceof ExceptionIWantToHandle) return substitution; else throw new CompletionException(x); })` – Holger Jan 08 '18 at 19:27
  • Tried the above method and still my application doesn't stop. Am i missing something. – user3514641 Jan 22 '18 at 10:34
  • Followed ur apparoach , i'm using completableFuture in apache storm framework. Currently i need to throw RuntimeException to main thread so that it stops, but this doesnt happen. All i get is CompleteException. Any pointers will be helpful. – user3514641 Jan 23 '18 at 06:49

1 Answers1

3

Try this:

public static void main(String[] args) {
    try {
        final CompletableFuture<String> retrieveName = CompletableFuture.supplyAsync(() -> {
            System.out.println("running");
            int i = 0;
            if (i == 0) {
                throw new RuntimeException("ding");
            }
            return "test";
        }).exceptionally(it -> {
            if (it.getMessage().contains("ding")) {
                throw (RuntimeException) it;
            }
            System.out.println(it.getMessage());
            return "empty";
        }).thenApply(it -> {
            System.out.println("last block" + it);
            return "dummy";
        });
        retrieveName.join();
    } catch (Exception e) {
        System.out.println("main() exception, cause=" + e.getCause());
    }
}

This is the output:

running

main() exception, cause=java.lang.RuntimeException: ding

I made 3 small changes to your code:

  • Wrapped it all in a try-catch
  • Threw a RuntimeException in exceptionally() for the "ding" exception.
  • Added a call to retrieveName.join(). From the Javadoc for CompletableFuture.join():

public T join​()

Returns the result value when complete, or throws an (unchecked) exception if completed exceptionally.


Update based on OP feedback ------->

Lets say if i want to throw runtime exception and want application to stop. Basically if i throw Runtime exception , it shouldn't proceed to next block in pipeline. How should i do that.

You can achieve what you want with just 2 changes to your code:

[1] Completely remove the exceptionally() callback so the CompletableFuture (CF) terminates with an exception. In exceptionally() in the OP code the exception was being swallowed rather than rethrown, and returning a CF, so the thenApply() method was still performed.

[2] Add a call to retrieveName.join() at the end of main(). This is a blocking call, but since the thread had terminated with an exception that 's not really relevant for the sample code. The join() method will extract the thrown RunTimeException and re-throw it, wrapped in a CompletionException.

Here's your modified code:

 public static void main(String[] args) {
    final CompletableFuture<String> retrieveName = CompletableFuture.supplyAsync(() -> {
        System.out.println("running");
        int i = 0;
        if(i == 0) {
            throw new RuntimeException("ding");
        }
        return "test";
    }).thenApply(it -> {
        System.out.println("last block" + it);
        return "dummy";
    });
    retrieveName.join();
}

Notes:

  • This is not how to do things in Production. The blocking call from join() was not a problem here, but could be for a long running CF. But you obviously can't extract the exception from the CF until it is complete, so it makes sense that the join() call blocks.
  • Always bear in mind that main() is not running in the same thread(s) as the CF.
  • An alternative approach (if viable) might be to handle all the necessary post-exception actions (logging, etc,) within exceptionally() and then terminate normally with a suitable return value (e.g. "Exception handled!") rather than propagating the exception.
  • You can check whether the CF is still running by calling the non-blocking isDone() method. You can also check whether the CF ended with an exception (isCompletedExceptionally()) or was cancelled(isCancelled​()).
skomisa
  • 16,436
  • 7
  • 61
  • 102
  • isn't join a blocking call??. Lets suppose i have issued request A and later request B. Using join will wait till A finishes, then starts B. Correct me if i'm wrong. I need a non blocking requests, which returns when it finishes. – user3514641 Feb 04 '18 at 13:27
  • 1
    Yes, the join() is blocking, but you must call some CompleteableFuture (CF) method (**join()** or **get()** or **getNow()**) to access the exception you threw within your CF. You can't expect the exception to automatically land in an external catch block in main(); the thread that main() runs in is different to the thread(s) for the CF. I've updated my answer to hopefully make things clearer. – skomisa Feb 06 '18 at 04:23