Retry.java

package com.github.dakusui.actionunit.actions;

import com.github.dakusui.actionunit.core.Action;

import java.util.Formatter;
import java.util.concurrent.TimeUnit;

import static com.github.dakusui.actionunit.utils.Checks.requireArgument;
import static com.github.dakusui.actionunit.utils.InternalUtils.formatDuration;
import static com.github.dakusui.actionunit.utils.InternalUtils.formatNumberOfTimes;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.SECONDS;

public interface Retry extends Action {

  Action perform();

  int times();

  Class<? extends Throwable> targetExceptionClass();

  long intervalInNanoseconds();

  default void accept(Visitor visitor) {
    visitor.visit(this);
  }

  @Override
  default void formatTo(Formatter formatter, int flags, int width, int precision) {
    formatter.format("retry %s in %s on %s",
        formatNumberOfTimes(times()),
        formatDuration(intervalInNanoseconds()),
        targetExceptionClass().getSimpleName()
    );
  }

  class Builder extends Action.Builder<Retry> {

    private final Action                     perform;
    private       int                        times                = 2;
    private       Class<? extends Throwable> targetExceptionClass = Exception.class;
    private       long                       interval             = 10;
    private       TimeUnit                   timeUnit             = SECONDS;

    public Builder(Action perform) {
      this.perform = requireNonNull(perform);
    }

    public Builder times(int times) {
      requireArgument(v -> v >= 0, times);
      this.times = times;
      return this;
    }

    public Builder on(Class<? extends Throwable> targetExceptionClass) {
      this.targetExceptionClass = requireNonNull(targetExceptionClass);
      return this;
    }

    public Builder withIntervalOf(long interval, TimeUnit timeUnit) {
      this.interval = requireArgument(v -> v > 0, interval);
      this.timeUnit = requireNonNull(timeUnit);
      return this;
    }

    @Override
    public Retry build() {
      return new Retry() {
        @Override
        public Action perform() {
          return Builder.this.perform;
        }

        @Override
        public int times() {
          return Builder.this.times;
        }

        @Override
        public Class<? extends Throwable> targetExceptionClass() {
          return Builder.this.targetExceptionClass;
        }

        @Override
        public long intervalInNanoseconds() {
          return timeUnit.toNanos(Builder.this.interval);
        }

        @Override
        public String toString() {
          return String.format("retry(%sx%dtimes)",
              formatDuration(intervalInNanoseconds()),
              times
          );
        }
      };
    }
  }
}