001/* 002 * ==================================================================== 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, 014 * software distributed under the License is distributed on an 015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 016 * KIND, either express or implied. See the License for the 017 * specific language governing permissions and limitations 018 * under the License. 019 * ==================================================================== 020 * 021 * This software consists of voluntary contributions made by many 022 * individuals on behalf of the Apache Software Foundation. For more 023 * information on the Apache Software Foundation, please see 024 * <http://www.apache.org/>. 025 * 026 */ 027 028package org.apache.http.impl.client; 029 030import java.io.IOException; 031import java.io.InterruptedIOException; 032import java.net.ConnectException; 033import java.net.UnknownHostException; 034import java.util.Arrays; 035import java.util.Collection; 036import java.util.HashSet; 037import java.util.Set; 038 039import javax.net.ssl.SSLException; 040 041import org.apache.http.HttpEntityEnclosingRequest; 042import org.apache.http.HttpRequest; 043import org.apache.http.annotation.Contract; 044import org.apache.http.annotation.ThreadingBehavior; 045import org.apache.http.client.HttpRequestRetryHandler; 046import org.apache.http.client.methods.HttpUriRequest; 047import org.apache.http.client.protocol.HttpClientContext; 048import org.apache.http.protocol.HttpContext; 049import org.apache.http.util.Args; 050 051/** 052 * The default {@link HttpRequestRetryHandler} used by request executors. 053 * 054 * @since 4.0 055 */ 056@Contract(threading = ThreadingBehavior.IMMUTABLE) 057public class DefaultHttpRequestRetryHandler implements HttpRequestRetryHandler { 058 059 public static final DefaultHttpRequestRetryHandler INSTANCE = new DefaultHttpRequestRetryHandler(); 060 061 /** the number of times a method will be retried */ 062 private final int retryCount; 063 064 /** Whether or not methods that have successfully sent their request will be retried */ 065 private final boolean requestSentRetryEnabled; 066 067 private final Set<Class<? extends IOException>> nonRetriableClasses; 068 069 /** 070 * Create the request retry handler using the specified IOException classes 071 * 072 * @param retryCount how many times to retry; 0 means no retries 073 * @param requestSentRetryEnabled true if it's OK to retry requests that have been sent 074 * @param clazzes the IOException types that should not be retried 075 * @since 4.3 076 */ 077 protected DefaultHttpRequestRetryHandler( 078 final int retryCount, 079 final boolean requestSentRetryEnabled, 080 final Collection<Class<? extends IOException>> clazzes) { 081 super(); 082 this.retryCount = retryCount; 083 this.requestSentRetryEnabled = requestSentRetryEnabled; 084 this.nonRetriableClasses = new HashSet<Class<? extends IOException>>(); 085 for (final Class<? extends IOException> clazz: clazzes) { 086 this.nonRetriableClasses.add(clazz); 087 } 088 } 089 090 /** 091 * Create the request retry handler using the following list of 092 * non-retriable IOException classes: <br> 093 * <ul> 094 * <li>InterruptedIOException</li> 095 * <li>UnknownHostException</li> 096 * <li>ConnectException</li> 097 * <li>SSLException</li> 098 * </ul> 099 * @param retryCount how many times to retry; 0 means no retries 100 * @param requestSentRetryEnabled true if it's OK to retry non-idempotent requests that have been sent 101 */ 102 @SuppressWarnings("unchecked") 103 public DefaultHttpRequestRetryHandler(final int retryCount, final boolean requestSentRetryEnabled) { 104 this(retryCount, requestSentRetryEnabled, Arrays.asList( 105 InterruptedIOException.class, 106 UnknownHostException.class, 107 ConnectException.class, 108 SSLException.class)); 109 } 110 111 /** 112 * Create the request retry handler with a retry count of 3, requestSentRetryEnabled false 113 * and using the following list of non-retriable IOException classes: <br> 114 * <ul> 115 * <li>InterruptedIOException</li> 116 * <li>UnknownHostException</li> 117 * <li>ConnectException</li> 118 * <li>SSLException</li> 119 * </ul> 120 */ 121 public DefaultHttpRequestRetryHandler() { 122 this(3, false); 123 } 124 /** 125 * Used {@code retryCount} and {@code requestSentRetryEnabled} to determine 126 * if the given method should be retried. 127 */ 128 @Override 129 public boolean retryRequest( 130 final IOException exception, 131 final int executionCount, 132 final HttpContext context) { 133 Args.notNull(exception, "Exception parameter"); 134 Args.notNull(context, "HTTP context"); 135 if (executionCount > this.retryCount) { 136 // Do not retry if over max retry count 137 return false; 138 } 139 if (this.nonRetriableClasses.contains(exception.getClass())) { 140 return false; 141 } else { 142 for (final Class<? extends IOException> rejectException : this.nonRetriableClasses) { 143 if (rejectException.isInstance(exception)) { 144 return false; 145 } 146 } 147 } 148 final HttpClientContext clientContext = HttpClientContext.adapt(context); 149 final HttpRequest request = clientContext.getRequest(); 150 151 if(requestIsAborted(request)){ 152 return false; 153 } 154 155 if (handleAsIdempotent(request)) { 156 // Retry if the request is considered idempotent 157 return true; 158 } 159 160 if (!clientContext.isRequestSent() || this.requestSentRetryEnabled) { 161 // Retry if the request has not been sent fully or 162 // if it's OK to retry methods that have been sent 163 return true; 164 } 165 // otherwise do not retry 166 return false; 167 } 168 169 /** 170 * @return {@code true} if this handler will retry methods that have 171 * successfully sent their request, {@code false} otherwise 172 */ 173 public boolean isRequestSentRetryEnabled() { 174 return requestSentRetryEnabled; 175 } 176 177 /** 178 * @return the maximum number of times a method will be retried 179 */ 180 public int getRetryCount() { 181 return retryCount; 182 } 183 184 /** 185 * @since 4.2 186 */ 187 protected boolean handleAsIdempotent(final HttpRequest request) { 188 return !(request instanceof HttpEntityEnclosingRequest); 189 } 190 191 /** 192 * @since 4.2 193 * 194 * @deprecated (4.3) 195 */ 196 @Deprecated 197 protected boolean requestIsAborted(final HttpRequest request) { 198 HttpRequest req = request; 199 if (request instanceof RequestWrapper) { // does not forward request to original 200 req = ((RequestWrapper) request).getOriginal(); 201 } 202 return (req instanceof HttpUriRequest && ((HttpUriRequest)req).isAborted()); 203 } 204 205}