Java tutorial
/* * Copyright (c) 2013 3 Round Stones Inc., Some Rights Reserved * * 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 org.callimachusproject.client; import java.io.Closeable; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; import org.apache.http.ConnectionReuseStrategy; import org.apache.http.HttpClientConnection; import org.apache.http.HttpHost; import org.apache.http.HttpResponse; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.cache.ResourceFactory; import org.apache.http.client.config.RequestConfig; import org.apache.http.config.ConnectionConfig; import org.apache.http.config.RegistryBuilder; import org.apache.http.config.SocketConfig; import org.apache.http.conn.ConnectionKeepAliveStrategy; import org.apache.http.conn.ConnectionRequest; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.routing.HttpRoute; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.LayeredConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.DefaultConnectionReuseStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy; import org.apache.http.impl.client.SystemDefaultCredentialsProvider; import org.apache.http.impl.client.cache.CacheConfig; import org.apache.http.impl.client.cache.CachingHttpClientBuilder; import org.apache.http.impl.client.cache.FileResourceFactory; import org.apache.http.impl.client.cache.ManagedHttpCacheStorage; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.impl.execchain.ClientExecChain; import org.apache.http.message.BasicHeader; import org.apache.http.protocol.HttpContext; import org.callimachusproject.Version; import org.callimachusproject.io.FileUtil; import org.callimachusproject.util.MailProperties; import org.callimachusproject.util.SystemProperties; /** * Manages the connections and cache entries for outgoing requests. * * @author James Leigh * */ public class HttpClientFactory implements Closeable { private static final String DEFAULT_NAME = Version.getInstance().getVersion(); private static HttpClientFactory instance; static { try { String tmpDirStr = System.getProperty("java.io.tmpdir"); if (tmpDirStr != null) { File tmpDir = new File(tmpDirStr); if (!tmpDir.exists()) { tmpDir.mkdirs(); } } File dir = File.createTempFile("http-client-cache", ""); dir.delete(); FileUtil.deleteOnExit(dir); setCacheDirectory(dir); instance = new HttpClientFactory(dir); } catch (IOException e) { throw new AssertionError(e); } Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { public void run() { synchronized (HttpClientFactory.class) { if (instance != null) { instance.close(); instance = null; } } } })); } public static synchronized HttpClientFactory getInstance() { return instance; } public static synchronized void setCacheDirectory(File dir) throws IOException { if (instance != null) { instance.close(); } instance = new HttpClientFactory(dir); } private final ProxyClientExecDecorator decorator; private final ResourceFactory entryFactory; final PoolingHttpClientConnectionManager connManager; private final ConnectionReuseStrategy reuseStrategy; private final ConnectionKeepAliveStrategy keepAliveStrategy; private HttpClientFactory(File cacheDir) throws IOException { cacheDir.mkdirs(); entryFactory = new FileResourceFactory(cacheDir); decorator = new ProxyClientExecDecorator(); LayeredConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactory.getSystemSocketFactory(); connManager = new PoolingHttpClientConnectionManager(RegistryBuilder.<ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.getSocketFactory()) .register("https", sslSocketFactory).build()); connManager.setDefaultSocketConfig(getDefaultSocketConfig()); connManager.setDefaultConnectionConfig(getDefaultConnectionConfig()); int max = Integer.parseInt(System.getProperty("http.maxConnections", "20")); connManager.setDefaultMaxPerRoute(max); connManager.setMaxTotal(2 * max); reuseStrategy = DefaultConnectionReuseStrategy.INSTANCE; keepAliveStrategy = new ConnectionKeepAliveStrategy() { private final long KEEPALIVE = SystemProperties.getClientKeepAliveTimeout(); private ConnectionKeepAliveStrategy delegate = DefaultConnectionKeepAliveStrategy.INSTANCE; public long getKeepAliveDuration(HttpResponse response, HttpContext context) { long ret = delegate.getKeepAliveDuration(response, context); if (ret > 0) return ret; return KEEPALIVE; } }; } public synchronized void close() { connManager.shutdown(); } public synchronized ClientExecChain getProxy(HttpHost destination) { return decorator.getProxy(destination); } public synchronized ClientExecChain setProxy(HttpHost destination, ClientExecChain proxy) { return decorator.setProxy(destination, proxy); } public synchronized ClientExecChain setProxyIfAbsent(HttpHost destination, ClientExecChain proxy) { return decorator.setProxyIfAbsent(destination, proxy); } public synchronized boolean removeProxy(HttpHost destination, ClientExecChain proxy) { return decorator.removeProxy(destination, proxy); } public synchronized boolean removeProxy(ClientExecChain proxy) { return decorator.removeProxy(proxy); } public CloseableHttpClient createHttpClient(String source) { return createHttpClient(source, new SystemDefaultCredentialsProvider()); } public synchronized CloseableHttpClient createHttpClient(String source, CredentialsProvider credentials) { CacheConfig cache = getDefaultCacheConfig(); ManagedHttpCacheStorage storage = new ManagedHttpCacheStorage(cache); List<BasicHeader> headers = new ArrayList<BasicHeader>(); headers.add(new BasicHeader("Origin", getOrigin(source))); headers.addAll(getAdditionalRequestHeaders()); return new AutoClosingHttpClient(new CachingHttpClientBuilder() { protected ClientExecChain decorateMainExec(ClientExecChain mainExec) { return super.decorateMainExec(decorator.decorateMainExec(mainExec)); } }.setResourceFactory(entryFactory).setHttpCacheStorage(storage).setCacheConfig(cache) .setConnectionManager(getConnectionManager()).setConnectionReuseStrategy(reuseStrategy) .setKeepAliveStrategy(keepAliveStrategy).useSystemProperties().disableContentCompression() .setDefaultRequestConfig(getDefaultRequestConfig()).addInterceptorFirst(new GZipInterceptor()) .addInterceptorFirst(new GUnzipInterceptor()).setDefaultCredentialsProvider(credentials) .setDefaultHeaders(headers).setUserAgent(DEFAULT_NAME).build(), storage); } private HttpClientConnectionManager getConnectionManager() { return new HttpClientConnectionManager() { public ConnectionRequest requestConnection(HttpRoute route, Object state) { return connManager.requestConnection(route, state); } public void releaseConnection(HttpClientConnection conn, Object newState, long validDuration, TimeUnit timeUnit) { connManager.releaseConnection(conn, newState, validDuration, timeUnit); } public void connect(HttpClientConnection conn, HttpRoute route, int connectTimeout, HttpContext context) throws IOException { connManager.connect(conn, route, connectTimeout, context); } public void upgrade(HttpClientConnection conn, HttpRoute route, HttpContext context) throws IOException { connManager.upgrade(conn, route, context); } public void routeComplete(HttpClientConnection conn, HttpRoute route, HttpContext context) throws IOException { connManager.routeComplete(conn, route, context); } public void closeIdleConnections(long idletime, TimeUnit tunit) { connManager.closeIdleConnections(idletime, tunit); } public void closeExpiredConnections() { connManager.closeExpiredConnections(); } public void shutdown() { // connection manager is closed elsewhere } }; } private String getOrigin(String source) { assert source != null; int scheme = source.indexOf("://"); if (scheme < 0 && (source.startsWith("file:") || source.startsWith("jar:file:"))) { return "file://"; } else { if (scheme < 0) throw new IllegalArgumentException("Not an absolute hierarchical URI: " + source); int path = source.indexOf('/', scheme + 3); if (path >= 0) { return source.substring(0, path); } else { return source; } } } private RequestConfig getDefaultRequestConfig() { return RequestConfig.custom().setSocketTimeout(0).setConnectTimeout(10000) .setStaleConnectionCheckEnabled(false).setExpectContinueEnabled(true).setMaxRedirects(20) .setRedirectsEnabled(true).setCircularRedirectsAllowed(false).build(); } private ConnectionConfig getDefaultConnectionConfig() { return ConnectionConfig.custom().setBufferSize(8 * 1024).build(); } private SocketConfig getDefaultSocketConfig() { return SocketConfig.custom().setTcpNoDelay(false).setSoTimeout(60 * 1000).build(); } private Collection<BasicHeader> getAdditionalRequestHeaders() { try { MailProperties mail = MailProperties.getInstance(); String from = mail.getMailProperties().get("mail.from"); if (from != null) { BasicHeader hd = new BasicHeader("From", from); return Collections.singleton(hd); } } catch (IOException e) { // ignore } catch (SecurityException e) { // ignore } return Collections.emptySet(); } private CacheConfig getDefaultCacheConfig() { return CacheConfig.custom().setSharedCache(false).setAllow303Caching(true) .setWeakETagOnPutDeleteAllowed(true).setHeuristicCachingEnabled(true) .setHeuristicDefaultLifetime(60 * 60 * 24).setMaxObjectSize(1024 * 1024).build(); } }