summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Lenz <thomas.lenz@egiz.gv.at>2020-11-05 11:39:02 +0100
committerThomas Lenz <thomas.lenz@egiz.gv.at>2020-11-05 11:39:02 +0100
commit83e19359c762bd5652dfa8e2a66d7e5a0c3f2184 (patch)
treee25cc7dc770bf88b3bf5ac8cb282452572ca9ca8
parent62e36db9929c944754a3cebabcbb8227b3f8b592 (diff)
downloadEAAF-Components-83e19359c762bd5652dfa8e2a66d7e5a0c3f2184.tar.gz
EAAF-Components-83e19359c762bd5652dfa8e2a66d7e5a0c3f2184.tar.bz2
EAAF-Components-83e19359c762bd5652dfa8e2a66d7e5a0c3f2184.zip
add scheduled eviction policy to clean-up expired or old http connections from pool
-rw-r--r--eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientFactory.java168
1 files changed, 104 insertions, 64 deletions
diff --git a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientFactory.java b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientFactory.java
index 647c0636..07522b56 100644
--- a/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientFactory.java
+++ b/eaaf_core_utils/src/main/java/at/gv/egiz/eaaf/core/impl/http/HttpClientFactory.java
@@ -4,6 +4,8 @@ import java.security.KeyStore;
import java.security.Provider;
import java.util.HashMap;
import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.TimeUnit;
import javax.annotation.Nonnull;
import javax.annotation.PostConstruct;
@@ -23,6 +25,7 @@ import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
+import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
@@ -33,10 +36,12 @@ import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultRedirectStrategy;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
+import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContexts;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Scheduled;
import at.gv.egiz.eaaf.core.api.idp.IConfiguration;
import at.gv.egiz.eaaf.core.exceptions.EaafConfigurationException;
@@ -65,10 +70,10 @@ public class HttpClientFactory implements IHttpClientFactory {
public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_CONNECTION =
"client.http.connection.timeout.connection";
public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_REQUEST =
- "client.http.connection.timeout.request";
- public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_COUNT =
+ "client.http.connection.timeout.request";
+ public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_COUNT =
"client.http.connection.retry.count";
- public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_POST =
+ public static final String PROP_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_POST =
"client.http.connection.retry.post";
public static final String PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL =
"client.http.ssl.hostnameverifier.trustall";
@@ -97,9 +102,14 @@ public class HttpClientFactory implements IHttpClientFactory {
public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE = "100";
public static final String DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_COUNT = "3";
public static final String DEFAUTL_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_POST = String.valueOf(false);
+
+ public static final int DEFAULT_CLEANUP_RUNNER_TIME = 30000;
+ public static final int DEFAULT_CLEANUP_IDLE_TIME = 60;
+
private String defaultConfigurationId = null;
- private final Map<String, HttpClientBuilder> availableBuilders = new HashMap<>();
+ private final Map<String, Pair<HttpClientBuilder, HttpClientConnectionManager>>
+ availableBuilders = new HashMap<>();
/*
* (non-Javadoc)
@@ -114,7 +124,7 @@ public class HttpClientFactory implements IHttpClientFactory {
@Override
public CloseableHttpClient getHttpClient(final boolean followRedirects) {
- return availableBuilders.get(defaultConfigurationId).setRedirectStrategy(
+ return availableBuilders.get(defaultConfigurationId).getFirst().setRedirectStrategy(
buildRedirectStrategy(followRedirects)).build();
}
@@ -124,30 +134,31 @@ public class HttpClientFactory implements IHttpClientFactory {
log.trace("Build http client for: {}", config.getFriendlyName());
HttpClientBuilder builder = null;
if (availableBuilders.containsKey(config.getUuid())) {
- builder = availableBuilders.get(config.getUuid());
+ builder = availableBuilders.get(config.getUuid()).getFirst();
} else {
log.debug("Initialize new http-client builder for: {}", config.getFriendlyName());
- //validate configuration object
+ // validate configuration object
config.validate();
builder = HttpClients.custom();
-
- //inject request configuration
+
+ // inject request configuration
builder.setDefaultRequestConfig(buildDefaultRequestConfig());
injectInternalRetryHandler(builder, config);
-
- //inject basic authentication infos
+
+ // inject basic authentication infos
injectBasicAuthenticationIfRequired(builder, config);
- //inject authentication if required
+ // inject authentication if required
final LayeredConnectionSocketFactory sslConnectionFactory = getSslContext(config);
// set pool connection if required
- injectDefaultConnectionPoolIfRequired(builder, sslConnectionFactory);
+ HttpClientConnectionManager connectionManager
+ = injectConnectionManager(builder, sslConnectionFactory);
- availableBuilders.put(config.getUuid(), builder);
+ availableBuilders.put(config.getUuid(), Pair.newInstance(builder, connectionManager));
}
@@ -156,27 +167,45 @@ public class HttpClientFactory implements IHttpClientFactory {
}
- private void injectInternalRetryHandler(HttpClientBuilder builder, HttpClientConfiguration config) {
+ /**
+ * Worker that closes expired connections or connections that in idle
+ * for more than DEFAULT_CLEANUP_IDLE_TIME seconds.
+ *
+ */
+ @Scheduled(fixedDelay = DEFAULT_CLEANUP_RUNNER_TIME)
+ private void httpConnectionPoolCleaner() {
+ log.trace("Starting http connection-pool eviction policy ... ");
+ for (final Entry<String, Pair<HttpClientBuilder, HttpClientConnectionManager>> el
+ : availableBuilders.entrySet()) {
+ log.trace("Checking connections of http-client: {}", el.getKey());
+ el.getValue().getSecond().closeExpiredConnections();
+ el.getValue().getSecond().closeIdleConnections(DEFAULT_CLEANUP_IDLE_TIME, TimeUnit.SECONDS);
+
+ }
+
+ }
+
+ private void injectInternalRetryHandler(HttpClientBuilder builder, HttpClientConfiguration config) {
if (config.getHttpErrorRetryCount() > 0) {
- log.info("Set HTTP error-retry to {} for http-client: {}",
+ log.info("Set HTTP error-retry to {} for http-client: {}",
config.getHttpErrorRetryCount(), config.getFriendlyName());
builder.setRetryHandler(new EaafHttpRequestRetryHandler(
- config.getHttpErrorRetryCount(),
- config.isHttpErrorRetryPost()));
-
+ config.getHttpErrorRetryCount(),
+ config.isHttpErrorRetryPost()));
+
if (config.getServiceUnavailStrategy() != null) {
log.debug("HttpClient configuration: {} set custom ServiceUnavailableRetryStrategy: {}",
config.getFriendlyName(), config.getServiceUnavailStrategy().getClass().getName());
builder.setServiceUnavailableRetryStrategy(config.getServiceUnavailStrategy());
-
+
}
-
+
} else {
log.info("Disable HTTP error-retry for http-client: {}", config.getFriendlyName());
builder.disableAutomaticRetries();
-
+
}
-
+
}
@PostConstruct
@@ -190,8 +219,8 @@ public class HttpClientFactory implements IHttpClientFactory {
// set default request configuration
defaultHttpClientBuilder.setDefaultRequestConfig(buildDefaultRequestConfig());
injectInternalRetryHandler(defaultHttpClientBuilder, defaultHttpClientConfig);
-
- //inject http basic authentication
+
+ // inject http basic authentication
injectBasicAuthenticationIfRequired(defaultHttpClientBuilder, defaultHttpClientConfig);
// inject authentication if required
@@ -199,11 +228,13 @@ public class HttpClientFactory implements IHttpClientFactory {
getSslContext(defaultHttpClientConfig);
// set pool connection if required
- injectDefaultConnectionPoolIfRequired(defaultHttpClientBuilder, sslConnectionFactory);
+ HttpClientConnectionManager connectionManager
+ = injectConnectionManager(defaultHttpClientBuilder, sslConnectionFactory);
- //set default http client builder
+ // set default http client builder
defaultConfigurationId = defaultHttpClientConfig.getUuid();
- availableBuilders.put(defaultConfigurationId, defaultHttpClientBuilder);
+ availableBuilders.put(defaultConfigurationId,
+ Pair.newInstance(defaultHttpClientBuilder, connectionManager));
}
@@ -239,13 +270,12 @@ public class HttpClientFactory implements IHttpClientFactory {
PROP_CONFIG_CLIENT_HTTP_SSL_HOSTNAMEVERIFIER_TRUSTALL, false));
config.setHttpErrorRetryCount(Integer.parseInt(basicConfig.getBasicConfiguration(
- PROP_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_COUNT,
+ PROP_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_COUNT,
DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_COUNT)));
config.setHttpErrorRetryPost(Boolean.parseBoolean(basicConfig.getBasicConfiguration(
- PROP_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_POST,
+ PROP_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_POST,
DEFAUTL_CONFIG_CLIENT_HTTP_CONNECTION_RETRY_POST)));
-
-
+
// validate configuration object
config.validate();
@@ -280,7 +310,8 @@ public class HttpClientFactory implements IHttpClientFactory {
SSLContext sslContext = null;
if (httpClientConfig.getAuthMode().equals(HttpClientConfiguration.ClientAuthMode.SSL)) {
log.debug("Open keyStore with type: {}", httpClientConfig.getKeyStoreConfig().getKeyStoreType());
- final Pair<KeyStore, Provider> keyStore = keyStoreFactory.buildNewKeyStore(httpClientConfig.getKeyStoreConfig());
+ final Pair<KeyStore, Provider> keyStore = keyStoreFactory.buildNewKeyStore(httpClientConfig
+ .getKeyStoreConfig());
log.trace("Injecting SSL client-authentication into http client ... ");
sslContext = HttpUtils.buildSslContextWithSslClientAuthentication(keyStore,
@@ -290,7 +321,7 @@ public class HttpClientFactory implements IHttpClientFactory {
} else {
log.trace("Initializing default SSL Context ... ");
sslContext = SSLContexts.createDefault();
-
+
}
// set hostname verifier
@@ -308,48 +339,37 @@ public class HttpClientFactory implements IHttpClientFactory {
}
- private void injectDefaultConnectionPoolIfRequired(
+ @Nonnull
+ private HttpClientConnectionManager injectConnectionManager(
HttpClientBuilder builder, final LayeredConnectionSocketFactory sslConnectionFactory) {
if (basicConfig.getBasicConfigurationBoolean(PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_USE,
true)) {
- PoolingHttpClientConnectionManager pool;
-
- // set socketFactoryRegistry if SSLConnectionFactory is Set
- if (sslConnectionFactory != null) {
- final Registry<ConnectionSocketFactory> socketFactoryRegistry =
- RegistryBuilder.<ConnectionSocketFactory>create()
- .register("http", PlainConnectionSocketFactory.getSocketFactory())
- .register("https", sslConnectionFactory).build();
- log.trace("Inject SSLSocketFactory into pooled connection");
- pool = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
-
- } else {
- pool = new PoolingHttpClientConnectionManager();
-
- }
-
- pool.setDefaultMaxPerRoute(Integer.parseInt(
+ PoolingHttpClientConnectionManager connectionPool
+ = new PoolingHttpClientConnectionManager(getDefaultRegistry(sslConnectionFactory));
+ connectionPool.setDefaultMaxPerRoute(Integer.parseInt(
basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE,
DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXPERROUTE)));
- pool.setMaxTotal(Integer.parseInt(
+ connectionPool.setMaxTotal(Integer.parseInt(
basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL,
DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_POOL_MAXTOTAL)));
-
- pool.setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(Integer.parseInt(
+ connectionPool.setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(Integer.parseInt(
basicConfig.getBasicConfiguration(PROP_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET,
DEFAULT_CONFIG_CLIENT_HTTP_CONNECTION_TIMEOUT_SOCKET))
* 1000).build());
+ builder.setConnectionManager(connectionPool);
+ log.debug("Initalize http-client pool with, maxTotal: {} maxPerRoute: {}",
+ connectionPool.getMaxTotal(), connectionPool.getDefaultMaxPerRoute());
+ return connectionPool;
+
+ } else {
+ log.debug("Building http-client without Connection-Pool ... ");
+ final BasicHttpClientConnectionManager basicPool = new BasicHttpClientConnectionManager(
+ getDefaultRegistry(sslConnectionFactory));
+ builder.setConnectionManager(basicPool);
+ return basicPool;
- builder.setConnectionManager(pool);
- log.debug("Initalize http-client pool with, maxTotal: {} maxPerRoute: {}", pool.getMaxTotal(),
- pool.getDefaultMaxPerRoute());
-
- } else if (sslConnectionFactory != null) {
- log.trace("Inject SSLSocketFactory without connection pool");
- builder.setSSLSocketFactory(sslConnectionFactory);
-
}
-
+
}
private RequestConfig buildDefaultRequestConfig() {
@@ -392,5 +412,25 @@ public class HttpClientFactory implements IHttpClientFactory {
return redirectStrategy;
}
+
+ private static Registry<ConnectionSocketFactory> getDefaultRegistry(
+ final LayeredConnectionSocketFactory sslConnectionFactory) {
+ final RegistryBuilder<ConnectionSocketFactory> builder =
+ RegistryBuilder.<ConnectionSocketFactory>create()
+ .register("http", PlainConnectionSocketFactory.getSocketFactory());
+
+ if (sslConnectionFactory != null) {
+ log.trace("Inject own SSLSocketFactory into pooled connection");
+ builder.register("https", sslConnectionFactory);
+
+ } else {
+ log.trace("Inject default SSLSocketFactory into pooled connection");
+ builder.register("https", SSLConnectionSocketFactory.getSocketFactory());
+
+ }
+
+ return builder.build();
+
+ }
}