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

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

The newest version!
package com.fitbur.failsafe;

import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

import com.fitbur.failsafe.internal.util.Assert;
import com.fitbur.failsafe.util.concurrent.Scheduler;

/**
 * Tracks asynchronous executions and allows retries to be scheduled according to a {@link RetryPolicy}.
 * 
 * @author Jonathan Halterman
 */
public final class AsyncExecution extends AbstractExecution {
  private final Callable callable;
  private final FailsafeFuture future;
  private final Scheduler scheduler;
  volatile boolean completeCalled;
  volatile boolean retryCalled;

  @SuppressWarnings("unchecked")
   AsyncExecution(Callable callable, RetryPolicy retryPolicy, CircuitBreaker circuitBreaker, Scheduler scheduler,
      FailsafeFuture future, ListenerBindings listeners) {
    super(retryPolicy, circuitBreaker, listeners);
    this.callable = (Callable) callable;
    this.scheduler = scheduler;
    this.future = (FailsafeFuture) future;
  }

  /**
   * Completes the execution and the associated {@code FutureResult}.
   *
   * @throws IllegalStateException if the execution is already complete
   */
  public void complete() {
    complete(null, null, false);
  }

  /**
   * Attempts to complete the execution and the associated {@code FutureResult} with the {@code result}. Returns true on
   * success, else false if completion failed and the execution should be retried via {@link #retry()}.
   *
   * @throws IllegalStateException if the execution is already complete
   */
  public boolean complete(Object result) {
    return complete(result, null, true);
  }

  /**
   * Attempts to complete the execution and the associated {@code FutureResult} with the {@code result} and
   * {@code failure}. Returns true on success, else false if completion failed and the execution should be retried via
   * {@link #retry()}.
   * 

* Note: the execution may be completed even when the {@code failure} is not {@code null}, such as when the * RetryPolicy does not allow retries for the {@code failure}. * * @throws IllegalStateException if the execution is already complete */ public boolean complete(Object result, Throwable failure) { return complete(result, failure, true); } /** * Records an execution and returns true if a retry has been scheduled for else returns returns false and completes * the execution and associated {@code FutureResult}. * * @throws IllegalStateException if a retry method has already been called or the execution is already complete */ public boolean retry() { Assert.state(!retryCalled, "Retry has already been called"); retryCalled = true; return completeOrRetry(lastResult, lastFailure); } /** * Records an execution and returns true if a retry has been scheduled for the {@code result}, else returns false and * marks the execution and associated {@code FutureResult} as complete. * * @throws IllegalStateException if a retry method has already been called or the execution is already complete */ public boolean retryFor(Object result) { return retryFor(result, null); } /** * Records an execution and returns true if a retry has been scheduled for the {@code result} or {@code failure}, else * returns false and marks the execution and associated {@code FutureResult} as complete. * * @throws IllegalStateException if a retry method has already been called or the execution is already complete */ public boolean retryFor(Object result, Throwable failure) { Assert.state(!retryCalled, "Retry has already been called"); retryCalled = true; return completeOrRetry(result, failure); } /** * Records an execution and returns true if a retry has been scheduled for the {@code failure}, else returns false and * marks the execution and associated {@code FutureResult} as complete. * * @throws NullPointerException if {@code failure} is null * @throws IllegalStateException if a retry method has already been called or the execution is already complete */ public boolean retryOn(Throwable failure) { Assert.notNull(failure, "failure"); return retryFor(null, failure); } /** * Prepares for an execution retry by recording the start time, checking if the circuit is open, resetting internal * flags, and calling the retry listeners. */ void before() { if (circuitBreaker != null && !circuitBreaker.allowsExecution()) { completed = true; Exception failure = new CircuitBreakerOpenException(); if (listeners != null) listeners.complete(null, failure, this, false); future.complete(null, failure); return; } if (completeCalled && listeners != null) listeners.handleRetry(lastResult, lastFailure, this); super.before(); completeCalled = false; retryCalled = false; } /** * Attempts to complete the parent execution, calls failure handlers, and completes the future if needed. * * @throws IllegalStateException if the execution is already complete */ @Override synchronized boolean complete(Object result, Throwable failure, boolean checkArgs) { if (!completeCalled) { if (super.complete(result, failure, checkArgs)) future.complete(result, failure); completeCalled = true; } return completed; } /** * Attempts to complete the execution else schedule a retry, returning whether a retry has been scheduled or not. * * @throws IllegalStateException if the execution is already complete */ @SuppressWarnings({ "unchecked", "rawtypes" }) synchronized boolean completeOrRetry(Object result, Throwable failure) { if (!complete(result, failure, true) && !future.isDone() && !future.isCancelled()) { try { future.setFuture((Future) scheduler.schedule(callable, waitNanos, TimeUnit.NANOSECONDS)); return true; } catch (Throwable t) { failure = t; if (listeners != null) listeners.complete(null, t, this, false); future.complete(null, failure); } } return false; } }