StatefulRetryHandler.java
package net.secodo.jcircuitbreaker.breakhandler.impl;
import java.util.Optional;
import java.util.concurrent.Callable;
import net.secodo.jcircuitbreaker.breaker.ContextAwareCircuitBreaker;
import net.secodo.jcircuitbreaker.breaker.execution.ExecutionContext;
import net.secodo.jcircuitbreaker.breakhandler.BreakHandler;
import net.secodo.jcircuitbreaker.breakhandler.exception.BreakHandlerException;
import net.secodo.jcircuitbreaker.breakstrategy.BreakStrategy;
import net.secodo.jcircuitbreaker.exception.TaskExecutionException;
import net.secodo.jcircuitbreaker.task.Task;
/**
* An implementation of {@link BreakHandler} that retries to execute given <i>Task</i> again. The maximum number of
* attempts is given as a constructor param. In case maximum number of attempts is exceeded
* {@link RetryHandlerException} is thrown.
*
* <p>If you need other result of exceeding max number of attempts than just an exception, you can subclass this class,
* call super, catch {@link RetryHandlerException} and handle this situation in the catch clause.
*
* <p>WARNING: This implementation is stateful and if directly passed to circuit breaker it will be shared between
* different executions of circuit breaker. This is usually not what is expected because it stores number of
* attempts in private field. If you plan to use {@link StatefulRetryHandler} in most cases you want to use
* {@link ReusableRetryHandler} instead. This factory
* create new instance of {@link StatefulRetryHandler} per single execution if circuit breaker.
*
* @param <R> the return type of real-method
*/
public class StatefulRetryHandler<R> implements BreakHandler<R> {
private final int maxNumberOfAttempts;
private final Optional<RetryHandlerOnRetryCallback<R>> onRetryCallback;
private int currentRetryAttempt;
public StatefulRetryHandler() {
this(1);
}
public StatefulRetryHandler(int maxNumberOfAttempts) {
this(maxNumberOfAttempts, null);
}
public StatefulRetryHandler(int maxNumberOfAttempts, RetryHandlerOnRetryCallback<R> onRetryCallback) {
this.maxNumberOfAttempts = maxNumberOfAttempts;
this.onRetryCallback = Optional.ofNullable(onRetryCallback);
}
@Override
public R onBreak(ContextAwareCircuitBreaker<R> circuitBreaker, Task<R> task, BreakStrategy<R> breakStrategy,
ExecutionContext<R> executionContext) throws TaskExecutionException, BreakHandlerException {
if (currentRetryAttempt == maxNumberOfAttempts) {
throw new RetryHandlerException(
"Number of retries reached maximum. Unable to execute the task: " + task + ". Max number of attempts was: " +
maxNumberOfAttempts);
}
currentRetryAttempt++;
onRetry(currentRetryAttempt, task, executionContext);
return circuitBreaker.executeInContext(task, breakStrategy, this, executionContext);
}
protected void onRetry(int currentRetryAttempt, Task<R> task, ExecutionContext<R> executionContext) {
if (onRetryCallback.isPresent()) {
onRetryCallback.get().onRetry(currentRetryAttempt, executionContext);
}
}
}