/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.apache.harmony.luni.internal.net.www.protocol.http; import java.io.IOException; import java.net.Proxy; import java.net.URI; import java.security.AccessController; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.harmony.luni.util.PriviAction; /** * <code>HttpConnectionManager</code> manages a pool of <code>HttpConnection</code>s * that are not currently in use and is used to get hold of persistent <code>HttpConnection</code>s. * Clients should return an <code>HttpConnection</code> to the pool after use by calling * <code>returnConnectionToPool</code> * * Two system properties affect the behaviour of this class - <code>http.maxConnections</code> * and <code>http.keepAlive</code>. <code>http.keepAlive</code> determines whether * or not connections should be persisted and <code>http.maxConnections</code> * determines the maximum number of connections to each individual host that * should be kept in the pool. */ public class HttpConnectionManager { // The maximum number of connections to any location private static int maxConnections = 5; // Keeps connections alive if true private static boolean keepAlive = true; private static HttpConnectionManager defaultConnectionManager; private ConnectionPool pool = new ConnectionPool(); /** * Returns the default connection manager */ public static HttpConnectionManager getDefault() { if(defaultConnectionManager == null) { defaultConnectionManager = new HttpConnectionManager(); } return defaultConnectionManager; } public HttpConnection getConnection(URI uri, int connectTimeout) throws IOException { checkSystemProperties(); HttpConfiguration config = new HttpConfiguration(uri); return pool.getHttpConnection(config, connectTimeout); } public HttpConnection getConnection(URI uri, Proxy proxy, int connectTimeout) throws IOException { checkSystemProperties(); HttpConfiguration config = new HttpConfiguration(uri, proxy); return pool.getHttpConnection(config, connectTimeout); } public void returnConnectionToPool(HttpConnection connection) { checkSystemProperties(); pool.returnConnection(connection); } public int numFreeConnections() { return pool.numFreeConnections(); } private void checkSystemProperties() { String httpMaxConnections = AccessController.doPrivileged(new PriviAction<String>("http.maxConnections")); String httpKeepAlive = AccessController.doPrivileged(new PriviAction<String>("http.keepAlive")); if(httpMaxConnections != null) { maxConnections = Integer.parseInt(httpMaxConnections); } if(httpKeepAlive != null) { keepAlive = Boolean.parseBoolean(httpKeepAlive); if(!keepAlive) { pool.clear(); } } } private static class ConnectionPool { private Map<HttpConfiguration, List<HttpConnection>> freeConnectionMap = new HashMap<HttpConfiguration, List<HttpConnection>>(); // Map of free Sockets public synchronized void clear() { for (Iterator<List<HttpConnection>> iter = freeConnectionMap.values().iterator(); iter.hasNext();) { List<HttpConnection> connections = iter.next(); for (Iterator<HttpConnection> iterator = connections.iterator(); iterator.hasNext();) { HttpConnection connection = iterator.next(); connection.closeSocketAndStreams(); } } freeConnectionMap.clear(); } public synchronized void returnConnection(HttpConnection connection) { // BEGIN android-note // Simplified the following test. // END android-note if(keepAlive && connection.isEligibleForRecycling()) { HttpConfiguration config = connection.getHttpConfiguration(); List<HttpConnection> connections = freeConnectionMap.get(config); if(connections == null) { connections = new ArrayList<HttpConnection>(); freeConnectionMap.put(config, connections); } if(connections.size() < HttpConnectionManager.maxConnections) { if(!connections.contains(connection)) { connections.add(connection); } } else { connection.closeSocketAndStreams(); } } else { // Make sure all streams are closed etc. connection.closeSocketAndStreams(); } } public synchronized HttpConnection getHttpConnection(HttpConfiguration config, int connectTimeout) throws IOException { List<HttpConnection> connections = freeConnectionMap.get(config); if(keepAlive && connections == null) { connections = new ArrayList<HttpConnection>(); freeConnectionMap.put(config, connections); } if(!keepAlive || connections.isEmpty()) { HttpConnection connection = new HttpConnection(config, connectTimeout); return connection; } else { HttpConnection connection = connections.get(0); connections.remove(0); if(!connection.isStale()) { SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkConnect(connection.getSocket().getInetAddress().getHostName(), connection.getSocket().getPort()); } return connection; } else { return getHttpConnection(config, connectTimeout); } } } public int numFreeConnections() { int numFree = 0; for (Iterator<List<HttpConnection>> iter = freeConnectionMap.values().iterator(); iter.hasNext();) { List<HttpConnection> connections = iter.next(); numFree += connections.size(); } return numFree; } } public void reset() { pool.clear(); } }