
io.rouz.task.context.AsyncContext Maven / Gradle / Ivy
package io.rouz.task.context;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
import java.util.function.Function;
import io.rouz.task.Task;
import io.rouz.task.TaskContext;
import io.rouz.task.dsl.TaskBuilder.F0;
/**
* A {@link TaskContext} that executes evaluation and {@link Value} computations on a given
* {@link ExecutorService}.
*
* Override {@link #evaluate(Task)} to implement {@link Value} memoization.
*/
public class AsyncContext implements TaskContext {
private final ExecutorService executor;
protected AsyncContext(ExecutorService executor) {
this.executor = Objects.requireNonNull(executor);
}
public static TaskContext create(ExecutorService executor) {
return new AsyncContext(executor);
}
@Override
public Value evaluate(Task task) {
return flatten(() -> TaskContext.super.evaluate(task));
}
@Override
public final Value value(F0 t) {
return new FutureValue<>(CompletableFuture.supplyAsync(t, executor));
}
@Override
public final Value immediateValue(T t) {
return new FutureValue<>(CompletableFuture.completedFuture(t));
}
@Override
public Promise promise() {
return new FuturePromise<>();
}
protected final Value flatten(F0> t) {
return flatten(CompletableFuture.supplyAsync(t, executor));
}
protected final Value flatten(CompletionStage extends Value extends T>> future) {
final CompletableFuture next = new CompletableFuture<>();
future.whenCompleteAsync(
(value, throwable) -> {
if (throwable != null) {
next.completeExceptionally(resolveAppThrowable(throwable));
} else {
value.consume(next::complete);
value.onFail(next::completeExceptionally);
}
},
executor);
return new FutureValue<>(next);
}
private static Throwable resolveAppThrowable(Throwable throwable) {
return (throwable instanceof CompletionException)
? resolveAppThrowable(throwable.getCause())
: throwable;
}
private final class FutureValue implements Value {
private final CompletionStage future;
private FutureValue(CompletionStage future) {
this.future = future;
}
@Override
public TaskContext context() {
return AsyncContext.this;
}
@Override
public void consume(Consumer consumer) {
future.thenAcceptAsync(consumer, executor);
}
@Override
public void onFail(Consumer errorConsumer) {
future.whenCompleteAsync((ˍ, throwable) -> {
if (throwable != null) {
errorConsumer.accept(resolveAppThrowable(throwable));
}
}, executor);
}
@Override
public Value map(Function super T, ? extends U> fn) {
return new FutureValue<>(future.thenApplyAsync(fn, executor));
}
@Override
public Value flatMap(Function super T, ? extends Value extends U>> function) {
return flatten(future.thenApplyAsync(function, executor));
}
}
private final class FuturePromise implements Promise {
private final CompletableFuture future = new CompletableFuture<>();
private final FutureValue value = new FutureValue<>(future);
@Override
public Value value() {
return value;
}
@Override
public void set(T t) {
final boolean completed = future.complete(t);
if (!completed) {
throw new IllegalStateException("Promise was already completed");
}
}
@Override
public void fail(Throwable throwable) {
final boolean completed = future.completeExceptionally(throwable);
if (!completed) {
throw new IllegalStateException("Promise was already completed");
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy