View Javadoc
1   package net.secodo.jcircuitbreaker.breakhandler.impl;
2   
3   import java.util.Optional;
4   import java.util.concurrent.Callable;
5   
6   import net.secodo.jcircuitbreaker.breaker.ContextAwareCircuitBreaker;
7   import net.secodo.jcircuitbreaker.breaker.execution.ExecutionContext;
8   import net.secodo.jcircuitbreaker.breakhandler.BreakHandler;
9   import net.secodo.jcircuitbreaker.breakhandler.exception.BreakHandlerException;
10  import net.secodo.jcircuitbreaker.breakstrategy.BreakStrategy;
11  import net.secodo.jcircuitbreaker.exception.TaskExecutionException;
12  import net.secodo.jcircuitbreaker.task.Task;
13  
14  
15  /**
16   * An implementation of {@link BreakHandler} that retries to execute given <i>Task</i> again. The maximum number of
17   * attempts is given as a constructor param. In case maximum number of attempts is exceeded
18   * {@link RetryHandlerException} is thrown.
19   *
20   * <p>If you need other result of exceeding max number of attempts than just an exception, you can subclass this class,
21   * call super, catch {@link RetryHandlerException} and handle this situation in the catch clause.
22   *
23   * <p>WARNING: This implementation is stateful and if directly passed to circuit breaker it will be shared between
24   * different executions of circuit breaker. This is usually not what is expected because it stores number of
25   * attempts in private field. If you plan to use {@link StatefulRetryHandler} in most cases you want to use
26   * {@link ReusableRetryHandler} instead. This factory
27   * create new instance of {@link StatefulRetryHandler} per single execution if circuit breaker.
28   *
29   * @param <R> the return type of real-method
30   */
31  public class StatefulRetryHandler<R> implements BreakHandler<R> {
32    private final int maxNumberOfAttempts;
33    private final Optional<RetryHandlerOnRetryCallback<R>> onRetryCallback;
34  
35    private int currentRetryAttempt;
36  
37    public StatefulRetryHandler() {
38      this(1);
39    }
40  
41    public StatefulRetryHandler(int maxNumberOfAttempts) {
42      this(maxNumberOfAttempts, null);
43    }
44  
45    public StatefulRetryHandler(int maxNumberOfAttempts, RetryHandlerOnRetryCallback<R> onRetryCallback) {
46      this.maxNumberOfAttempts = maxNumberOfAttempts;
47      this.onRetryCallback = Optional.ofNullable(onRetryCallback);
48    }
49  
50    @Override
51    public R onBreak(ContextAwareCircuitBreaker<R> circuitBreaker, Task<R> task, BreakStrategy<R> breakStrategy,
52                     ExecutionContext<R> executionContext) throws TaskExecutionException, BreakHandlerException {
53      if (currentRetryAttempt == maxNumberOfAttempts) {
54        throw new RetryHandlerException(
55          "Number of retries reached maximum. Unable to execute the task: " + task + ". Max number of attempts was: " +
56          maxNumberOfAttempts);
57      }
58      currentRetryAttempt++;
59      onRetry(currentRetryAttempt, task, executionContext);
60  
61      return circuitBreaker.executeInContext(task, breakStrategy, this, executionContext);
62  
63    }
64  
65    protected void onRetry(int currentRetryAttempt, Task<R> task, ExecutionContext<R> executionContext) {
66      if (onRetryCallback.isPresent()) {
67        onRetryCallback.get().onRetry(currentRetryAttempt, executionContext);
68      }
69    }
70  
71  }