0N/A/*
3909N/A * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.net.www.http;
0N/A
0N/Aimport java.io.IOException;
0N/Aimport java.io.NotSerializableException;
1676N/Aimport java.util.ArrayList;
1676N/Aimport java.util.HashMap;
0N/Aimport java.net.URL;
0N/A
0N/A/**
0N/A * A class that implements a cache of idle Http connections for keep-alive
0N/A *
0N/A * @author Stephen R. Pietrowicz (NCSA)
0N/A * @author Dave Brown
0N/A */
28N/Apublic class KeepAliveCache
1676N/A extends HashMap<KeepAliveKey, ClientVector>
28N/A implements Runnable {
0N/A private static final long serialVersionUID = -2937172892064557949L;
0N/A
0N/A /* maximum # keep-alive connections to maintain at once
0N/A * This should be 2 by the HTTP spec, but because we don't support pipe-lining
0N/A * a larger value is more appropriate. So we now set a default of 5, and the value
0N/A * refers to the number of idle connections per destination (in the cache) only.
0N/A * It can be reset by setting system property "http.maxConnections".
0N/A */
0N/A static final int MAX_CONNECTIONS = 5;
0N/A static int result = -1;
0N/A static int getMaxConnections() {
0N/A if (result == -1) {
0N/A result = java.security.AccessController.doPrivileged(
0N/A new sun.security.action.GetIntegerAction("http.maxConnections",
0N/A MAX_CONNECTIONS))
0N/A .intValue();
0N/A if (result <= 0)
0N/A result = MAX_CONNECTIONS;
0N/A }
0N/A return result;
0N/A }
0N/A
0N/A static final int LIFETIME = 5000;
0N/A
0N/A private Thread keepAliveTimer = null;
0N/A
0N/A /**
0N/A * Constructor
0N/A */
0N/A public KeepAliveCache() {}
0N/A
0N/A /**
0N/A * Register this URL and HttpClient (that supports keep-alive) with the cache
0N/A * @param url The URL contains info about the host and port
0N/A * @param http The HttpClient to be cached
0N/A */
0N/A public synchronized void put(final URL url, Object obj, HttpClient http) {
0N/A boolean startThread = (keepAliveTimer == null);
0N/A if (!startThread) {
0N/A if (!keepAliveTimer.isAlive()) {
0N/A startThread = true;
0N/A }
0N/A }
0N/A if (startThread) {
0N/A clear();
0N/A /* Unfortunately, we can't always believe the keep-alive timeout we got
0N/A * back from the server. If I'm connected through a Netscape proxy
0N/A * to a server that sent me a keep-alive
0N/A * time of 15 sec, the proxy unilaterally terminates my connection
28N/A * The robustness to get around this is in HttpClient.parseHTTP()
0N/A */
0N/A final KeepAliveCache cache = this;
0N/A java.security.AccessController.doPrivileged(
28N/A new java.security.PrivilegedAction<Void>() {
28N/A public Void run() {
0N/A // We want to create the Keep-Alive-Timer in the
0N/A // system threadgroup
0N/A ThreadGroup grp = Thread.currentThread().getThreadGroup();
0N/A ThreadGroup parent = null;
0N/A while ((parent = grp.getParent()) != null) {
0N/A grp = parent;
0N/A }
0N/A
0N/A keepAliveTimer = new Thread(grp, cache, "Keep-Alive-Timer");
0N/A keepAliveTimer.setDaemon(true);
0N/A keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2);
3480N/A // Set the context class loader to null in order to avoid
3480N/A // keeping a strong reference to an application classloader.
3480N/A keepAliveTimer.setContextClassLoader(null);
0N/A keepAliveTimer.start();
0N/A return null;
0N/A }
0N/A });
0N/A }
0N/A
0N/A KeepAliveKey key = new KeepAliveKey(url, obj);
28N/A ClientVector v = super.get(key);
0N/A
0N/A if (v == null) {
0N/A int keepAliveTimeout = http.getKeepAliveTimeout();
0N/A v = new ClientVector(keepAliveTimeout > 0?
0N/A keepAliveTimeout*1000 : LIFETIME);
0N/A v.put(http);
0N/A super.put(key, v);
0N/A } else {
0N/A v.put(http);
0N/A }
0N/A }
0N/A
28N/A /* remove an obsolete HttpClient from its VectorCache */
0N/A public synchronized void remove (HttpClient h, Object obj) {
0N/A KeepAliveKey key = new KeepAliveKey(h.url, obj);
28N/A ClientVector v = super.get(key);
0N/A if (v != null) {
0N/A v.remove(h);
0N/A if (v.empty()) {
0N/A removeVector(key);
0N/A }
0N/A }
0N/A }
0N/A
28N/A /* called by a clientVector thread when all its connections have timed out
0N/A * and that vector of connections should be removed.
0N/A */
0N/A synchronized void removeVector(KeepAliveKey k) {
0N/A super.remove(k);
0N/A }
0N/A
0N/A /**
0N/A * Check to see if this URL has a cached HttpClient
0N/A */
28N/A public synchronized HttpClient get(URL url, Object obj) {
0N/A
0N/A KeepAliveKey key = new KeepAliveKey(url, obj);
28N/A ClientVector v = super.get(key);
0N/A if (v == null) { // nothing in cache yet
0N/A return null;
0N/A }
0N/A return v.get();
0N/A }
0N/A
0N/A /* Sleeps for an alloted timeout, then checks for timed out connections.
0N/A * Errs on the side of caution (leave connections idle for a relatively
0N/A * short time).
0N/A */
1676N/A @Override
0N/A public void run() {
0N/A do {
0N/A try {
0N/A Thread.sleep(LIFETIME);
0N/A } catch (InterruptedException e) {}
0N/A synchronized (this) {
0N/A /* Remove all unused HttpClients. Starting from the
0N/A * bottom of the stack (the least-recently used first).
0N/A * REMIND: It'd be nice to not remove *all* connections
0N/A * that aren't presently in use. One could have been added
0N/A * a second ago that's still perfectly valid, and we're
0N/A * needlessly axing it. But it's not clear how to do this
0N/A * cleanly, and doing it right may be more trouble than it's
0N/A * worth.
0N/A */
0N/A
0N/A long currentTime = System.currentTimeMillis();
0N/A
28N/A ArrayList<KeepAliveKey> keysToRemove
28N/A = new ArrayList<KeepAliveKey>();
0N/A
28N/A for (KeepAliveKey key : keySet()) {
28N/A ClientVector v = get(key);
0N/A synchronized (v) {
0N/A int i;
0N/A
0N/A for (i = 0; i < v.size(); i++) {
28N/A KeepAliveEntry e = v.elementAt(i);
0N/A if ((currentTime - e.idleStartTime) > v.nap) {
0N/A HttpClient h = e.hc;
0N/A h.closeServer();
0N/A } else {
0N/A break;
0N/A }
0N/A }
0N/A v.subList(0, i).clear();
0N/A
0N/A if (v.size() == 0) {
0N/A keysToRemove.add(key);
0N/A }
0N/A }
0N/A }
28N/A
28N/A for (KeepAliveKey key : keysToRemove) {
28N/A removeVector(key);
0N/A }
0N/A }
0N/A } while (size() > 0);
0N/A
0N/A return;
0N/A }
0N/A
0N/A /*
0N/A * Do not serialize this class!
0N/A */
0N/A private void writeObject(java.io.ObjectOutputStream stream)
0N/A throws IOException {
0N/A throw new NotSerializableException();
0N/A }
0N/A
0N/A private void readObject(java.io.ObjectInputStream stream)
0N/A throws IOException, ClassNotFoundException {
0N/A throw new NotSerializableException();
0N/A }
0N/A}
0N/A
0N/A/* FILO order for recycling HttpClients, should run in a thread
0N/A * to time them out. If > maxConns are in use, block.
0N/A */
0N/A
0N/A
28N/Aclass ClientVector extends java.util.Stack<KeepAliveEntry> {
0N/A private static final long serialVersionUID = -8680532108106489459L;
0N/A
0N/A // sleep time in milliseconds, before cache clear
0N/A int nap;
0N/A
0N/A
0N/A
0N/A ClientVector (int nap) {
0N/A this.nap = nap;
0N/A }
0N/A
0N/A synchronized HttpClient get() {
0N/A if (empty()) {
0N/A return null;
0N/A } else {
0N/A // Loop until we find a connection that has not timed out
0N/A HttpClient hc = null;
0N/A long currentTime = System.currentTimeMillis();
0N/A do {
28N/A KeepAliveEntry e = pop();
0N/A if ((currentTime - e.idleStartTime) > nap) {
0N/A e.hc.closeServer();
0N/A } else {
0N/A hc = e.hc;
0N/A }
0N/A } while ((hc== null) && (!empty()));
0N/A return hc;
0N/A }
0N/A }
0N/A
0N/A /* return a still valid, unused HttpClient */
0N/A synchronized void put(HttpClient h) {
3400N/A if (size() >= KeepAliveCache.getMaxConnections()) {
0N/A h.closeServer(); // otherwise the connection remains in limbo
0N/A } else {
0N/A push(new KeepAliveEntry(h, System.currentTimeMillis()));
0N/A }
0N/A }
0N/A
0N/A /*
0N/A * Do not serialize this class!
0N/A */
0N/A private void writeObject(java.io.ObjectOutputStream stream)
0N/A throws IOException {
0N/A throw new NotSerializableException();
0N/A }
0N/A
0N/A private void readObject(java.io.ObjectInputStream stream)
0N/A throws IOException, ClassNotFoundException {
0N/A throw new NotSerializableException();
0N/A }
0N/A}
0N/A
0N/A
0N/Aclass KeepAliveKey {
0N/A private String protocol = null;
0N/A private String host = null;
0N/A private int port = 0;
0N/A private Object obj = null; // additional key, such as socketfactory
0N/A
0N/A /**
0N/A * Constructor
0N/A *
0N/A * @param url the URL containing the protocol, host and port information
0N/A */
0N/A public KeepAliveKey(URL url, Object obj) {
0N/A this.protocol = url.getProtocol();
0N/A this.host = url.getHost();
0N/A this.port = url.getPort();
0N/A this.obj = obj;
0N/A }
0N/A
0N/A /**
0N/A * Determine whether or not two objects of this type are equal
0N/A */
1676N/A @Override
0N/A public boolean equals(Object obj) {
0N/A if ((obj instanceof KeepAliveKey) == false)
0N/A return false;
0N/A KeepAliveKey kae = (KeepAliveKey)obj;
0N/A return host.equals(kae.host)
0N/A && (port == kae.port)
0N/A && protocol.equals(kae.protocol)
0N/A && this.obj == kae.obj;
0N/A }
0N/A
0N/A /**
0N/A * The hashCode() for this object is the string hashCode() of
0N/A * concatenation of the protocol, host name and port.
0N/A */
1676N/A @Override
0N/A public int hashCode() {
0N/A String str = protocol+host+port;
0N/A return this.obj == null? str.hashCode() :
0N/A str.hashCode() + this.obj.hashCode();
0N/A }
0N/A}
0N/A
0N/Aclass KeepAliveEntry {
0N/A HttpClient hc;
0N/A long idleStartTime;
0N/A
0N/A KeepAliveEntry(HttpClient hc, long idleStartTime) {
0N/A this.hc = hc;
0N/A this.idleStartTime = idleStartTime;
0N/A }
0N/A}