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

com.github.dakusui.actionunit.visitors.ActionPerformer Maven / Gradle / Ivy

package com.github.dakusui.actionunit.visitors;

import com.github.dakusui.actionunit.actions.*;
import com.github.dakusui.actionunit.core.Action;
import com.github.dakusui.actionunit.exceptions.ActionException;
import com.github.dakusui.actionunit.helpers.InternalUtils;
import com.github.dakusui.actionunit.visitors.reporting.Node;

import java.util.Deque;
import java.util.LinkedList;
import java.util.concurrent.Callable;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import static com.github.dakusui.actionunit.helpers.InternalUtils.runWithTimeout;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import static java.util.stream.StreamSupport.stream;

public class ActionPerformer extends ActionWalker {
  public ActionPerformer() {
  }

  @Override
  protected Consumer leafActionConsumer() {
    return Leaf::perform;
  }

  @Override
  protected Consumer concurrentActionConsumer() {
    return (Concurrent concurrent) -> {
      Deque> pathSnapshot = snapshotCurrentPath();
      stream(concurrent.spliterator(), false)
          .map(this::toRunnable)
          .map((Runnable runnable) -> (Runnable) () -> {
            branchPath(pathSnapshot);
            runnable.run();
          })
          .collect(Collectors.toList())
          .parallelStream()
          .forEach(Runnable::run);
    };
  }

  @Override
  protected  Consumer> forEachActionConsumer() {
    return (ForEach forEach) -> {
      Deque> pathSnapshot = snapshotCurrentPath();
      stream(forEach.data().spliterator(), forEach.getMode() == ForEach.Mode.CONCURRENTLY)
          .map((T item) -> (Supplier) () -> item)
          .map(forEach::createHandler)
          .forEach((Action eachChild) -> {
            branchPath(pathSnapshot);
            eachChild.accept(ActionPerformer.this);
          });
    };
  }

  @Override
  protected  Consumer> whileActionConsumer() {
    return (While while$) -> {
      Supplier value = while$.value();
      //noinspection unchecked
      while (while$.check().test(value.get())) {
        while$.createHandler(value).accept(ActionPerformer.this);
      }
    };
  }

  @Override
  protected  Consumer> whenActionConsumer() {
    return (When when) -> {
      Supplier value = when.value();
      //noinspection unchecked
      if (when.check().test(value.get())) {
        when.perform(value).accept(ActionPerformer.this);
      } else {
        when.otherwise(value).accept(ActionPerformer.this);
      }
    };
  }

  @Override
  protected  Consumer> attemptActionConsumer() {
    return (Attempt attempt) -> {
      try {
        attempt.attempt().accept(this);
      } catch (Throwable e) {
        if (!attempt.exceptionClass().isAssignableFrom(e.getClass())) {
          throw ActionException.wrap(e);
        }
        //noinspection unchecked
        attempt.recover(() -> (T) e).accept(this);
      } finally {
        attempt.ensure().accept(this);
      }
    };
  }

  @Override
  protected Consumer retryActionConsumer() {
    return (Retry retry) -> {
      try {
        toRunnable(retry.action).run();
      } catch (Throwable e) {
        Throwable lastException = e;
        for (int i = 0; i < retry.times || retry.times == Retry.INFINITE; i++) {
          if (retry.getTargetExceptionClass().isAssignableFrom(lastException.getClass())) {
            InternalUtils.sleep(retry.intervalInNanos, NANOSECONDS);
            try {
              toRunnable(retry.action).run();
              return;
            } catch (Throwable t) {
              lastException = t;
            }
          } else {
            throw ActionException.wrap(lastException);
          }
        }
        throw ActionException.wrap(lastException);
      }
    };
  }

  @Override
  protected Consumer timeOutActionConsumer() {
    return (TimeOut timeOut) -> {
      Deque> snapshotPath = snapshotCurrentPath();
      runWithTimeout((Callable) () -> {
            branchPath(snapshotPath);
            timeOut.action.accept(ActionPerformer.this);
            return true;
          },
          timeOut.durationInNanos,
          NANOSECONDS
      );
    };
  }

  private Runnable toRunnable(final Action action) {
    return () -> action.accept(ActionPerformer.this);
  }

  private Deque> snapshotCurrentPath() {
    return new LinkedList<>(this.getCurrentPath());
  }
}