/** * Copyright 2014 Jordan Zimmerman * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.soabase.client.apache; import io.soabase.core.features.client.RequestRunner; import io.soabase.core.features.client.RetryComponents; import io.soabase.core.features.client.RequestId; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.conn.ClientConnectionManager; import org.apache.http.params.HttpParams; import org.apache.http.protocol.HttpContext; import org.apache.http.util.Args; import org.apache.http.util.EntityUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.lang.reflect.UndeclaredThrowableException; import java.net.URI; import java.net.URISyntaxException; @SuppressWarnings("deprecation") public class WrappedHttpClient implements HttpClient { private final Logger log = LoggerFactory.getLogger(getClass()); private final HttpClient implementation; private final RetryComponents retryComponents; private final RequestId.HeaderSetter<HttpRequest> headerSetter = new RequestId.HeaderSetter<HttpRequest>() { @Override public void setHeader(HttpRequest request, String header, String value) { request.addHeader(header, value); } }; public WrappedHttpClient(HttpClient implementation, RetryComponents retryComponents) { this.implementation = implementation; this.retryComponents = retryComponents; } @Override public HttpParams getParams() { return implementation.getParams(); } @Override public ClientConnectionManager getConnectionManager() { return implementation.getConnectionManager(); } @Override public HttpResponse execute(HttpUriRequest request) throws IOException { return execute(request, (HttpContext)null); } @Override public HttpResponse execute(HttpUriRequest request, HttpContext context) throws IOException { RequestRunner<HttpUriRequest> requestRunner = new RequestRunner<>(retryComponents, headerSetter, request.getURI(), request.getMethod()); while ( requestRunner.shouldContinue() ) { URI uri = requestRunner.prepareRequest(request); request = new WrappedHttpUriRequest(request, uri); try { HttpResponse response = implementation.execute(request, context); if ( requestRunner.isSuccessResponse(response.getStatusLine().getStatusCode()) ) { return response; } } catch ( IOException e ) { if ( !requestRunner.shouldBeRetried(e) ) { throw e; } } } throw new IOException("Retries expired for " + requestRunner.getOriginalUri()); } @Override public HttpResponse execute(HttpHost target, HttpRequest request) throws IOException { return execute(target, request, (HttpContext)null); } @Override public HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context) throws IOException { URI uri; try { uri = new URI(request.getRequestLine().getUri()); } catch ( URISyntaxException e ) { log.error("Bad URI: " + request.getRequestLine().getUri()); throw new IOException(e); } RequestRunner<HttpRequest> requestRunner = new RequestRunner<>(retryComponents, headerSetter, uri, request.getRequestLine().getMethod()); while ( requestRunner.shouldContinue() ) { uri = requestRunner.prepareRequest(request); request = new WrappedHttpRequest(request, uri); target = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme()); try { HttpResponse response = implementation.execute(target, request, context); if ( requestRunner.isSuccessResponse(response.getStatusLine().getStatusCode()) ) { return response; } } catch ( IOException e ) { if ( !requestRunner.shouldBeRetried(e) ) { throw e; } } } throw new IOException("Retries expired for " + requestRunner.getOriginalUri()); } @Override public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler) throws IOException { return internalExecute(request, null, null, responseHandler, null); } @Override public <T> T execute(HttpUriRequest request, ResponseHandler<? extends T> responseHandler, HttpContext context) throws IOException { return internalExecute(request, null, null, responseHandler, context); } @Override public <T> T execute(HttpHost target, HttpRequest request, ResponseHandler<? extends T> responseHandler) throws IOException { return internalExecute(null, target, request, responseHandler, null); } @Override public <T> T execute(HttpHost target, HttpRequest request, ResponseHandler<? extends T> responseHandler, HttpContext context) throws IOException { return internalExecute(null, target, request, responseHandler, context); } // mostly copied from CloseableHttpClient.execute() private <T> T internalExecute(final HttpUriRequest uriRequest, final HttpHost target, final HttpRequest request, final ResponseHandler<? extends T> responseHandler, final HttpContext context) throws IOException { Args.notNull(responseHandler, "Response handler"); final HttpResponse response = (uriRequest != null) ? execute(uriRequest, context) : execute(target, request, context); final T result; try { result = responseHandler.handleResponse(response); } catch (final Exception t) { final HttpEntity entity = response.getEntity(); try { EntityUtils.consume(entity); } catch (final Exception t2) { // Log this exception. The original exception is more // important and will be thrown to the caller. log.warn("Error consuming content after an exception.", t2); } if (t instanceof RuntimeException) { throw (RuntimeException) t; } //noinspection ConstantConditions if (t instanceof IOException) { throw (IOException) t; } throw new UndeclaredThrowableException(t); } // Handling the response was successful. Ensure that the content has // been fully consumed. final HttpEntity entity = response.getEntity(); EntityUtils.consume(entity); return result; } }