
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, Object>) 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