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

net.jodah.failsafe.AsyncExecution Maven / Gradle / Ivy

There is a newer version: 2.4.4
Show newest version
/*
 * Copyright 2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */
package net.jodah.failsafe;

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

import net.jodah.failsafe.internal.util.Assert;
import net.jodah.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, Scheduler scheduler, FailsafeFuture future,
      FailsafeConfig config) {
    super(config);
    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 (config.circuitBreaker != null && !config.circuitBreaker.allowsExecution()) { completed = true; Exception failure = new CircuitBreakerOpenException(); if (config != null) config.handleComplete(null, failure, this, false); future.complete(null, failure, config.fallback, false); return; } if (completeCalled && config != null) config.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 boolean complete(Object result, Throwable failure, boolean checkArgs) { synchronized (future) { if (!completeCalled) { if (super.complete(result, failure, checkArgs)) future.complete(result, failure, config.fallback, success); 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" }) boolean completeOrRetry(Object result, Throwable failure) { synchronized (future) { if (!complete(result, failure, true) && !future.isDone() && !future.isCancelled()) { try { future.inject((Future) scheduler.schedule(callable, delayNanos, TimeUnit.NANOSECONDS)); return true; } catch (Throwable t) { failure = t; if (config != null) config.handleComplete(null, t, this, false); future.complete(null, failure, config.fallback, false); } } return false; } } }