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

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

There is a newer version: 0.0.13
Show newest version
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> 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");
      }
    }
  }
}