package org.infinispan.util.concurrent;
import static java.util.Objects.requireNonNull;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* Utility methods connecting {@link CompletableFuture} futures.
*
* @author Dan Berindei
* @since 8.0
*/
public class CompletableFutures {
private static final CompletableFuture completedEmptyMapFuture = CompletableFuture.completedFuture(Collections.emptyMap());
private static final CompletableFuture completedNullFuture = CompletableFuture.completedFuture(null);
private static final long BIG_DELAY_NANOS = TimeUnit.DAYS.toNanos(1);
@SuppressWarnings("unchecked")
public static <K,V> CompletableFuture<Map<K, V>> completedEmptyMap() {
return completedEmptyMapFuture;
}
@SuppressWarnings("unchecked")
public static <T> CompletableFuture<T> completedNull() {
return completedNullFuture;
}
public static <T> CompletableFuture<List<T>> sequence(List<CompletableFuture<T>> futures) {
CompletableFuture<Void> all = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
return all.thenApply(v -> futures.stream().map(CompletableFuture::join).collect(Collectors.toList()));
}
public static <T> CompletableFuture<T> completedExceptionFuture(Throwable ex) {
CompletableFuture<T> future = new CompletableFuture<>();
future.completeExceptionally(ex);
return future;
}
/**
* It waits until the {@link CompletableFuture} is completed.
* <p>
* It ignore if the {@link CompletableFuture} is completed normally or exceptionally.
*
* @param future the {@link CompletableFuture} to test.
* @param time the timeout.
* @param unit the timeout unit.
* @return {@code true} if completed, {@code false} if timed out.
* @throws InterruptedException if interrupted while waiting.
* @throws NullPointerException if {@code future} or {@code unit} is {@code null}.
*/
public static boolean await(CompletableFuture<?> future, long time, TimeUnit unit) throws InterruptedException {
try {
requireNonNull(future, "Completable Future must be non-null.").get(time, requireNonNull(unit, "Time Unit must be non-null"));
return true;
} catch (ExecutionException e) {
return true;
} catch (java.util.concurrent.TimeoutException e) {
return false;
}
}
/**
* Wait for a long time until the {@link CompletableFuture} is completed.
*
* @param future the {@link CompletableFuture}.
* @param <T> the return type.
* @return the result value.
* @throws ExecutionException if the {@link CompletableFuture} completed exceptionally.
* @throws InterruptedException if the current thread was interrupted while waiting.
*/
public static <T> T await(CompletableFuture<T> future) throws ExecutionException, InterruptedException {
try {
return Objects.requireNonNull(future, "Completable Future must be non-null.").get(BIG_DELAY_NANOS, TimeUnit.NANOSECONDS);
} catch (java.util.concurrent.TimeoutException e) {
throw new IllegalStateException("This should never happen!", e);
}
}
public static CompletionException asCompletionException(Throwable t) {
if (t instanceof CompletionException) {
return ((CompletionException) t);
} else {
return new CompletionException(t);
}
}
public static void rethrowException(Throwable t) {
if (t != null) throw asCompletionException(t);
}
public static Throwable extractException(Throwable t) {
Throwable cause = t.getCause();
if (cause != null && t instanceof CompletionException) {
return cause;
}
return t;
}
}