Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.hubspot.singularity.async.CompletableFutures Maven / Gradle / Ivy
package com.hubspot.singularity.async;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import io.netty.util.HashedWheelTimer;
import io.netty.util.Timeout;
import java.util.Collection;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
public class CompletableFutures {
private CompletableFutures() {}
public static CompletableFuture allOf(
Collection> futures
) {
return CompletableFuture.allOf(
futures.toArray(new CompletableFuture[futures.size()])
);
}
public static CompletableFuture anyOf(
Collection> futures
) {
return CompletableFuture.anyOf(
futures.toArray(new CompletableFuture[futures.size()])
);
}
public static CompletableFuture exceptionalFuture(Throwable t) {
CompletableFuture future = new CompletableFuture<>();
future.completeExceptionally(t);
return future;
}
/**
* Return a future that completes with a timeout after a delay.
*/
public static CompletableFuture timeoutFuture(
HashedWheelTimer hwt,
long delay,
TimeUnit timeUnit
) {
try {
CompletableFuture future = new CompletableFuture<>();
hwt.newTimeout(future::complete, delay, timeUnit);
return future;
} catch (Throwable t) {
return exceptionalFuture(t);
}
}
/**
* Useful for composing an async call in response to exceptional cases.
* For example:
* {@code
* return thenHandleCompose(myFuture, (success, th) -> {
* if (th != null) {
* return newCompletableFuture();
* } else {
* return CompletableFuture.completedFuture(success);
* }
* });
* }
*/
public static CompletableFuture thenHandleCompose(
CompletionStage completionStage,
BiFunction super T, Throwable, ? extends CompletionStage> fn
) {
return completionStage
.handle((success, throwable) -> new SuccessOrThrowable<>(success, throwable))
.thenCompose(pair -> fn.apply(pair.item, pair.ex))
.toCompletableFuture();
}
public static CompletableFuture thenHandleComposeAsync(
CompletionStage completionStage,
BiFunction super T, Throwable, ? extends CompletionStage> fn,
ExecutorService executorService
) {
return completionStage
.handleAsync(
(success, throwable) -> new SuccessOrThrowable<>(success, throwable),
executorService
)
.thenComposeAsync(pair -> fn.apply(pair.item, pair.ex), executorService)
.toCompletableFuture();
}
private static final HashedWheelTimer DEFAULT_TIMER = new HashedWheelTimer(
new ThreadFactoryBuilder().setDaemon(true).setNameFormat("futures-utils-%s").build()
);
public static CompletableFuture enforceTimeout(
CompletionStage underlyingFuture,
long timeout,
TimeUnit timeUnit
) {
return enforceTimeout(underlyingFuture, DEFAULT_TIMER, timeout, timeUnit);
}
public static CompletableFuture enforceTimeout(
CompletionStage underlyingFuture,
long timeout,
TimeUnit timeUnit,
Supplier exceptionSupplier
) {
return enforceTimeout(
underlyingFuture,
DEFAULT_TIMER,
timeout,
timeUnit,
exceptionSupplier
);
}
public static CompletableFuture enforceTimeout(
CompletionStage underlyingFuture,
HashedWheelTimer timer,
long timeout,
TimeUnit timeUnit
) {
return enforceTimeout(
underlyingFuture,
timer,
timeout,
timeUnit,
TimeoutException::new
);
}
public static CompletableFuture enforceTimeout(
CompletionStage underlyingFuture,
HashedWheelTimer timer,
long timeout,
TimeUnit timeUnit,
Supplier exceptionSupplier
) {
// We don't want to muck with the underlying future passed in, so
// chaining a .thenApply(x -> x) forces a new future to be created with its own
// completion tracking. In this way, the original future is left alone and can
// time out on its own schedule.
CompletableFuture future = underlyingFuture
.thenApply(x -> x)
.toCompletableFuture();
Timeout hwtTimeout = timer.newTimeout(
ignored -> future.completeExceptionally(exceptionSupplier.get()),
timeout,
timeUnit
);
future.whenComplete((result, throwable) -> hwtTimeout.cancel());
return future;
}
public static CompletableFuture executeWithTimeout(
Callable callable,
ExecutorService executorService,
long timeout,
TimeUnit timeUnit
) {
return executeWithTimeout(
callable,
executorService,
DEFAULT_TIMER,
timeout,
timeUnit
);
}
public static CompletableFuture executeWithTimeout(
Callable callable,
ExecutorService executorService,
HashedWheelTimer timer,
long timeout,
TimeUnit timeUnit
) {
CompletableFuture future = new CompletableFuture<>();
AtomicReference timeoutRef = new AtomicReference<>();
Future underlying = executorService.submit(
() -> {
if (future.complete(callable.call())) {
Timeout timeout1 = timeoutRef.get();
if (timeout1 != null) {
timeout1.cancel();
}
}
return null;
}
);
timeoutRef.set(
timer.newTimeout(
ignored -> {
if (!future.isDone()) {
if (future.completeExceptionally(new TimeoutException())) {
underlying.cancel(true);
}
}
},
timeout,
timeUnit
)
);
return future;
}
public static T join(CompletableFuture future, long timeout, TimeUnit unit) {
try {
return future.get(timeout, unit);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
throw new RuntimeException(e);
}
}
public static Collector, ?, CompletableFuture> collectAllOf() {
return Collectors.collectingAndThen(Collectors.toList(), CompletableFutures::allOf);
}
private static class SuccessOrThrowable {
private final T item;
private final Throwable ex;
public SuccessOrThrowable(T item, Throwable ex) {
this.item = item;
this.ex = ex;
}
}
}