AbstractCircuitBreaker.java
package net.secodo.jcircuitbreaker.breaker.impl;
import java.util.concurrent.ConcurrentHashMap;
import net.secodo.jcircuitbreaker.breaker.CircuitBreaker;
import net.secodo.jcircuitbreaker.breaker.ContextAwareCircuitBreaker;
import net.secodo.jcircuitbreaker.breaker.execution.ExecutedTask;
import net.secodo.jcircuitbreaker.breaker.execution.ExecutionContext;
import net.secodo.jcircuitbreaker.breaker.execution.impl.DefaultExecutionContextImpl;
import net.secodo.jcircuitbreaker.breakhandler.BreakHandler;
import net.secodo.jcircuitbreaker.breakhandler.exception.BreakHandlerException;
import net.secodo.jcircuitbreaker.breakstrategy.BreakStrategy;
import net.secodo.jcircuitbreaker.exception.CircuitBreakerException;
import net.secodo.jcircuitbreaker.exception.TaskExecutionException;
import net.secodo.jcircuitbreaker.task.Task;
import net.secodo.jcircuitbreaker.util.TimeUtil;
public abstract class AbstractCircuitBreaker<R> implements CircuitBreaker<R>, ContextAwareCircuitBreaker<R> {
private final ConcurrentHashMap<String, ExecutedTask<R>> tasksInProgress;
private final TimeUtil timeUtil;
public AbstractCircuitBreaker() {
this.tasksInProgress = new ConcurrentHashMap<>();
timeUtil = new TimeUtil();
}
@Override
public <U> R execute(Task<R> task, BreakStrategy<R> breakStrategy, BreakHandler<R> breakHandler,
U userData) throws TaskExecutionException, BreakHandlerException, CircuitBreakerException {
DefaultExecutionContextImpl<R> executionContext = new DefaultExecutionContextImpl<>(tasksInProgress, userData);
return executeInContext(task, breakStrategy, breakHandler, executionContext);
}
@Override
public R executeInContext(Task<R> task, BreakStrategy<R> breakStrategy, BreakHandler<R> breakHandler,
ExecutionContext<R> executionContext)
throws TaskExecutionException, BreakHandlerException, CircuitBreakerException {
if (!breakStrategy.shouldBreak(task, executionContext)) {
// track state asynchronously - not to block by resize operation on possible resize of hashmap
R returnedValue = null;
String taskKey = "not_existing_task_key"; // prevents possible null pointer in finally clause
try {
taskKey = String.valueOf(Thread.currentThread().getId()) + "_" + task.hashCode();
final ExecutedTask<R> executedTask = new ExecutedTask<R>(task, timeUtil.getCurrentTimeMilis());
tasksInProgress.put(taskKey, executedTask);
try {
returnedValue = task.execute();
} catch (Exception ex) {
throw new TaskExecutionException("Execution of task: " + task + " failed with exception: " + ex.getMessage(),
ex);
}
} catch (TaskExecutionException ex) {
throw ex;
} catch (Exception ex) {
String taskInfo;
try {
taskInfo = task.toString();
} catch (Throwable th) {
taskInfo = "[exception while call task.toString(): " + ex.getMessage() + "]";
}
throw new CircuitBreakerException("Exception while executing an actual call to task: " + taskInfo, ex);
} finally {
tasksInProgress.remove(taskKey);
}
return returnedValue;
} else {
return breakHandler.onBreak(this, task, breakStrategy, executionContext);
}
}
}