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

com.iodesystems.fn.thread.Async Maven / Gradle / Ivy

Go to download

Fn is a lazy Java Library that helps utilize some rudimentary functional concepts with more nounular objects

There is a newer version: 3.0.4
Show newest version
package com.iodesystems.fn.thread;

import com.iodesystems.fn.data.Option;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

public abstract class Async {

  public static final Executor INLINE = Runnable::run;
  protected final List> nexts = new ArrayList<>();
  final Executor executor;
  protected Option result = null;
  protected Exception exception = null;
  protected int progress = -1;

  Async(Executor executor) {
    this.executor = executor;
  }

  public static  Async async(Callable initial) {
    return async(INLINE, initial);
  }

  public static  Async async(final A value) {
    return async(INLINE, () -> value);
  }

  public static  Deferred defer() {
    return defer(INLINE);
  }

  public static  Deferred defer(Executor executor) {
    return new Deferred<>(executor);
  }

  public static  Async async(Executor executor, Callable initial) {
    return new Initial<>(executor, initial);
  }

  @SafeVarargs
  public static  Next, List> when(final Executor executor, final Async... asyncs) {
    final List results = new ArrayList<>(asyncs.length);
    final List> thens = new ArrayList<>();
    final AtomicInteger countdown = new AtomicInteger(asyncs.length);
    final AtomicBoolean hasException = new AtomicBoolean(false);
    int index = 0;

    final Next, List> next =
        new Next, List>(
            executor,
            new Result>() {
              // Ignore this
            },
            null) {
          @Override
          public void remove() {
            for (Next then : thens) {
              then.remove();
            }
          }
        };

    for (Async async : asyncs) {
      final int asyncIndex = index++;
      results.add(null);
      thens.add(
          async.then(
              executor,
              new Result() {
                @Override
                public A onResult(A a) throws Exception {
                  results.set(asyncIndex, a);
                  if (countdown.decrementAndGet() == 0) {
                    next.onParentResult(executor, results);
                  }

                  return super.onResult(a);
                }

                @Override
                public Option onException(Exception e) {
                  if (hasException.compareAndSet(false, true)) {
                    next.onParentException(executor, e);
                  }
                  return Option.empty();
                }
              }));
    }
    return next;
  }

  @SafeVarargs
  public static  Async> when(Async... asyncs) {
    return when(INLINE, asyncs);
  }

  synchronized void exceptionInternal(Executor executor, Exception exception) {
    this.exception = exception;
    for (Next next : nexts) {
      next.onParentException(executor, exception);
    }
  }

  protected synchronized  Next then(Next next) {
    if (progress >= 0) {
      next.onParentProgress(executor, progress);
    }
    if (result != null) {
      next.onParentResult(executor, result.orElse(null));
    } else if (exception != null) {
      next.onParentException(executor, exception);
    }
    nexts.add(next);
    return next;
  }

  public  Next then(OnResult onResult) {
    return then(executor, onResult);
  }

  public  Next then(OnResult onResult, final OnException onException) {
    return then(executor, onResult, onException);
  }

  public  Next then(
      OnResult onResult, final OnException onException, final OnProgress onProgress) {
    return then(executor, onResult, onException, onProgress);
  }

  public  Next then(Executor executor, OnResult onResult) {
    return then(executor, onResult, null);
  }

  public  Next then(
      Executor executor, OnResult onResult, final OnException onException) {
    return then(executor, onResult, onException, null);
  }

  public  Next then(
      Executor executor,
      final OnResult onResult,
      final OnException onException,
      final OnProgress onProgress) {
    return then(
        new Next<>(
            executor,
            new From() {
              @Override
              public B onResult(A a) throws Exception {
                return onResult.onResult(a);
              }

              @Override
              public int onProgress(int progress) {
                if (onProgress != null) {
                  return onProgress.onProgress(progress);
                }
                return super.onProgress(progress);
              }

              @Override
              public Option onException(Exception e) {
                if (onException != null) {
                  return onException.onException(e);
                }
                return super.onException(e);
              }
            },
            this));
  }

  public Next then(Result from) {
    return then(executor, from);
  }

  public  Next then(From from) {
    return then(executor, from);
  }

  public Next then(Executor executor, Result result) {
    return then(new Next<>(executor, result, this));
  }

  public  Next then(Executor executor, From from) {
    return then(new Next<>(executor, from, this));
  }

  synchronized void progressInternal(Executor executor, int progress) {
    this.progress = progress;
    for (Next next : nexts) {
      next.onParentProgress(executor, progress);
    }
  }

  synchronized void resultInternal(Executor executor, A result) {
    this.result = Option.of(result);
    for (Next next : nexts) {
      next.onParentResult(executor, result);
    }
  }

  public Next onProgress(OnProgress onProgress) {
    return then(executor, null, null, onProgress);
  }

  public  Next onException(OnException onException) {
    return then(executor, null, onException, null);
  }

  public Next onProgress(Executor executor, OnProgress onProgress) {
    return then(executor, null, null, onProgress);
  }

  public  Next onException(Executor executor, OnException onException) {
    return then(executor, null, onException, null);
  }

  private synchronized void remove(Next next) {
    nexts.remove(next);
  }

  public interface OnResult {

    B onResult(A a) throws Exception;
  }

  public interface OnException {

    Option onException(Exception e);
  }

  public interface OnProgress {

    int onProgress(int progress);
  }

  public static class Initial extends Async {

    private Initial(final Executor executor, final Callable callable) {
      super(executor);
      if (executor == INLINE) {
        try {
          resultInternal(executor, callable.call());
        } catch (Exception e) {
          exceptionInternal(executor, e);
        }
      } else {
        executor.execute(
            () -> {
              try {
                Initial.this.resultInternal(executor, callable.call());
              } catch (Exception e) {
                Initial.this.exceptionInternal(executor, e);
              }
            });
      }
    }
  }

  public static class Next extends Async {

    private final From from;
    private final Async parent;

    public Next(Executor executor, From from, Async parent) {
      super(executor);
      this.from = from;
      this.parent = parent;
    }

    public void remove() {
      //noinspection unchecked
      parent.remove(this);
    }

    private void onParentProgressInternal(int progress) {
      progressInternal(executor, from.onProgress(progress));
    }

    private void onParentResultInternal(final A result) {
      try {
        resultInternal(executor, from.onResult(result));
      } catch (Exception e) {
        exceptionInternal(executor, e);
      }
    }

    void onParentProgress(Executor parentExecutor, final int progress) {
      if (isCurrentExecutor(parentExecutor)) {
        onParentProgressInternal(progress);
      } else {
        this.executor.execute(() -> Next.this.onParentProgressInternal(progress));
      }
    }

    private boolean isCurrentExecutor(Executor executor) {
      return this.executor == INLINE || executor == this.executor;
    }

    void onParentResult(Executor parentExecutor, final A result) {
      if (isCurrentExecutor(parentExecutor)) {
        onParentResultInternal(result);
      } else {
        this.executor.execute(() -> Next.this.onParentResultInternal(result));
      }
    }

    void onParentException(Executor parentExecutor, final Exception exception) {
      if (isCurrentExecutor(parentExecutor)) {
        onParentExceptionInternal(exception);
      } else {
        this.executor.execute(() -> Next.this.onParentExceptionInternal(exception));
      }
    }

    private void onParentExceptionInternal(Exception exception) {
      try {
        Option recovery = from.onException(exception);
        if (recovery.isPresent()) {
          resultInternal(executor, recovery.get());
        } else {
          exceptionInternal(executor, exception);
        }
      } catch (Exception e) {
        exceptionInternal(executor, e);
      }
    }
  }

  public abstract static class Result extends From {

    @Override
    public A onResult(A a) throws Exception {
      return a;
    }
  }

  public abstract static class From implements OnResult, OnException, OnProgress {

    @Override
    public B onResult(A a) throws Exception {
      return null;
    }

    @Override
    public int onProgress(int progress) {
      return progress;
    }

    @Override
    public Option onException(Exception e) {
      return Option.empty();
    }
  }
}