Skip to content

Commit

Permalink
Polishing.
Browse files Browse the repository at this point in the history
Related: #1164.
  • Loading branch information
gregturn committed May 12, 2023
1 parent 05daff5 commit 86e5d92
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@

import java.net.URI;
import java.net.URISyntaxException;
import java.time.Duration;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.apache.hc.client5.http.HttpRoute;
import org.apache.hc.client5.http.auth.AuthScope;
Expand All @@ -31,25 +31,28 @@
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.core5.http.HttpHost;

import org.apache.hc.core5.util.Timeout;
import org.springframework.beans.factory.FactoryBean;

/**
* {@code FactoryBean} to set up a <a href="http://hc.apache.org/httpcomponents-client">Apache
* CloseableHttpClient</a>
* {@link FactoryBean} to set up a {@link CloseableHttpClient} using HttpComponents HttpClient 5.
*
* @see http://hc.apache.org/httpcomponents-client
* @author Lars Uffmann
* @since 4.0.5
*/
public class HttpComponents5ClientFactory implements FactoryBean<CloseableHttpClient> {
private static final int DEFAULT_CONNECTION_TIMEOUT_MILLISECONDS = (60 * 1000);

private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT_MILLISECONDS;
private static final int DEFAULT_READ_TIMEOUT_MILLISECONDS = (60 * 1000);
private static final Duration DEFAULT_CONNECTION_TIMEOUT = Duration.ofSeconds(60);

private static final Duration DEFAULT_READ_TIMEOUT = Duration.ofSeconds(60);

private Duration connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;

private int readTimeout = DEFAULT_READ_TIMEOUT_MILLISECONDS;
private Duration readTimeout = DEFAULT_READ_TIMEOUT;

private int maxTotalConnections = -1;

private AuthScope authScope = null;

private Credentials credentials = null;
Expand Down Expand Up @@ -86,24 +89,28 @@ public void setAuthScope(AuthScope authScope) {
/**
* Sets the timeout until a connection is established. A value of 0 means <em>never</em> timeout.
*
* @param timeout the timeout value in milliseconds
* @param timeout the timeout value
*/
public void setConnectionTimeout(int timeout) {
if (timeout < 0) {
public void setConnectionTimeout(Duration timeout) {

if (timeout.isNegative()) {
throw new IllegalArgumentException("timeout must be a non-negative value");
}

this.connectionTimeout = timeout;
}

/**
* Set the socket read timeout for the underlying HttpClient. A value of 0 means <em>never</em> timeout.
*
* @param timeout the timeout value in milliseconds
* @param timeout the timeout value
*/
public void setReadTimeout(int timeout) {
if (timeout < 0) {
public void setReadTimeout(Duration timeout) {

if (timeout.isNegative()) {
throw new IllegalArgumentException("timeout must be a non-negative value");
}

this.readTimeout = timeout;
}

Expand All @@ -114,9 +121,11 @@ public void setReadTimeout(int timeout) {
* @see PoolingHttpClientConnectionManager...
*/
public void setMaxTotalConnections(int maxTotalConnections) {

if (maxTotalConnections <= 0) {
throw new IllegalArgumentException("maxTotalConnections must be a positive value");
}

this.maxTotalConnections = maxTotalConnections;
}

Expand All @@ -142,14 +151,14 @@ public void setMaxConnectionsPerHost(Map<String, String> maxConnectionsPerHost)
void applyMaxConnectionsPerHost(PoolingHttpClientConnectionManager connectionManager) throws URISyntaxException {

for (Map.Entry<String, String> entry : maxConnectionsPerHost.entrySet()) {

URI uri = new URI(entry.getKey());
HttpHost host = new HttpHost(uri.getScheme(), uri.getHost(), getPort(uri));
final HttpRoute route;

if (uri.getScheme().equals("https")) {
route = new HttpRoute(host, null, true);
}
else {
} else {
route = new HttpRoute(host);
}
int max = Integer.parseInt(entry.getValue());
Expand All @@ -158,14 +167,17 @@ void applyMaxConnectionsPerHost(PoolingHttpClientConnectionManager connectionMan
}

static int getPort(URI uri) {

if (uri.getPort() == -1) {

if ("https".equalsIgnoreCase(uri.getScheme())) {
return 443;
}
if ("http".equalsIgnoreCase(uri.getScheme())) {
return 80;
}
}

return uri.getPort();
}

Expand All @@ -176,34 +188,38 @@ public boolean isSingleton() {

@Override
public CloseableHttpClient getObject() throws Exception {
PoolingHttpClientConnectionManagerBuilder connectionManagerBuilder = PoolingHttpClientConnectionManagerBuilder.create();

PoolingHttpClientConnectionManagerBuilder connectionManagerBuilder = PoolingHttpClientConnectionManagerBuilder
.create();

if (this.maxTotalConnections != -1) {
connectionManagerBuilder.setMaxConnTotal(this.maxTotalConnections);
}

if (null != this.connectionManagerBuilderCustomizer) {
if (this.connectionManagerBuilderCustomizer != null) {
this.connectionManagerBuilderCustomizer.customize(connectionManagerBuilder);
}

this.connectionManager = connectionManagerBuilder.build();

applyMaxConnectionsPerHost(connectionManager);

RequestConfig.Builder requestConfigBuilder = RequestConfig.custom()
.setConnectTimeout(connectionTimeout, TimeUnit.MILLISECONDS)
.setResponseTimeout(readTimeout, TimeUnit.MILLISECONDS);
RequestConfig.Builder requestConfigBuilder = RequestConfig.custom() //
.setConnectionRequestTimeout(Timeout.of(connectionTimeout)) //
.setResponseTimeout(Timeout.of(readTimeout));

HttpClientBuilder httpClientBuilder = HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfigBuilder.build())
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create() //
.setDefaultRequestConfig(requestConfigBuilder.build()) //
.setConnectionManager(connectionManager);

if (null != credentials && null != authScope) {
if (credentials != null && authScope != null) {

BasicCredentialsProvider basicCredentialsProvider = new BasicCredentialsProvider();
basicCredentialsProvider.setCredentials(authScope, credentials);
httpClientBuilder.setDefaultCredentialsProvider(basicCredentialsProvider);
}

if (null != this.clientBuilderCustomizer) {
if (this.clientBuilderCustomizer != null) {
clientBuilderCustomizer.customize(httpClientBuilder);
}

Expand All @@ -223,7 +239,8 @@ public void setClientBuilderCustomizer(HttpClientBuilderCustomizer clientBuilder
this.clientBuilderCustomizer = clientBuilderCustomizer;
}

public void setConnectionManagerBuilderCustomizer(PoolingHttpClientConnectionManagerBuilderCustomizer connectionManagerBuilderCustomizer) {
public void setConnectionManagerBuilderCustomizer(
PoolingHttpClientConnectionManagerBuilderCustomizer connectionManagerBuilderCustomizer) {
this.connectionManagerBuilderCustomizer = connectionManagerBuilderCustomizer;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,19 @@
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.classic.methods.HttpPost;
import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpResponse;
import org.apache.hc.core5.http.NameValuePair;
import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.protocol.HttpContext;

import org.springframework.util.Assert;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.transport.WebServiceConnection;

/**
* Implementation of {@link WebServiceConnection} that is based on Apache HttpClient. Exposes a {@link org.apache.hc.client5.http.classic.methods.HttpPost} and
* {@link org.apache.hc.core5.http.HttpResponse
* Implementation of {@link WebServiceConnection} that is based on Apache HttpClient 5. Exposes a {@link HttpPost} and
* {@link HttpResponse}.
*
* @author Alan Stewart
* @author Barry Pitman
Expand All @@ -63,8 +62,10 @@ public class HttpComponents5Connection extends AbstractHttpSenderConnection {
private ByteArrayOutputStream requestBuffer;

protected HttpComponents5Connection(HttpClient httpClient, HttpPost httpPost, HttpContext httpContext) {

Assert.notNull(httpClient, "httpClient must not be null");
Assert.notNull(httpPost, "httpPost must not be null");

this.httpClient = httpClient;
this.httpPost = httpPost;
this.httpContext = httpContext;
Expand All @@ -80,8 +81,9 @@ public HttpResponse getHttpResponse() {

@Override
public void onClose() throws IOException {
//XXX:

if (httpResponse instanceof ClassicHttpResponse response) {

if (response.getEntity() != null) {
EntityUtils.consume(response.getEntity());
}
Expand Down Expand Up @@ -117,9 +119,10 @@ protected OutputStream getRequestOutputStream() throws IOException {

@Override
protected void onSendAfterWrite(WebServiceMessage message) throws IOException {
//XXX

httpPost.setEntity(new ByteArrayEntity(requestBuffer.toByteArray(), null));
requestBuffer = null;

if (httpContext != null) {
httpResponse = httpClient.execute(httpPost, httpContext);
} else {
Expand All @@ -143,8 +146,9 @@ protected String getResponseMessage() throws IOException {

@Override
protected long getResponseContentLength() throws IOException {
//XXX:

if (httpResponse instanceof ClassicHttpResponse response) {

HttpEntity entity = response.getEntity();
if (entity != null) {
return entity.getContentLength();
Expand All @@ -155,32 +159,31 @@ protected long getResponseContentLength() throws IOException {

@Override
protected InputStream getRawResponseInputStream() throws IOException {

if (httpResponse instanceof ClassicHttpResponse response) {

HttpEntity entity = response.getEntity();
if (entity != null) {
return entity.getContent();
}
}

throw new IllegalStateException("Response has no enclosing response entity, cannot create input stream");
}

@Override
public Iterator<String> getResponseHeaderNames() throws IOException {
Header[] headers = httpResponse.getHeaders();
String[] names = new String[headers.length];
for (int i = 0; i < headers.length; i++) {
names[i] = headers[i].getName();
}
return Arrays.asList(names).iterator();

return Arrays.stream(httpResponse.getHeaders()) //
.map(NameValuePair::getName) //
.iterator();
}

@Override
public Iterator<String> getResponseHeaders(String name) throws IOException {
Header[] headers = httpResponse.getHeaders(name);
String[] values = new String[headers.length];
for (int i = 0; i < headers.length; i++) {
values[i] = headers[i].getValue();
}
return Arrays.asList(values).iterator();

return Arrays.stream(httpResponse.getHeaders(name)) //
.map(NameValuePair::getValue) //
.iterator();
}
}
Loading

0 comments on commit 86e5d92

Please sign in to comment.