View Javadoc
1   package net.secodo.jcircuitbreaker.breakhandler.impl;
2   
3   import java.lang.reflect.Constructor;
4   
5   import java.util.Optional;
6   import java.util.concurrent.Callable;
7   
8   import net.secodo.jcircuitbreaker.breaker.ContextAwareCircuitBreaker;
9   import net.secodo.jcircuitbreaker.breaker.execution.ExecutionContext;
10  import net.secodo.jcircuitbreaker.breakhandler.BreakHandler;
11  import net.secodo.jcircuitbreaker.breakhandler.BreakHandlerFactory;
12  import net.secodo.jcircuitbreaker.breakhandler.exception.BreakHandlerException;
13  import net.secodo.jcircuitbreaker.breakstrategy.BreakStrategy;
14  import net.secodo.jcircuitbreaker.task.Task;
15  
16  
17  /**
18   * The handler that throws custom exception in case "break" occurs.  The exception will be thrown with given
19   * message. The exception MUST be subclass of {@link BreakHandlerException}.
20   *
21   * <p>NOTE: the exception class given as a parameter MUST define an exception with a publicly(!) accessible constructor
22   * taking "message" String as the only parameter. This is because such constructor will be used to create instance of
23   * exception to throw.
24   *
25   * <p>This implementation can be shared between different executions of circuit breaker and therefore does not require
26   * factory method implementing {@link BreakHandlerFactory} to be reusable.
27   *
28   * @param <R> the return type of the original method that should have been executed (but break handler took over the
29   *           control instead)
30   */
31  public class ExceptionThrowingHandler<R> implements BreakHandler<R> {
32    private final String exceptionMessage;
33    private final Class<? extends BreakHandlerException> exceptionClass;
34    private final Optional<ExceptionThrowingHandlerMessageCallback<R>> messageCallback;
35  
36    /**
37     * Constructs new instance of {@link ExceptionThrowingHandler} which will throw {@link BreakHandlerException} with
38     * given message in case "break" occurred (circuit breaker executed <i>break handler</i> instead of
39     * <i>real-method</i>).
40     *
41     * @param exceptionMessage the message for the exception
42     */
43    public ExceptionThrowingHandler(String exceptionMessage) {
44      this.exceptionClass = BreakHandlerException.class;
45      this.exceptionMessage = exceptionMessage;
46      this.messageCallback = Optional.empty();
47    }
48  
49    /**
50     * Constructs new instance of {@link ExceptionThrowingHandler} which will throw {@link BreakHandlerException} with
51     * message obtained by calling
52     * {@link ExceptionThrowingHandlerMessageCallback#buildMessage(Task, ExecutionContext)}.
53     *
54     * @param callback a callback method which returns the message for the exception
55     */
56    public ExceptionThrowingHandler(ExceptionThrowingHandlerMessageCallback<R> callback) {
57      this.messageCallback = Optional.ofNullable(callback);
58      this.exceptionClass = BreakHandlerException.class;
59      this.exceptionMessage = "";
60    }
61  
62    /**
63     * Constructs new instance of {@link ExceptionThrowingHandler} which will throw exception of given class
64     * with given message in case "break" occurred (circuit breaker executed <i>break handler</i> instead of
65     * <i>real-method</i>).
66     *
67     * @param exceptionClass the type of exception to throw - must be subclass of {@link BreakHandlerException}
68     * @param exceptionMessage the message for the exception
69     */
70    public ExceptionThrowingHandler(Class<? extends BreakHandlerException> exceptionClass, String exceptionMessage) {
71      this.exceptionClass = exceptionClass;
72      this.exceptionMessage = exceptionMessage;
73      this.messageCallback = Optional.empty();
74    }
75  
76    /**
77     * Constructs new instance of {@link ExceptionThrowingHandler} which will throw exception of given class
78     * with message obtained from given callback, in case "break" occurred (circuit breaker executed <i>break handler</i>
79     * instead of <i>real-method</i>).
80     *
81     * @param exceptionClass the type of exception to throw - must be subclass of {@link BreakHandlerException}
82     * @param callback a callback method which returns the message for the exception
83     */
84    public ExceptionThrowingHandler(Class<? extends BreakHandlerException> exceptionClass,
85                                    ExceptionThrowingHandlerMessageCallback<R> callback) {
86      this.exceptionClass = exceptionClass;
87      this.exceptionMessage = "";
88      this.messageCallback = Optional.of(callback);
89    }
90  
91    @Override
92    public R onBreak(ContextAwareCircuitBreaker<R> circuitBreaker, Task<R> task, BreakStrategy<R> breakStrategy,
93                     ExecutionContext<R> executionContext) throws BreakHandlerException {
94      final BreakHandlerException exceptionToThrow;
95      String message = null;
96      try {
97        message = buildMessage(task, executionContext);
98  
99        final Constructor<? extends BreakHandlerException> exceptionClassConstructor = exceptionClass.getConstructor(
100         String.class);
101 
102       exceptionToThrow = exceptionClassConstructor.newInstance(message);
103 
104     } catch (Exception ex) {
105       throw new BreakHandlerException(
106         "Unable to throw custom exception of class: " + exceptionClass + " and message: " + message,
107         ex);
108     }
109 
110     throw exceptionToThrow;
111   }
112 
113   protected String buildMessage(Task<R> task, ExecutionContext<R> executionContext) {
114     if (messageCallback.isPresent()) {
115       return messageCallback.get().buildMessage(task, executionContext);
116     } else {
117       return this.exceptionMessage;
118     }
119   }
120 }