package org.carlspring.strongbox.service.impl; import org.apache.http.HttpHost; import org.apache.http.conn.routing.HttpRoute; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.pool.PoolStats; import org.carlspring.strongbox.service.ProxyRepositoryConnectionPoolConfigurationService; import org.glassfish.jersey.apache.connector.ApacheClientProperties; import org.glassfish.jersey.apache.connector.ApacheConnectorProvider; import org.glassfish.jersey.client.ClientConfig; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import java.net.URI; import java.net.URISyntaxException; import java.util.concurrent.TimeUnit; /** * @author korest */ @Component public class ProxyRepositoryConnectionPoolConfigurationServiceImpl implements ProxyRepositoryConnectionPoolConfigurationService { private static final Logger LOGGER = LoggerFactory .getLogger(ProxyRepositoryConnectionPoolConfigurationServiceImpl.class); private PoolingHttpClientConnectionManager poolingHttpClientConnectionManager; private IdleConnectionMonitorThread idleConnectionMonitorThread; @Value("${pool.maxConnections:200}") private int maxTotal; @Value("${pool.defaultConnectionsPerRoute:5}") private int defaultMaxPerRoute; @Value("${pool.idleConnectionsTimeoutInSeconds:60}") private int idleConnectionsTimeoutInSeconds; @PostConstruct public void init() { poolingHttpClientConnectionManager = new PoolingHttpClientConnectionManager(); poolingHttpClientConnectionManager.setMaxTotal(maxTotal); //TODO value that depends on number of threads? poolingHttpClientConnectionManager.setDefaultMaxPerRoute(defaultMaxPerRoute); // thread for monitoring unused connections idleConnectionMonitorThread = new IdleConnectionMonitorThread(poolingHttpClientConnectionManager, idleConnectionsTimeoutInSeconds); idleConnectionMonitorThread.setDaemon(true); idleConnectionMonitorThread.start(); } @PreDestroy public void destroy() { shutdown(); } public Client getClient() { ClientConfig config = new ClientConfig(); config.connectorProvider(new ApacheConnectorProvider()); config.property(ApacheClientProperties.CONNECTION_MANAGER, poolingHttpClientConnectionManager); // property to prevent closing connection manager when client is closed config.property(ApacheClientProperties.CONNECTION_MANAGER_SHARED, true); // TODO set basic authentication here instead of setting it always in client? /* CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(userName, password)); config.property(ApacheClientProperties.CREDENTIALS_PROVIDER, credentialsProvider); */ return ClientBuilder.newBuilder().newClient(config); } @Override public void setMaxTotal(int max) { poolingHttpClientConnectionManager.setMaxTotal(max); } @Override public void setDefaultMaxPerRepository(int defaultMax) { poolingHttpClientConnectionManager.setDefaultMaxPerRoute(defaultMax); } @Override public int getDefaultMaxPerRepository() { return poolingHttpClientConnectionManager.getDefaultMaxPerRoute(); } @Override public void setMaxPerRepository(String repository, int max) { HttpRoute httpRoute = getHttpRouteFromRepository(repository); poolingHttpClientConnectionManager.setMaxPerRoute(httpRoute, max); } @Override public PoolStats getTotalStats() { return poolingHttpClientConnectionManager.getTotalStats(); } @Override public PoolStats getPoolStats(String repository) { HttpRoute httpRoute = getHttpRouteFromRepository(repository); return poolingHttpClientConnectionManager.getStats(httpRoute); } @Override public void shutdown() { idleConnectionMonitorThread.shutdown(); poolingHttpClientConnectionManager.shutdown(); } // code to create HttpRoute the same as in apache library private HttpRoute getHttpRouteFromRepository(String repository) { try { URI uri = new URI(repository); boolean secure = uri.getScheme().equalsIgnoreCase("https"); int port = uri.getPort(); if (uri.getPort() > 0) { port = uri.getPort(); } else if (uri.getScheme().equalsIgnoreCase("https")) { port = 443; } else if (uri.getScheme().equalsIgnoreCase("http")) { port = 80; } else { LOGGER.warn("Unknown port of uri %s", repository); } HttpHost httpHost = new HttpHost(uri.getHost(), port, uri.getScheme()); // TODO check whether we need second param InetAddress return new HttpRoute(httpHost, null, secure); } catch (URISyntaxException e) { LOGGER.error(e.getMessage(), e); } // default http route creation return new HttpRoute(HttpHost.create(repository)); } private static final class IdleConnectionMonitorThread extends Thread { private PoolingHttpClientConnectionManager poolingHttpClientConnectionManager; private volatile boolean shutdown; private int idleConnectionsTimeout; IdleConnectionMonitorThread(PoolingHttpClientConnectionManager poolingHttpClientConnectionManager, int idleConnectionsTimeout) { super(); this.poolingHttpClientConnectionManager = poolingHttpClientConnectionManager; this.idleConnectionsTimeout = idleConnectionsTimeout; } @Override public void run() { try { while (!shutdown) { synchronized (this) { wait(5000); poolingHttpClientConnectionManager.closeExpiredConnections(); poolingHttpClientConnectionManager.closeIdleConnections(idleConnectionsTimeout, TimeUnit.SECONDS); } } } catch (InterruptedException e) { shutdown(); } } public void shutdown() { shutdown = true; synchronized (this) { notifyAll(); } } } }