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

com.fitbur.failsafe.SyncFailsafe Maven / Gradle / Ivy

The newest version!
package com.fitbur.failsafe;

import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledExecutorService;

import com.fitbur.failsafe.Callables.ContextualCallableWrapper;
import com.fitbur.failsafe.function.CheckedRunnable;
import com.fitbur.failsafe.function.ContextualCallable;
import com.fitbur.failsafe.function.ContextualRunnable;
import com.fitbur.failsafe.internal.util.Assert;
import com.fitbur.failsafe.util.concurrent.Scheduler;
import com.fitbur.failsafe.util.concurrent.Schedulers;

/**
 * Performs synchronous executions according to a {@link RetryPolicy} and {@link CircuitBreaker}.
 * 
 * @author Jonathan Halterman
 * @param  listener result type
 */
public class SyncFailsafe extends ListenerBindings, L> {
  RetryPolicy retryPolicy = RetryPolicy.NEVER;
  CircuitBreaker circuitBreaker;

  SyncFailsafe(RetryPolicy retryPolicy) {
    this.retryPolicy = retryPolicy;
  }

  SyncFailsafe(CircuitBreaker circuitBreaker) {
    this.circuitBreaker = circuitBreaker;
  }

  /**
   * Executes the {@code callable} until a successful result is returned or the configured {@link RetryPolicy} is
   * exceeded.
   * 
   * @throws NullPointerException if the {@code callable} is null
   * @throws FailsafeException if the {@code callable} fails with a Throwable and the retry policy is exceeded, or if
   *           interrupted while waiting to perform a retry.
   * @throws CircuitBreakerOpenException if a configured circuit is open.
   */
  public  T get(Callable callable) {
    return call(Assert.notNull(callable, "callable"));
  }

  /**
   * Executes the {@code callable} until a successful result is returned or the configured {@link RetryPolicy} is
   * exceeded.
   * 
   * @throws NullPointerException if the {@code callable} is null
   * @throws FailsafeException if the {@code callable} fails with a Throwable and the retry policy is exceeded, or if
   *           interrupted while waiting to perform a retry.
   * @throws CircuitBreakerOpenException if a configured circuit is open.
   */
  public  T get(ContextualCallable callable) {
    return call(Callables.of(callable));
  }

  /**
   * Executes the {@code runnable} until successful or until the configured {@link RetryPolicy} is exceeded.
   * 
   * @throws NullPointerException if the {@code runnable} is null
   * @throws FailsafeException if the {@code callable} fails with a Throwable and the retry policy is exceeded, or if
   *           interrupted while waiting to perform a retry.
   * @throws CircuitBreakerOpenException if a configured circuit is open.
   */
  public void run(CheckedRunnable runnable) {
    call(Callables.of(runnable));
  }

  /**
   * Executes the {@code runnable} until successful or until the configured {@link RetryPolicy} is exceeded.
   * 
   * @throws NullPointerException if the {@code runnable} is null
   * @throws FailsafeException if the {@code callable} fails with a Throwable and the retry policy is exceeded, or if
   *           interrupted while waiting to perform a retry.
   * @throws CircuitBreakerOpenException if a configured circuit is open.
   */
  public void run(ContextualRunnable runnable) {
    call(Callables.of(runnable));
  }

  /**
   * Configures the {@code circuitBreaker} to be used to control the rate of event execution.
   * 
   * @throws NullPointerException if {@code circuitBreaker} is null
   * @throws IllegalStateException if a circuit breaker is already configured
   */
  public SyncFailsafe with(CircuitBreaker circuitBreaker) {
    Assert.state(this.circuitBreaker == null, "A circuit breaker has already been configured");
    this.circuitBreaker = Assert.notNull(circuitBreaker, "circuitBreaker");
    return this;
  }

  /**
   * Configures the {@code retryPolicy} to be used for retrying failed executions.
   * 
   * @throws NullPointerException if {@code retryPolicy} is null
   * @throws IllegalStateException if a retry policy is already configured
   */
  public SyncFailsafe with(RetryPolicy retryPolicy) {
    Assert.state(this.retryPolicy == RetryPolicy.NEVER, "A retry policy has already been configured");
    this.retryPolicy = Assert.notNull(retryPolicy, "retryPolicy");
    return this;
  }

  /**
   * Configures the {@code listeners} to be called as execution events occur.
   * 
   * @throws NullPointerException if {@code listeners} is null
   */
  @SuppressWarnings("unchecked")
  public  SyncFailsafe with(Listeners listeners) {
    this.listeners = (Listeners) Assert.notNull(listeners, "listeners");
    return (SyncFailsafe) this;
  }

  /**
   * Creates and returns a new AsyncFailsafe instance that will perform executions and retries asynchronously via the
   * {@code executor}.
   * 
   * @throws NullPointerException if {@code executor} is null
   */
  public AsyncFailsafe with(ScheduledExecutorService executor) {
    return new AsyncFailsafe(this, Schedulers.of(executor));
  }

  /**
   * Creates and returns a new AsyncFailsafe instance that will perform executions and retries asynchronously via the
   * {@code scheduler}.
   * 
   * @throws NullPointerException if {@code scheduler} is null
   */
  public AsyncFailsafe with(Scheduler scheduler) {
    return new AsyncFailsafe(this, Assert.notNull(scheduler, "scheduler"));
  }

  /**
   * Calls the {@code callable} synchronously, performing retries according to the {@code retryPolicy}.
   * 
   * @throws FailsafeException if the {@code callable} fails with a Throwable and the retry policy is exceeded or if
   *           interrupted while waiting to perform a retry
   * @throws CircuitBreakerOpenException if a configured circuit breaker is open
   */
  @SuppressWarnings("unchecked")
  private  T call(Callable callable) {
    if (circuitBreaker != null)
      circuitBreaker.initialize();
    Execution execution = new Execution(retryPolicy, circuitBreaker, (ListenerBindings) this);

    // Handle contextual calls
    if (callable instanceof ContextualCallableWrapper)
      ((ContextualCallableWrapper) callable).inject(execution);

    T result = null;
    Throwable failure;

    while (true) {
      if (circuitBreaker != null && !circuitBreaker.allowsExecution())
        throw new CircuitBreakerOpenException();

      try {
        execution.before();
        failure = null;
        result = callable.call();
      } catch (Throwable t) {
        failure = t;
      }

      // Attempt to complete execution
      if (execution.complete(result, failure, true)) {
        if (execution.success || failure == null)
          return result;
        FailsafeException re = failure instanceof FailsafeException ? (FailsafeException) failure
            : new FailsafeException(failure);
        throw re;
      } else {
        try {
          Thread.sleep(execution.getWaitTime().toMillis());
        } catch (InterruptedException e) {
          throw new FailsafeException(e);
        }

        handleRetry((L) result, failure, execution);
      }
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy