
org.dspace.service.impl.HttpConnectionPoolService Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of dspace-api Show documentation
Show all versions of dspace-api Show documentation
DSpace core data model and service APIs.
The newest version!
/**
* The contents of this file are subject to the license and copyright
* detailed in the LICENSE and NOTICE files at the root of the source
* tree and available online at
*
* http://www.dspace.org/license/
*/
package org.dspace.service.impl;
import java.util.concurrent.TimeUnit;
import jakarta.annotation.PostConstruct;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.inject.Singleton;
import org.apache.http.HeaderElement;
import org.apache.http.HeaderElementIterator;
import org.apache.http.HttpResponse;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicHeaderElementIterator;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
import org.dspace.services.ConfigurationService;
/**
* Factory for HTTP clients sharing a pool of connections.
*
* You may create multiple pools. Each is identified by a configuration
* "prefix" (passed to the constructor) which is used to create names of
* properties which will configure the pool. The properties are:
*
*
* - PREFIX.client.keepAlive
* - Default keep-alive time for open connections, in milliseconds
* - PREFIX.client.maxTotalConnections
* - maximum open connections
* - PREFIX.client.maxPerRoute
* - maximum open connections per service instance
* - PREFIX.client.timeToLive
* - maximum lifetime of a pooled connection, in seconds
*
*
* @author Mark H. Wood
*/
@Named
@Singleton
public class HttpConnectionPoolService {
@Inject
ConfigurationService configurationService;
/** Configuration properties will begin with this string. */
private final String configPrefix;
/** Maximum number of concurrent pooled connections. */
private static final int DEFAULT_MAX_TOTAL_CONNECTIONS = 20;
/** Maximum number of concurrent pooled connections per route. */
private static final int DEFAULT_MAX_PER_ROUTE = 15;
/** Keep connections open at least this long, if the response did not
* specify: milliseconds
*/
private static final int DEFAULT_KEEPALIVE = 5 * 1000;
/** Pooled connection maximum lifetime: seconds */
private static final int DEFAULT_TTL = 10 * 60;
/** Clean up stale connections this often: milliseconds */
private static final int CHECK_INTERVAL = 1000;
/** Connection idle if unused for this long: seconds */
private static final int IDLE_INTERVAL = 30;
private PoolingHttpClientConnectionManager connManager;
private final ConnectionKeepAliveStrategy keepAliveStrategy
= new KeepAliveStrategy();
/**
* Construct a pool for a given set of configuration properties.
*
* @param configPrefix Configuration property names will begin with this.
*/
public HttpConnectionPoolService(String configPrefix) {
this.configPrefix = configPrefix;
}
@PostConstruct
protected void init() {
connManager = new PoolingHttpClientConnectionManager(
configurationService.getIntProperty(configPrefix + ".client.timeToLive", DEFAULT_TTL),
TimeUnit.SECONDS);
connManager.setMaxTotal(configurationService.getIntProperty(
configPrefix + ".client.maxTotalConnections", DEFAULT_MAX_TOTAL_CONNECTIONS));
connManager.setDefaultMaxPerRoute(
configurationService.getIntProperty(configPrefix + ".client.maxPerRoute",
DEFAULT_MAX_PER_ROUTE));
Thread connectionMonitor = new IdleConnectionMonitorThread(connManager);
connectionMonitor.setDaemon(true);
connectionMonitor.start();
}
/**
* Create an HTTP client which uses a pooled connection.
*
* @return the client.
*/
public CloseableHttpClient getClient() {
CloseableHttpClient httpClient = HttpClientBuilder.create()
.setKeepAliveStrategy(keepAliveStrategy)
.setConnectionManager(connManager)
.build();
return httpClient;
}
/**
* A connection keep-alive strategy that obeys the Keep-Alive header and
* applies a default if none is given.
*
* Swiped from https://www.baeldung.com/httpclient-connection-management
*/
public class KeepAliveStrategy
implements ConnectionKeepAliveStrategy {
@Override
public long getKeepAliveDuration(HttpResponse response,
HttpContext context) {
HeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator(HTTP.CONN_KEEP_ALIVE));
while (it.hasNext()) {
HeaderElement he = it.nextElement();
String name = he.getName();
String value = he.getValue();
if (value != null && "timeout".equalsIgnoreCase(name)) {
return Long.parseLong(value) * 1000;
}
}
// If server did not request keep-alive, use configured value.
return configurationService.getIntProperty(configPrefix + ".client.keepAlive",
DEFAULT_KEEPALIVE);
}
}
/**
* Clean up stale connections.
*
* Swiped from https://www.baeldung.com/httpclient-connection-management
*/
public class IdleConnectionMonitorThread
extends Thread {
private final HttpClientConnectionManager connMgr;
private volatile boolean shutdown;
/**
* Constructor.
*
* @param connMgr the manager to be monitored.
*/
public IdleConnectionMonitorThread(
PoolingHttpClientConnectionManager connMgr) {
super();
this.connMgr = connMgr;
}
@Override
public void run() {
try {
while (!shutdown) {
synchronized (this) {
wait(CHECK_INTERVAL);
connMgr.closeExpiredConnections();
connMgr.closeIdleConnections(IDLE_INTERVAL, TimeUnit.SECONDS);
}
}
} catch (InterruptedException ex) {
shutdown();
}
}
/**
* Cause a controlled exit from the thread.
*/
public void shutdown() {
shutdown = true;
synchronized (this) {
notifyAll();
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy