blob: 61d7cbedeadce6949c10810bb7207a2eb0124ff1 [file] [log] [blame]
/*
* 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();
}
}