I have the following code:
public void restart() throws Exception
{
CompletableFuture<?> delayed = new CompletableFuture<>();
ScheduledExecutorService executorService =
Executors.newSingleThreadScheduledExecutor();
executorService.scheduleWithFixedDelay(() ->
{
try
{
throw new RuntimeException("My exception.");
}
catch(Exception e)
{
delayed.completeExceptionally(e);
}
}, 1000L, 150L, TimeUnit.MILLISECONDS));
delayed.whenCompleteAsync((r, t) -> {
if (t != null)
{
throw new RuntimeException(t);
}
});
}
I'm trying to bubble up the exception which I'll catch in my executorService
. But what happens instead is that the exception is thrown in the try block which is caught and CompletableFuture
is completedExceptionally
. Which then rethrows the exception. I was hoping that this way I'd be able to bubble up the exception.
But unfortunately, that's not what's happening. The delayed
throws the exception but it doesn't bubble up. On top of that for some reason, the exception loop starts right after this. Which is try keeps throwing exception and catch keeps catching but of course completableFuture
has already been completed so it doesn't come to that.
The question is how can we handle and bubble up the exception?
This worked for me:
public void restart() throws Exception {
CompletableFuture<?> delayed = new CompletableFuture<>();
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleWithFixedDelay(() -> {
try {
throw new RuntimeException("My exception.");
} catch(Exception e) {
delayed.completeExceptionally(e);
}
}, 1000L, 150L, TimeUnit.MILLISECONDS);
try {
delayed.get();
} catch (ExecutionException e) {
throw (Exception) e.getCause();
}
}
In this modified code, we are calling the get()
method on the delayed CompletableFuture
, which blocks until the CompletableFuture
is completed. If the CompletableFuture
is completed exceptionally, get()
throws an ExecutionException
with the original exception as its cause. We then extract the cause and rethrow it as an Exception
. This way, the exception is bubbled up to the restart()
method and can be handled appropriately.
But if we want to achieve it without using blocking get call, we can use callback Consumer<Throwable> callback
and if exeption throws, use callback.accept(e);
like in this snippet:
public static void main(String[] args) {
restart((t) -> System.out.println("Exception occurred: " + t.getMessage()));
}
public static void restart(Consumer<Throwable> callback) {
CompletableFuture.runAsync(() -> {
try {
// Code that may throw an exception goes here
throw new RuntimeException("My exception.");
} catch (Exception e) {
callback.accept(e);
}
});
}