ProjectAres/Util/core/src/main/java/tc/oc/commons/core/concurrent/FutureUtils.java

115 lines
4.4 KiB
Java

package tc.oc.commons.core.concurrent;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.function.Consumer;
import java.util.function.Function;
import javax.annotation.Nullable;
import com.google.common.base.Throwables;
import com.google.common.util.concurrent.AsyncFunction;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.common.util.concurrent.Uninterruptibles;
import tc.oc.commons.core.util.Pair;
import tc.oc.commons.core.util.ThrowingFunction;
public final class FutureUtils {
private FutureUtils() {}
/**
* Get the result of the given {@link Future}, retrying if interrupted using
* {@link Uninterruptibles#getUninterruptibly}, and wrap any checked exceptions
* other than the one given using {@link Throwables#propagate(Throwable)}.
*
* Use this when you only need to handle a particular checked exception,
* and want any others can be handled generically.
*/
public static <T, E extends Throwable> T getUncheckedExcept(Future<T> future, Class<E> except) throws E {
try {
return Uninterruptibles.getUninterruptibly(future);
} catch(ExecutionException e) {
if(except.isInstance(e.getCause())) {
throw except.cast(e.getCause());
} else {
throw Throwables.propagate(e);
}
}
}
public static <T> FutureCallback<T> successCallback(Consumer<T> consumer) {
return new SuccessCallback<>(consumer);
}
public static <T> FutureCallback<T> failureCallback(Consumer<Throwable> consumer) {
return new FailureCallback<>(consumer);
}
public static <T> void onSuccess(ListenableFuture<T> future, Consumer<T> callback) {
Futures.addCallback(future, successCallback(callback));
}
public static <T> void onSuccess(ListenableFuture<T> future, Consumer<T> callback, @Nullable Executor executor) {
if(executor != null) {
Futures.addCallback(future, successCallback(callback), executor);
} else {
onSuccess(future, callback);
}
}
public static <T> void onFailure(ListenableFuture<T> future, Consumer<Throwable> callback) {
Futures.addCallback(future, failureCallback(callback));
}
public static <T> void onFailure(ListenableFuture<T> future, Consumer<Throwable> callback, @Nullable Executor executor) {
if(executor != null) {
Futures.addCallback(future, failureCallback(callback), executor);
} else {
onFailure(future, callback);
}
}
public static <T> ListenableFuture<T> peek(ListenableFuture<T> future, Consumer<T> callback) {
return peek(future, callback, MoreExecutors.sameThreadExecutor());
}
public static <T> ListenableFuture<T> peek(ListenableFuture<T> future, Consumer<T> callback, Executor executor) {
return Futures.transform(future, (com.google.common.base.Function<T, T>) t -> {
callback.accept(t);
return t;
}, executor);
}
/**
* Equivalent to {@link Futures#transform}, but can be passed a lambda unambiguously
*/
public static <T, R> ListenableFuture<R> mapSync(ListenableFuture<T> in, ThrowingFunction<T, R, ?> op) {
return mapSync(in, op, MoreExecutors.sameThreadExecutor());
}
public static <T, R> ListenableFuture<R> mapSync(ListenableFuture<T> in, ThrowingFunction<T, R, ?> op, Executor executor) {
return mapAsync(in, out -> {
try {
return Futures.immediateFuture(op.applyThrows(out));
} catch(Throwable ex) {
return Futures.immediateFailedFuture(ex);
}
}, executor);
}
public static <T, R> ListenableFuture<R> mapAsync(ListenableFuture<T> in, AsyncFunction<T, R> op) {
return Futures.transform(in, op);
}
public static <T, R> ListenableFuture<R> mapAsync(ListenableFuture<T> in, AsyncFunction<T, R> op, Executor executor) {
return Futures.transform(in, op, executor);
}
public static <A, B> ListenableFuture<Pair<A, B>> pair(ListenableFuture<A> a, ListenableFuture<B> b) {
return mapSync(Futures.allAsList(a, b), list -> Pair.of((A) list.get(0), (B) list.get(1)));
}
}