All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.rouz.flo.context.AsyncContext Maven / Gradle / Ivy

package io.rouz.flo.context;

import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.function.Function;

import io.rouz.flo.Fn;
import io.rouz.flo.Task;
import io.rouz.flo.TaskBuilder.F0;
import io.rouz.flo.TaskContext;

/**
 * A {@link TaskContext} that executes evaluation and {@link Value} computations on a given
 * {@link Executor}.
 *
 * 

Override {@link #evaluate(Task)} to implement {@link Value} memoization. */ public class AsyncContext implements TaskContext { private final Executor executor; protected AsyncContext(Executor executor) { this.executor = Objects.requireNonNull(executor); } public static TaskContext create(Executor executor) { return new AsyncContext(executor); } @Override public Value evaluateInternal(Task task, TaskContext context) { return flatten(() -> TaskContext.super.evaluateInternal(task, context)); } @Override public final Value value(Fn 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> 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 fn) { return new FutureValue<>(future.thenApplyAsync(fn, executor)); } @Override public Value flatMap(Function> 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"); } } } }