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.execchain; 029 030import java.io.IOException; 031 032import org.apache.commons.logging.Log; 033import org.apache.commons.logging.LogFactory; 034import org.apache.http.Header; 035import org.apache.http.HttpException; 036import org.apache.http.NoHttpResponseException; 037import org.apache.http.annotation.Contract; 038import org.apache.http.annotation.ThreadingBehavior; 039import org.apache.http.client.HttpRequestRetryHandler; 040import org.apache.http.client.NonRepeatableRequestException; 041import org.apache.http.client.methods.CloseableHttpResponse; 042import org.apache.http.client.methods.HttpExecutionAware; 043import org.apache.http.client.methods.HttpRequestWrapper; 044import org.apache.http.client.protocol.HttpClientContext; 045import org.apache.http.conn.routing.HttpRoute; 046import org.apache.http.util.Args; 047 048/** 049 * Request executor in the request execution chain that is responsible 050 * for making a decision whether a request failed due to an I/O error 051 * should be re-executed. 052 * <p> 053 * Further responsibilities such as communication with the opposite 054 * endpoint is delegated to the next executor in the request execution 055 * chain. 056 * </p> 057 * 058 * @since 4.3 059 */ 060@Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL) 061public class RetryExec implements ClientExecChain { 062 063 private final Log log = LogFactory.getLog(getClass()); 064 065 private final ClientExecChain requestExecutor; 066 private final HttpRequestRetryHandler retryHandler; 067 068 public RetryExec( 069 final ClientExecChain requestExecutor, 070 final HttpRequestRetryHandler retryHandler) { 071 Args.notNull(requestExecutor, "HTTP request executor"); 072 Args.notNull(retryHandler, "HTTP request retry handler"); 073 this.requestExecutor = requestExecutor; 074 this.retryHandler = retryHandler; 075 } 076 077 @Override 078 public CloseableHttpResponse execute( 079 final HttpRoute route, 080 final HttpRequestWrapper request, 081 final HttpClientContext context, 082 final HttpExecutionAware execAware) throws IOException, HttpException { 083 Args.notNull(route, "HTTP route"); 084 Args.notNull(request, "HTTP request"); 085 Args.notNull(context, "HTTP context"); 086 final Header[] origheaders = request.getAllHeaders(); 087 for (int execCount = 1;; execCount++) { 088 try { 089 return this.requestExecutor.execute(route, request, context, execAware); 090 } catch (final IOException ex) { 091 if (execAware != null && execAware.isAborted()) { 092 this.log.debug("Request has been aborted"); 093 throw ex; 094 } 095 if (retryHandler.retryRequest(ex, execCount, context)) { 096 if (this.log.isInfoEnabled()) { 097 this.log.info("I/O exception ("+ ex.getClass().getName() + 098 ") caught when processing request to " 099 + route + 100 ": " 101 + ex.getMessage()); 102 } 103 if (this.log.isDebugEnabled()) { 104 this.log.debug(ex.getMessage(), ex); 105 } 106 if (!RequestEntityProxy.isRepeatable(request)) { 107 this.log.debug("Cannot retry non-repeatable request"); 108 throw new NonRepeatableRequestException("Cannot retry request " + 109 "with a non-repeatable request entity", ex); 110 } 111 request.setHeaders(origheaders); 112 if (this.log.isInfoEnabled()) { 113 this.log.info("Retrying request to " + route); 114 } 115 } else { 116 if (ex instanceof NoHttpResponseException) { 117 final NoHttpResponseException updatedex = new NoHttpResponseException( 118 route.getTargetHost().toHostString() + " failed to respond"); 119 updatedex.setStackTrace(ex.getStackTrace()); 120 throw updatedex; 121 } else { 122 throw ex; 123 } 124 } 125 } 126 } 127 } 128 129}