Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/net/www/http/KeepAliveCache.java
38923 views
/*1* Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package sun.net.www.http;2627import java.io.IOException;28import java.io.NotSerializableException;29import java.util.ArrayList;30import java.util.HashMap;31import java.net.URL;3233/**34* A class that implements a cache of idle Http connections for keep-alive35*36* @author Stephen R. Pietrowicz (NCSA)37* @author Dave Brown38*/39public class KeepAliveCache40extends HashMap<KeepAliveKey, ClientVector>41implements Runnable {42private static final long serialVersionUID = -2937172892064557949L;4344/* maximum # keep-alive connections to maintain at once45* This should be 2 by the HTTP spec, but because we don't support pipe-lining46* a larger value is more appropriate. So we now set a default of 5, and the value47* refers to the number of idle connections per destination (in the cache) only.48* It can be reset by setting system property "http.maxConnections".49*/50static final int MAX_CONNECTIONS = 5;51static int result = -1;52static int getMaxConnections() {53if (result == -1) {54result = java.security.AccessController.doPrivileged(55new sun.security.action.GetIntegerAction("http.maxConnections",56MAX_CONNECTIONS))57.intValue();58if (result <= 0)59result = MAX_CONNECTIONS;60}61return result;62}6364static final int LIFETIME = 5000;6566private Thread keepAliveTimer = null;6768/**69* Constructor70*/71public KeepAliveCache() {}7273/**74* Register this URL and HttpClient (that supports keep-alive) with the cache75* @param url The URL contains info about the host and port76* @param http The HttpClient to be cached77*/78public synchronized void put(final URL url, Object obj, HttpClient http) {79boolean startThread = (keepAliveTimer == null);80if (!startThread) {81if (!keepAliveTimer.isAlive()) {82startThread = true;83}84}85if (startThread) {86clear();87/* Unfortunately, we can't always believe the keep-alive timeout we got88* back from the server. If I'm connected through a Netscape proxy89* to a server that sent me a keep-alive90* time of 15 sec, the proxy unilaterally terminates my connection91* The robustness to get around this is in HttpClient.parseHTTP()92*/93final KeepAliveCache cache = this;94java.security.AccessController.doPrivileged(95new java.security.PrivilegedAction<Void>() {96public Void run() {97// We want to create the Keep-Alive-Timer in the98// system threadgroup99ThreadGroup grp = Thread.currentThread().getThreadGroup();100ThreadGroup parent = null;101while ((parent = grp.getParent()) != null) {102grp = parent;103}104105keepAliveTimer = new Thread(grp, cache, "Keep-Alive-Timer");106keepAliveTimer.setDaemon(true);107keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2);108// Set the context class loader to null in order to avoid109// keeping a strong reference to an application classloader.110keepAliveTimer.setContextClassLoader(null);111keepAliveTimer.start();112return null;113}114});115}116117KeepAliveKey key = new KeepAliveKey(url, obj);118ClientVector v = super.get(key);119120if (v == null) {121int keepAliveTimeout = http.getKeepAliveTimeout();122v = new ClientVector(keepAliveTimeout > 0?123keepAliveTimeout*1000 : LIFETIME);124v.put(http);125super.put(key, v);126} else {127v.put(http);128}129}130131/* remove an obsolete HttpClient from its VectorCache */132public synchronized void remove (HttpClient h, Object obj) {133KeepAliveKey key = new KeepAliveKey(h.url, obj);134ClientVector v = super.get(key);135if (v != null) {136v.remove(h);137if (v.empty()) {138removeVector(key);139}140}141}142143/* called by a clientVector thread when all its connections have timed out144* and that vector of connections should be removed.145*/146synchronized void removeVector(KeepAliveKey k) {147super.remove(k);148}149150/**151* Check to see if this URL has a cached HttpClient152*/153public synchronized HttpClient get(URL url, Object obj) {154155KeepAliveKey key = new KeepAliveKey(url, obj);156ClientVector v = super.get(key);157if (v == null) { // nothing in cache yet158return null;159}160return v.get();161}162163/* Sleeps for an alloted timeout, then checks for timed out connections.164* Errs on the side of caution (leave connections idle for a relatively165* short time).166*/167@Override168public void run() {169do {170try {171Thread.sleep(LIFETIME);172} catch (InterruptedException e) {}173synchronized (this) {174/* Remove all unused HttpClients. Starting from the175* bottom of the stack (the least-recently used first).176* REMIND: It'd be nice to not remove *all* connections177* that aren't presently in use. One could have been added178* a second ago that's still perfectly valid, and we're179* needlessly axing it. But it's not clear how to do this180* cleanly, and doing it right may be more trouble than it's181* worth.182*/183184long currentTime = System.currentTimeMillis();185186ArrayList<KeepAliveKey> keysToRemove187= new ArrayList<KeepAliveKey>();188189for (KeepAliveKey key : keySet()) {190ClientVector v = get(key);191synchronized (v) {192int i;193194for (i = 0; i < v.size(); i++) {195KeepAliveEntry e = v.elementAt(i);196if ((currentTime - e.idleStartTime) > v.nap) {197HttpClient h = e.hc;198h.closeServer();199} else {200break;201}202}203v.subList(0, i).clear();204205if (v.size() == 0) {206keysToRemove.add(key);207}208}209}210211for (KeepAliveKey key : keysToRemove) {212removeVector(key);213}214}215} while (size() > 0);216217return;218}219220/*221* Do not serialize this class!222*/223private void writeObject(java.io.ObjectOutputStream stream)224throws IOException {225throw new NotSerializableException();226}227228private void readObject(java.io.ObjectInputStream stream)229throws IOException, ClassNotFoundException {230throw new NotSerializableException();231}232}233234/* FILO order for recycling HttpClients, should run in a thread235* to time them out. If > maxConns are in use, block.236*/237238239class ClientVector extends java.util.Stack<KeepAliveEntry> {240private static final long serialVersionUID = -8680532108106489459L;241242// sleep time in milliseconds, before cache clear243int nap;244245246247ClientVector (int nap) {248this.nap = nap;249}250251synchronized HttpClient get() {252if (empty()) {253return null;254} else {255// Loop until we find a connection that has not timed out256HttpClient hc = null;257long currentTime = System.currentTimeMillis();258do {259KeepAliveEntry e = pop();260if ((currentTime - e.idleStartTime) > nap) {261e.hc.closeServer();262} else {263hc = e.hc;264}265} while ((hc== null) && (!empty()));266return hc;267}268}269270/* return a still valid, unused HttpClient */271synchronized void put(HttpClient h) {272if (size() >= KeepAliveCache.getMaxConnections()) {273h.closeServer(); // otherwise the connection remains in limbo274} else {275push(new KeepAliveEntry(h, System.currentTimeMillis()));276}277}278279/*280* Do not serialize this class!281*/282private void writeObject(java.io.ObjectOutputStream stream)283throws IOException {284throw new NotSerializableException();285}286287private void readObject(java.io.ObjectInputStream stream)288throws IOException, ClassNotFoundException {289throw new NotSerializableException();290}291}292293294class KeepAliveKey {295private String protocol = null;296private String host = null;297private int port = 0;298private Object obj = null; // additional key, such as socketfactory299300/**301* Constructor302*303* @param url the URL containing the protocol, host and port information304*/305public KeepAliveKey(URL url, Object obj) {306this.protocol = url.getProtocol();307this.host = url.getHost();308this.port = url.getPort();309this.obj = obj;310}311312/**313* Determine whether or not two objects of this type are equal314*/315@Override316public boolean equals(Object obj) {317if ((obj instanceof KeepAliveKey) == false)318return false;319KeepAliveKey kae = (KeepAliveKey)obj;320return host.equals(kae.host)321&& (port == kae.port)322&& protocol.equals(kae.protocol)323&& this.obj == kae.obj;324}325326/**327* The hashCode() for this object is the string hashCode() of328* concatenation of the protocol, host name and port.329*/330@Override331public int hashCode() {332String str = protocol+host+port;333return this.obj == null? str.hashCode() :334str.hashCode() + this.obj.hashCode();335}336}337338class KeepAliveEntry {339HttpClient hc;340long idleStartTime;341342KeepAliveEntry(HttpClient hc, long idleStartTime) {343this.hc = hc;344this.idleStartTime = idleStartTime;345}346}347348349