/*
* The MIT License
*
* Copyright 2015 cain.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.github.horrorho.liquiddonkey.http;
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLException;
import net.jcip.annotations.Immutable;
import net.jcip.annotations.ThreadSafe;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.protocol.HttpContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Aggressive HttpRequestRetryHandler.
*
* @author cain
*/
@Immutable
@ThreadSafe
public final class PersistentHttpRequestRetryHandler implements HttpRequestRetryHandler {
private static final Logger logger = LoggerFactory.getLogger(PersistentHttpRequestRetryHandler.class);
private final int retryCount;
private final int retryDelayMs;
private final int timeOutMs;
private final boolean requestSentRetryEnabled;
/**
* Returns a new instance.
*
* @param retryCount maximum retry count
* @param retryDelayMs retry in milliseconds
* @param timeOutMs timeout in milliseconds
* @param requestSentRetryEnabled true to retry requests that have been sent
*/
public PersistentHttpRequestRetryHandler(
int retryCount,
int retryDelayMs,
int timeOutMs,
boolean requestSentRetryEnabled) {
this.retryCount = retryCount;
this.retryDelayMs = retryDelayMs;
this.timeOutMs = timeOutMs;
this.requestSentRetryEnabled = requestSentRetryEnabled;
}
@Override
public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
logger.trace("<< retryRequest() < exception: {} executionCount: {} context: {}",
exception, executionCount, context);
boolean toRetry = doRetryRequest(exception, executionCount, context);
logger.trace(">> retryRequest() > {}", toRetry);
return toRetry;
}
boolean doRetryRequest(final IOException exception, final int executionCount, final HttpContext context) {
HttpClientContext clientContext = HttpClientContext.adapt(context);
HttpRequest request = clientContext.getRequest();
if (executionCount > retryCount) {
logger.debug("-- doRetryRequest() > {} {} > false (limit)", request.getRequestLine(), exception.toString());
return false;
}
if (exception instanceof SSLException) {
logger.debug("-- doRetryRequest() > {} {} > false (SSLException)",
request.getRequestLine(), exception.toString());
return false;
}
if (exception instanceof UnknownHostException && retryCount > 3) {
logger.debug("-- doRetryRequest() > {} {} > UnknownHostException sleep({})",
request.getRequestLine(), exception.toString(), retryDelayMs);
sleep(timeOutMs);
}
if (retryCount > 3) {
logger.debug("-- doRetryRequest() > {} {} > sleep({})",
request.getRequestLine(), exception.toString(), retryDelayMs);
sleep(timeOutMs);
}
if (!(request instanceof HttpEntityEnclosingRequest)) {
logger.debug("-- doRetryRequest() > {} {} > true (idempotent)",
request.getRequestLine(), exception.toString());
return true;
}
if (!clientContext.isRequestSent() || requestSentRetryEnabled) {
logger.debug("-- doRetryRequest() > {} {} > true (non-idempotent)",
request.getRequestLine(), exception.toString());
return true;
}
logger.debug("-- doRetryRequest() : {} >> {} > false (non-idempotent)",
request.getRequestLine(), exception.toString());
return false;
}
private void sleep(int timeMs) {
try {
TimeUnit.MILLISECONDS.sleep(timeMs);
} catch (InterruptedException ex) {
logger.warn("-- sleep() > interrupted: ", ex);
Thread.currentThread().interrupt();
}
}
}