Created
September 7, 2016 00:46
-
-
Save zkendall/a7a0f796687b8360d045563c2f75be1c to your computer and use it in GitHub Desktop.
Factory for creating a Spring RetryTemplate, primarily for use for HTTP calls such as with Spring's RestTemplate.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import org.apache.http.conn.ConnectTimeoutException; | |
import org.apache.http.conn.HttpHostConnectException; | |
import org.slf4j.Logger; | |
import org.slf4j.LoggerFactory; | |
import org.springframework.retry.RetryCallback; | |
import org.springframework.retry.RetryContext; | |
import org.springframework.retry.RetryListener; | |
import org.springframework.retry.RetryPolicy; | |
import org.springframework.retry.backoff.ExponentialBackOffPolicy; | |
import org.springframework.retry.policy.SimpleRetryPolicy; | |
import org.springframework.retry.support.RetryTemplate; | |
import org.springframework.web.client.HttpServerErrorException; | |
import java.util.HashMap; | |
import java.util.Map; | |
public class RetryTemplateFactory { | |
private static final Logger DEFAULT_LOGGER = LoggerFactory.getLogger(RetryTemplate.class); | |
static final Class<? extends Throwable>[] SERVER_RETRYABLES = new Class[]{ | |
HttpServerErrorException.class, | |
HttpHostConnectException.class, | |
ConnectTimeoutException.class | |
}; | |
/** | |
* Retries HttpServerErrorExceptions, HttpHostConnectException, ConnectTimeoutException. | |
*/ | |
public static RetryTemplate serverErrorRetry(int initialInterval, int multiplier, | |
int maxInterval, int maxAttempts, Logger logger) { | |
return build(initialInterval, multiplier, maxInterval, maxAttempts, logger, SERVER_RETRYABLES); | |
} | |
/** | |
* Build Retry template for specified throwables. | |
*/ | |
public static RetryTemplate build(int initialInterval, int multiplier, | |
int maxInterval, int maxAttempts, Logger logger, | |
Class<? extends Throwable>... throwables) { | |
SimpleRetryPolicy policy = new SimpleRetryPolicy(maxAttempts, throwableTrueMap(throwables), true); | |
final RetryTemplate template = buildRetry(policy, initialInterval, multiplier, maxInterval); | |
if (logger == null) { | |
logger = DEFAULT_LOGGER; | |
} | |
registerLoggingListener(template, logger); | |
return template; | |
} | |
static RetryTemplate buildRetry(RetryPolicy retryPolicy, int initialInterval, int multiplier, int maxInterval) { | |
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy(); | |
backOffPolicy.setInitialInterval(initialInterval); | |
backOffPolicy.setMultiplier(multiplier); | |
backOffPolicy.setMaxInterval(maxInterval); | |
RetryTemplate template = new RetryTemplate(); | |
template.setRetryPolicy(retryPolicy); | |
template.setBackOffPolicy(backOffPolicy); | |
return template; | |
} | |
/** | |
* On Error log exception. OnClose Log success/fail so that logs tell complete story. | |
*/ | |
static void registerLoggingListener(RetryTemplate retryTemplate, Logger log) { | |
final RetryListener retryListener = new RetryListener() { | |
@Override | |
public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) { | |
// true == do handle; false == abort | |
return true; | |
} | |
@Override | |
public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, | |
Throwable lastException) { | |
if (lastException == null) { | |
if (context.getRetryCount() > 10) { | |
log.info("Retry finally succeeded! You go glen coco!"); | |
} else if (context.getRetryCount() > 0) { | |
log.info("Retry succeeded! Hooray."); | |
} | |
// else success on first try; don't log. | |
} else { | |
log.error("Finished retrying without success."); | |
} | |
} | |
@Override | |
public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, | |
Throwable throwable) { | |
log.error("Error on attempt #{}.", context.getRetryCount(), throwable); | |
} | |
}; | |
retryTemplate.registerListener(retryListener); | |
} | |
private static Map throwableTrueMap(Class<? extends Throwable>... items) { | |
Map<Class<? extends Throwable>, Boolean> map = new HashMap<>(); | |
for (Class<? extends Throwable> throwable : items) { | |
map.put(throwable, true); | |
} | |
return map; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment