
com.fitbur.failsafe.AsyncFailsafe Maven / Gradle / Ivy
package com.fitbur.failsafe;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import com.fitbur.failsafe.Callables.AsyncCallableWrapper;
import com.fitbur.failsafe.function.AsyncCallable;
import com.fitbur.failsafe.function.AsyncRunnable;
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;
/**
* Performs asynchronous executions according to a {@link RetryPolicy} and {@link CircuitBreaker}.
*
* @author Jonathan Halterman
* @param listener result type
*/
public class AsyncFailsafe extends AsyncListenerBindings, L> {
private RetryPolicy retryPolicy;
private CircuitBreaker circuitBreaker;
AsyncFailsafe(SyncFailsafe failsafe, Scheduler scheduler) {
super(scheduler);
this.retryPolicy = failsafe.retryPolicy;
this.circuitBreaker = failsafe.circuitBreaker;
this.listeners = failsafe.listeners;
this.listenerConfig = failsafe.listenerConfig;
}
/**
* Executes the {@code callable} asynchronously until a successful result is returned or the configured
* {@link RetryPolicy} is exceeded.
*
* @throws NullPointerException if the {@code callable} is null
* @throws CircuitBreakerOpenException if a configured circuit breaker is open
*/
public FailsafeFuture get(Callable callable) {
return call(Callables.asyncOf(callable), null);
}
/**
* Executes the {@code callable} asynchronously until a successful result is returned or the configured
* {@link RetryPolicy} is exceeded.
*
* @throws NullPointerException if the {@code callable} is null
* @throws CircuitBreakerOpenException if a configured circuit breaker is open
*/
public FailsafeFuture get(ContextualCallable callable) {
return call(Callables.asyncOf(callable), null);
}
/**
* Executes the {@code callable} asynchronously until a successful result is returned or the configured
* {@link RetryPolicy} is exceeded. This method is intended for integration with asynchronous code. Retries must be
* manually scheduled via one of the {@code AsyncExecution.retry} methods.
*
* @throws NullPointerException if the {@code callable} is null
* @throws CircuitBreakerOpenException if a configured circuit breaker is open
*/
public FailsafeFuture getAsync(AsyncCallable callable) {
return call(Callables.asyncOf(callable), null);
}
/**
* Executes the {@code runnable} asynchronously until successful or until the configured {@link RetryPolicy} is
* exceeded.
*
* @throws NullPointerException if the {@code runnable} is null
* @throws CircuitBreakerOpenException if a configured circuit breaker is open
*/
public FailsafeFuture run(CheckedRunnable runnable) {
return call(Callables.asyncOf(runnable), null);
}
/**
* Executes the {@code runnable} asynchronously until successful or until the configured {@link RetryPolicy} is
* exceeded.
*
* @throws NullPointerException if the {@code runnable} is null
* @throws CircuitBreakerOpenException if a configured circuit breaker is open
*/
public FailsafeFuture run(ContextualRunnable runnable) {
return call(Callables.asyncOf(runnable), null);
}
/**
* Executes the {@code runnable} asynchronously until successful or until the configured {@link RetryPolicy} is
* exceeded. This method is intended for integration with asynchronous code. Retries must be manually scheduled via
* one of the {@code AsyncExecution.retry} methods.
*
* @throws NullPointerException if the {@code runnable} is null
* @throws CircuitBreakerOpenException if a configured circuit breaker is open
*/
public FailsafeFuture runAsync(AsyncRunnable runnable) {
return call(Callables.asyncOf(runnable), null);
}
/**
* Executes the {@code callable} asynchronously until the resulting future is successfully completed or the configured
* {@link RetryPolicy} is exceeded.
*
* Supported on Java 8 and above.
*
* @throws NullPointerException if the {@code callable} is null
* @throws CircuitBreakerOpenException if a configured circuit breaker is open
*/
public java.util.concurrent.CompletableFuture future(
Callable> callable) {
java.util.concurrent.CompletableFuture response = new java.util.concurrent.CompletableFuture();
call(Callables.ofFuture(callable), new FailsafeFuture(response));
return response;
}
/**
* Executes the {@code callable} asynchronously until the resulting future is successfully completed or the configured
* {@link RetryPolicy} is exceeded.
*
* Supported on Java 8 and above.
*
* @throws NullPointerException if the {@code callable} is null
* @throws CircuitBreakerOpenException if a configured circuit breaker is open
*/
public java.util.concurrent.CompletableFuture future(
ContextualCallable> callable) {
java.util.concurrent.CompletableFuture response = new java.util.concurrent.CompletableFuture();
call(Callables.ofFuture(callable), new FailsafeFuture(response));
return response;
}
/**
* Executes the {@code callable} asynchronously until the resulting future is successfully completed or the configured
* {@link RetryPolicy} is exceeded. This method is intended for integration with asynchronous code. Retries must be
* manually scheduled via one of the {@code AsyncExecution.retry} methods.
*
* Supported on Java 8 and above.
*
* @throws NullPointerException if the {@code callable} is null
* @throws CircuitBreakerOpenException if a configured circuit breaker is open
*/
public java.util.concurrent.CompletableFuture futureAsync(
AsyncCallable> callable) {
java.util.concurrent.CompletableFuture response = new java.util.concurrent.CompletableFuture();
call(Callables.ofFuture(callable), new FailsafeFuture(response));
return response;
}
/**
* 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 AsyncFailsafe with(CircuitBreaker circuitBreaker) {
Assert.state(this.circuitBreaker == null, "A circuit breaker has already been configurd");
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 AsyncFailsafe with(RetryPolicy retryPolicy) {
Assert.state(this.retryPolicy == RetryPolicy.NEVER, "A retry policy has already been configurd");
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 AsyncFailsafe with(Listeners listeners) {
this.listeners = (Listeners) Assert.notNull(listeners, "listeners");
return (AsyncFailsafe) this;
}
/**
* Calls the asynchronous {@code callable} via the {@code executor}, performing retries according to the
* {@code retryPolicy}.
*
* @throws NullPointerException if any argument is null
* @throws CircuitBreakerOpenException if a configured circuit breaker is open
*/
@SuppressWarnings("unchecked")
private FailsafeFuture call(AsyncCallableWrapper callable, FailsafeFuture future) {
if (circuitBreaker != null) {
circuitBreaker.initialize();
if (!circuitBreaker.allowsExecution())
throw new CircuitBreakerOpenException();
}
if (future == null)
future = new FailsafeFuture();
AsyncExecution execution = new AsyncExecution(callable, retryPolicy, circuitBreaker, scheduler, future,
(ListenerBindings, Object>) this);
callable.inject(execution);
try {
future.setFuture((Future) scheduler.schedule(callable, 0, TimeUnit.MILLISECONDS));
} catch (Throwable t) {
complete(null, t, execution, false);
future.complete(null, t);
}
return future;
}
}