package com.opower.rest.client.generator.hystrix;
import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.netflix.hystrix.exception.HystrixBadRequestException;
import com.opower.rest.client.generator.core.BaseClientResponse;
import com.opower.rest.client.generator.extractors.ClientErrorHandler;
import java.lang.reflect.Method;
import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.opower.rest.client.generator.util.HttpResponseCodes.SC_BAD_REQUEST;
/**
* Certain responses should be treated as HystrixBadRequestExceptions. By default only requests with a response
* code of 400 will be handled this way. To change that behavior, just specify new criteria for any method
* in your resource interface.
*
* @author chris.phillips
*/
public class HystrixClientErrorHandler implements ClientErrorHandler {
static final BadRequestCriteria DEFAULT_BAD_REQUEST_CRITERIA = new BadRequestCriteria() {
@Override
public boolean apply(BaseClientResponse response, Exception exception) {
return response != null && response.getStatus() == SC_BAD_REQUEST;
}
};
private final ClientErrorHandler clientErrorHandler;
private final Map<Method, ? extends BadRequestCriteria> badRequestCriteriaMap;
/**
* Creates an instance based on the provided ClientErrorHandler and bad request criteria.
* @param badRequestCriteriaMap the bad request criteria to apply for each method
* @param clientErrorHandler the ClientErrorHandler to wrap
*/
public HystrixClientErrorHandler(Map<Method, ? extends BadRequestCriteria> badRequestCriteriaMap,
ClientErrorHandler clientErrorHandler) {
this.clientErrorHandler = checkNotNull(clientErrorHandler);
this.badRequestCriteriaMap = checkNotNull(badRequestCriteriaMap);
}
@Override
public void clientErrorHandling(Method method, BaseClientResponse clientResponse, RuntimeException e) {
try {
this.clientErrorHandler.clientErrorHandling(method, clientResponse, e);
checkForBadRequest(method, clientResponse, e);
} catch (Exception ex) {
checkForBadRequest(method, clientResponse, ex);
}
}
private void checkForBadRequest(Method method, BaseClientResponse clientResponse, Exception ex) {
BadRequestCriteria criteria;
if (method != null) {
criteria = Optional.fromNullable(this.badRequestCriteriaMap.get(method)).or(DEFAULT_BAD_REQUEST_CRITERIA);
} else {
criteria = DEFAULT_BAD_REQUEST_CRITERIA;
}
if (criteria.apply(clientResponse, ex) || (clientResponse != null && clientResponse.isSuccessful())) {
throw new HystrixBadRequestException("Bad Request", ex);
} else {
Throwables.propagate(ex);
}
}
/**
* This method is for testing.
* @return the map of method -> BadRequestCriteria
*/
Map<Method, ? extends BadRequestCriteria> getCriteriaMap() {
return this.badRequestCriteriaMap;
}
/**
* Defines which responses should be NOT trigger the hystrix circuit breaker.
*/
public interface BadRequestCriteria {
/**
* Check the response and Exception to determine whether or not to wrap the Exception in a
* HystrixBadRequestException. When this method returns true, the Exception will be wrapped.
* @param response the response to check
* @param exception the exception to check
* @return true if the Exception should be wrapped in a HystrixBadRequestException
*/
boolean apply(BaseClientResponse response, Exception exception);
}
}