I know that CompletableFuture
design does not control its execution with interruptions, but I suppose some of you might have this problem. CompletableFutu
What about?
/** @return {@link CompletableFuture} which when cancelled will interrupt the supplier
*/
public static CompletableFuture supplyAsyncInterruptibly(Supplier supplier, Executor executor) {
return produceInterruptibleCompletableFuture((s) -> CompletableFuture.supplyAsync(s, executor), supplier);
}
// in case we want to do the same for similar methods later
private static CompletableFuture produceInterruptibleCompletableFuture(
Function,CompletableFuture> completableFutureAsyncSupplier, Supplier action) {
FutureTask task = new FutureTask<>(action::get);
return addCancellationAction(completableFutureAsyncSupplier.apply(asSupplier(task)), () ->
task.cancel(true));
}
/** Ensures the specified action is executed if the given {@link CompletableFuture} is cancelled.
*/
public static CompletableFuture addCancellationAction(CompletableFuture completableFuture,
@NonNull Runnable onCancellationAction) {
completableFuture.whenComplete((result, throwable) -> {
if (completableFuture.isCancelled()) {
onCancellationAction.run();
}
});
return completableFuture; // return original CompletableFuture
}
/** @return {@link Supplier} wrapper for the given {@link RunnableFuture} which calls {@link RunnableFuture#run()}
* followed by {@link RunnableFuture#get()}.
*/
public static Supplier asSupplier(RunnableFuture futureTask) throws CompletionException {
return () -> {
try {
futureTask.run();
try {
return futureTask.get();
} catch (ExecutionException e) { // unwrap ExecutionExceptions
final Throwable cause = e.getCause();
throw (cause != null) ? cause : e;
}
} catch (CompletionException e) {
throw e;
} catch (Throwable t) {
throw new CompletionException(t);
}
};
}