package net.greencoding.thysdrus.circuitbreaker.core.handler;
import net.greencoding.thysdrus.circuitbreaker.core.CircuitBreakerRegistry;
import net.greencoding.thysdrus.circuitbreaker.core.CircuitBreakerRegistryFactory;
import net.greencoding.thysdrus.circuitbreaker.core.handler.exception.CircuitBreakerMethodExecutionException;
import net.greencoding.thysdrus.circuitbreaker.core.model.CircuitBreaker;
import net.greencoding.thysdrus.circuitbreaker.core.model.CircuitBreakerStatus;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Nabil Ben Said (nabil.ben.said@gmail.com)
*
*/
public class DefaultCircuitBreakerHandler implements CircuitBreakerHandler {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private CircuitBreakerRegistry registry = CircuitBreakerRegistryFactory.getSingelton();
private static CircuitBreakerHandler singelton = null;
public static CircuitBreakerHandler getSingleton() {
if (singelton == null){
singelton = new DefaultCircuitBreakerHandler();
}
return singelton;
}
/*
* (non-Javadoc)
* @see net.greencoding.thysdrus.circuitbreaker.core.CircuitBreakerHandler#handleMethodInvocation(org.aspectj.lang.ProceedingJoinPoint, net.greencoding.thysdrus.circuitbreaker.core.model.CircuitBreaker)
*/
@Override
public MethodInvocationResult handleMethodInvocation(final ProceedingJoinPoint pjp, final CircuitBreaker circuitBreaker) {
if (circuitBreaker == null) {
throw new IllegalStateException("CB is null");
}
String circuitBreakerKey = circuitBreaker.getCircuitBreakerKey();
if (! registry.isRegistered(circuitBreakerKey)) {
registry.registerCircuitBreaker(circuitBreaker);
}
return handleMethodInvocation(pjp, circuitBreakerKey);
}
private void proceed(final ProceedingJoinPoint pjp, final String circuitBreakerKey, final MethodInvocationResult methodInvocationResult, boolean inHalfOpen) {
try {
methodInvocationResult.setReturnObject(proceed(pjp));
methodInvocationResult.setSuccessfullyTerminated(true);
if (inHalfOpen) {
registry.releaseHalfOpenLock(circuitBreakerKey, CircuitBreakerStatus.CLOSED);
}
logger.info("succesfully executed CB-key: {}, MethodInvocationResult: {}", circuitBreakerKey, methodInvocationResult);
} catch (CircuitBreakerMethodExecutionException e) {
methodInvocationResult.setCause(e.getCause());
boolean isFailure = registry.handleMethodInvocationException(circuitBreakerKey, e.getCause(), inHalfOpen);
if (isFailure) {
methodInvocationResult.setFailureWhileExecution(true);
} else {
methodInvocationResult.setSuccessfullyTerminated(true);
}
logger.info("failure while execution: CB-key: {}, MethodInvocationResult: {}", circuitBreakerKey, methodInvocationResult);
}
}
private Object proceed(final ProceedingJoinPoint pjp) throws CircuitBreakerMethodExecutionException {
try {
return pjp.proceed();
} catch (Throwable t) {
logger.warn("Exception while method execution: {}", pjp.getSignature().toLongString());
throw new CircuitBreakerMethodExecutionException(t);
}
}
/*
* (non-Javadoc)
* @see net.greencoding.thysdrus.circuitbreaker.core.handler.CircuitBreakerHandler#handleMethodInvocation(org.aspectj.lang.ProceedingJoinPoint, java.lang.String)
*/
@Override
public MethodInvocationResult handleMethodInvocation(ProceedingJoinPoint pjp, String circuitBreakerKey) {
MethodInvocationResult methodInvocationResult = new MethodInvocationResult();
final CircuitBreakerStatus status = registry.getCircuitBreakerStatus(circuitBreakerKey);
if (CircuitBreakerStatus.CLOSED.equals(status)){
logger.info("CB {} is in CLOSED state - execute method and track the result", circuitBreakerKey);
proceed(pjp, circuitBreakerKey, methodInvocationResult, false);
}
if (CircuitBreakerStatus.HALF_OPEN.equals(status)) {
logger.info("CB {} is in HALF-OPEN State - skip method execution", circuitBreakerKey);
methodInvocationResult.setSuccessfullyTerminated(false);
methodInvocationResult.setBlockedByCircuitBreaker(true);
methodInvocationResult.setReturnObject(null);
methodInvocationResult.setCircuitBreakerStatus(status);
}
if (CircuitBreakerStatus.OPEN.equals(status)) {
// 1. check half-open condition
// 2. try get lock
// 3. decide if you can proceed with method execution or return
boolean inHalfOpen = false;
if (registry.halfOpenConditionSatisfied(circuitBreakerKey)) {
inHalfOpen = registry.tryHalfOpenLock(circuitBreakerKey);
}
if (inHalfOpen) {
proceed(pjp, circuitBreakerKey, methodInvocationResult, false);
} else {
methodInvocationResult.setSuccessfullyTerminated(false);
methodInvocationResult.setBlockedByCircuitBreaker(true);
methodInvocationResult.setReturnObject(null);
methodInvocationResult.setCircuitBreakerStatus(status);
logger.info("CB {} is OPEN. Method invocation blocked.", circuitBreakerKey);
}
}
return methodInvocationResult;
}
@Override
public void registerCircuitBreaker(CircuitBreaker circuitBreaker) {
registry.registerCircuitBreaker(circuitBreaker);
}
}