ExceptionThrowingHandler.java
package net.secodo.jcircuitbreaker.breakhandler.impl;
import java.lang.reflect.Constructor;
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.BreakHandlerFactory;
import net.secodo.jcircuitbreaker.breakhandler.exception.BreakHandlerException;
import net.secodo.jcircuitbreaker.breakstrategy.BreakStrategy;
import net.secodo.jcircuitbreaker.task.Task;
/**
* The handler that throws custom exception in case "break" occurs. The exception will be thrown with given
* message. The exception MUST be subclass of {@link BreakHandlerException}.
*
* <p>NOTE: the exception class given as a parameter MUST define an exception with a publicly(!) accessible constructor
* taking "message" String as the only parameter. This is because such constructor will be used to create instance of
* exception to throw.
*
* <p>This implementation can be shared between different executions of circuit breaker and therefore does not require
* factory method implementing {@link BreakHandlerFactory} to be reusable.
*
* @param <R> the return type of the original method that should have been executed (but break handler took over the
* control instead)
*/
public class ExceptionThrowingHandler<R> implements BreakHandler<R> {
private final String exceptionMessage;
private final Class<? extends BreakHandlerException> exceptionClass;
private final Optional<ExceptionThrowingHandlerMessageCallback<R>> messageCallback;
/**
* Constructs new instance of {@link ExceptionThrowingHandler} which will throw {@link BreakHandlerException} with
* given message in case "break" occurred (circuit breaker executed <i>break handler</i> instead of
* <i>real-method</i>).
*
* @param exceptionMessage the message for the exception
*/
public ExceptionThrowingHandler(String exceptionMessage) {
this.exceptionClass = BreakHandlerException.class;
this.exceptionMessage = exceptionMessage;
this.messageCallback = Optional.empty();
}
/**
* Constructs new instance of {@link ExceptionThrowingHandler} which will throw {@link BreakHandlerException} with
* message obtained by calling
* {@link ExceptionThrowingHandlerMessageCallback#buildMessage(Task, ExecutionContext)}.
*
* @param callback a callback method which returns the message for the exception
*/
public ExceptionThrowingHandler(ExceptionThrowingHandlerMessageCallback<R> callback) {
this.messageCallback = Optional.ofNullable(callback);
this.exceptionClass = BreakHandlerException.class;
this.exceptionMessage = "";
}
/**
* Constructs new instance of {@link ExceptionThrowingHandler} which will throw exception of given class
* with given message in case "break" occurred (circuit breaker executed <i>break handler</i> instead of
* <i>real-method</i>).
*
* @param exceptionClass the type of exception to throw - must be subclass of {@link BreakHandlerException}
* @param exceptionMessage the message for the exception
*/
public ExceptionThrowingHandler(Class<? extends BreakHandlerException> exceptionClass, String exceptionMessage) {
this.exceptionClass = exceptionClass;
this.exceptionMessage = exceptionMessage;
this.messageCallback = Optional.empty();
}
/**
* Constructs new instance of {@link ExceptionThrowingHandler} which will throw exception of given class
* with message obtained from given callback, in case "break" occurred (circuit breaker executed <i>break handler</i>
* instead of <i>real-method</i>).
*
* @param exceptionClass the type of exception to throw - must be subclass of {@link BreakHandlerException}
* @param callback a callback method which returns the message for the exception
*/
public ExceptionThrowingHandler(Class<? extends BreakHandlerException> exceptionClass,
ExceptionThrowingHandlerMessageCallback<R> callback) {
this.exceptionClass = exceptionClass;
this.exceptionMessage = "";
this.messageCallback = Optional.of(callback);
}
@Override
public R onBreak(ContextAwareCircuitBreaker<R> circuitBreaker, Task<R> task, BreakStrategy<R> breakStrategy,
ExecutionContext<R> executionContext) throws BreakHandlerException {
final BreakHandlerException exceptionToThrow;
String message = null;
try {
message = buildMessage(task, executionContext);
final Constructor<? extends BreakHandlerException> exceptionClassConstructor = exceptionClass.getConstructor(
String.class);
exceptionToThrow = exceptionClassConstructor.newInstance(message);
} catch (Exception ex) {
throw new BreakHandlerException(
"Unable to throw custom exception of class: " + exceptionClass + " and message: " + message,
ex);
}
throw exceptionToThrow;
}
protected String buildMessage(Task<R> task, ExecutionContext<R> executionContext) {
if (messageCallback.isPresent()) {
return messageCallback.get().buildMessage(task, executionContext);
} else {
return this.exceptionMessage;
}
}
}