< prev index next >
src/java.base/share/classes/sun/net/www/http/KeepAliveCache.java
Print this page
rev 47358 : 8155590: Dubious collection management in sun.net.www.http.KeepAliveCache
*** 1,7 ****
/*
! * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
--- 1,7 ----
/*
! * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
*** 25,38 ****
package sun.net.www.http;
import java.io.IOException;
import java.io.NotSerializableException;
import java.util.ArrayList;
import java.util.HashMap;
! import java.net.URL;
import jdk.internal.misc.InnocuousThread;
/**
* A class that implements a cache of idle Http connections for keep-alive
*
* @author Stephen R. Pietrowicz (NCSA)
--- 25,46 ----
package sun.net.www.http;
import java.io.IOException;
import java.io.NotSerializableException;
+ import java.io.ObjectInputStream;
+ import java.io.ObjectOutputStream;
+ import java.net.URL;
+ import java.security.AccessController;
+ import java.security.PrivilegedAction;
+ import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
! import java.util.List;
!
import jdk.internal.misc.InnocuousThread;
+ import sun.security.action.GetIntegerAction;
/**
* A class that implements a cache of idle Http connections for keep-alive
*
* @author Stephen R. Pietrowicz (NCSA)
*** 51,68 ****
*/
static final int MAX_CONNECTIONS = 5;
static int result = -1;
static int getMaxConnections() {
if (result == -1) {
! result = java.security.AccessController.doPrivileged(
! new sun.security.action.GetIntegerAction("http.maxConnections",
! MAX_CONNECTIONS))
.intValue();
! if (result <= 0)
result = MAX_CONNECTIONS;
}
! return result;
}
static final int LIFETIME = 5000;
private Thread keepAliveTimer = null;
--- 59,76 ----
*/
static final int MAX_CONNECTIONS = 5;
static int result = -1;
static int getMaxConnections() {
if (result == -1) {
! result = AccessController.doPrivileged(
! new GetIntegerAction("http.maxConnections", MAX_CONNECTIONS))
.intValue();
! if (result <= 0) {
result = MAX_CONNECTIONS;
+ }
}
! return result;
}
static final int LIFETIME = 5000;
private Thread keepAliveTimer = null;
*** 91,102 ****
* to a server that sent me a keep-alive
* time of 15 sec, the proxy unilaterally terminates my connection
* The robustness to get around this is in HttpClient.parseHTTP()
*/
final KeepAliveCache cache = this;
! java.security.AccessController.doPrivileged(
! new java.security.PrivilegedAction<>() {
public Void run() {
keepAliveTimer = InnocuousThread.newSystemThread("Keep-Alive-Timer", cache);
keepAliveTimer.setDaemon(true);
keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2);
keepAliveTimer.start();
--- 99,109 ----
* to a server that sent me a keep-alive
* time of 15 sec, the proxy unilaterally terminates my connection
* The robustness to get around this is in HttpClient.parseHTTP()
*/
final KeepAliveCache cache = this;
! AccessController.doPrivileged(new PrivilegedAction<>() {
public Void run() {
keepAliveTimer = InnocuousThread.newSystemThread("Keep-Alive-Timer", cache);
keepAliveTimer.setDaemon(true);
keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2);
keepAliveTimer.start();
*** 108,133 ****
KeepAliveKey key = new KeepAliveKey(url, obj);
ClientVector v = super.get(key);
if (v == null) {
int keepAliveTimeout = http.getKeepAliveTimeout();
! v = new ClientVector(keepAliveTimeout > 0?
! keepAliveTimeout*1000 : LIFETIME);
v.put(http);
super.put(key, v);
} else {
v.put(http);
}
}
/* remove an obsolete HttpClient from its VectorCache */
! public synchronized void remove (HttpClient h, Object obj) {
KeepAliveKey key = new KeepAliveKey(h.url, obj);
ClientVector v = super.get(key);
if (v != null) {
v.remove(h);
! if (v.empty()) {
removeVector(key);
}
}
}
--- 115,140 ----
KeepAliveKey key = new KeepAliveKey(url, obj);
ClientVector v = super.get(key);
if (v == null) {
int keepAliveTimeout = http.getKeepAliveTimeout();
! v = new ClientVector(keepAliveTimeout > 0 ?
! keepAliveTimeout * 1000 : LIFETIME);
v.put(http);
super.put(key, v);
} else {
v.put(http);
}
}
/* remove an obsolete HttpClient from its VectorCache */
! public synchronized void remove(HttpClient h, Object obj) {
KeepAliveKey key = new KeepAliveKey(h.url, obj);
ClientVector v = super.get(key);
if (v != null) {
v.remove(h);
! if (v.isEmpty()) {
removeVector(key);
}
}
}
*** 140,150 ****
/**
* Check to see if this URL has a cached HttpClient
*/
public synchronized HttpClient get(URL url, Object obj) {
-
KeepAliveKey key = new KeepAliveKey(url, obj);
ClientVector v = super.get(key);
if (v == null) { // nothing in cache yet
return null;
}
--- 147,156 ----
*** 159,263 ****
public void run() {
do {
try {
Thread.sleep(LIFETIME);
} catch (InterruptedException e) {}
- synchronized (this) {
- /* Remove all unused HttpClients. Starting from the
- * bottom of the stack (the least-recently used first).
- * REMIND: It'd be nice to not remove *all* connections
- * that aren't presently in use. One could have been added
- * a second ago that's still perfectly valid, and we're
- * needlessly axing it. But it's not clear how to do this
- * cleanly, and doing it right may be more trouble than it's
- * worth.
- */
long currentTime = System.currentTimeMillis();
!
! ArrayList<KeepAliveKey> keysToRemove
! = new ArrayList<>();
for (KeepAliveKey key : keySet()) {
ClientVector v = get(key);
synchronized (v) {
! int i;
!
! for (i = 0; i < v.size(); i++) {
! KeepAliveEntry e = v.elementAt(i);
if ((currentTime - e.idleStartTime) > v.nap) {
! HttpClient h = e.hc;
! h.closeServer();
} else {
break;
}
}
- v.subList(0, i).clear();
! if (v.size() == 0) {
keysToRemove.add(key);
}
}
}
for (KeepAliveKey key : keysToRemove) {
removeVector(key);
}
}
! } while (size() > 0);
!
! return;
}
/*
* Do not serialize this class!
*/
! private void writeObject(java.io.ObjectOutputStream stream)
! throws IOException {
throw new NotSerializableException();
}
! private void readObject(java.io.ObjectInputStream stream)
! throws IOException, ClassNotFoundException {
throw new NotSerializableException();
}
}
/* FILO order for recycling HttpClients, should run in a thread
* to time them out. If > maxConns are in use, block.
*/
!
!
! class ClientVector extends java.util.Stack<KeepAliveEntry> {
private static final long serialVersionUID = -8680532108106489459L;
// sleep time in milliseconds, before cache clear
int nap;
!
!
! ClientVector (int nap) {
this.nap = nap;
}
synchronized HttpClient get() {
! if (empty()) {
return null;
- } else {
- // Loop until we find a connection that has not timed out
- HttpClient hc = null;
- long currentTime = System.currentTimeMillis();
- do {
- KeepAliveEntry e = pop();
- if ((currentTime - e.idleStartTime) > nap) {
- e.hc.closeServer();
- } else {
- hc = e.hc;
- }
- } while ((hc== null) && (!empty()));
- return hc;
}
}
/* return a still valid, unused HttpClient */
synchronized void put(HttpClient h) {
if (size() >= KeepAliveCache.getMaxConnections()) {
--- 165,251 ----
public void run() {
do {
try {
Thread.sleep(LIFETIME);
} catch (InterruptedException e) {}
+ // Remove all outdated HttpClients.
+ synchronized (this) {
long currentTime = System.currentTimeMillis();
! List<KeepAliveKey> keysToRemove = new ArrayList<>();
for (KeepAliveKey key : keySet()) {
ClientVector v = get(key);
synchronized (v) {
! KeepAliveEntry e = v.peek();
! while (e != null) {
if ((currentTime - e.idleStartTime) > v.nap) {
! v.poll();
! e.hc.closeServer();
} else {
break;
}
+ e = v.peek();
}
! if (v.isEmpty()) {
keysToRemove.add(key);
}
}
}
for (KeepAliveKey key : keysToRemove) {
removeVector(key);
}
}
! } while (!isEmpty());
}
/*
* Do not serialize this class!
*/
! private void writeObject(ObjectOutputStream stream) throws IOException {
throw new NotSerializableException();
}
! private void readObject(ObjectInputStream stream)
! throws IOException, ClassNotFoundException
! {
throw new NotSerializableException();
}
}
/* FILO order for recycling HttpClients, should run in a thread
* to time them out. If > maxConns are in use, block.
*/
! class ClientVector extends ArrayDeque<KeepAliveEntry> {
private static final long serialVersionUID = -8680532108106489459L;
// sleep time in milliseconds, before cache clear
int nap;
! ClientVector(int nap) {
this.nap = nap;
}
synchronized HttpClient get() {
! if (isEmpty()) {
return null;
}
+
+ // Loop until we find a connection that has not timed out
+ HttpClient hc = null;
+ long currentTime = System.currentTimeMillis();
+ do {
+ KeepAliveEntry e = pop();
+ if ((currentTime - e.idleStartTime) > nap) {
+ e.hc.closeServer();
+ } else {
+ hc = e.hc;
+ }
+ } while ((hc == null) && (!isEmpty()));
+ return hc;
}
/* return a still valid, unused HttpClient */
synchronized void put(HttpClient h) {
if (size() >= KeepAliveCache.getMaxConnections()) {
*** 265,289 ****
} else {
push(new KeepAliveEntry(h, System.currentTimeMillis()));
}
}
/*
* Do not serialize this class!
*/
! private void writeObject(java.io.ObjectOutputStream stream)
! throws IOException {
throw new NotSerializableException();
}
! private void readObject(java.io.ObjectInputStream stream)
! throws IOException, ClassNotFoundException {
throw new NotSerializableException();
}
}
-
class KeepAliveKey {
private String protocol = null;
private String host = null;
private int port = 0;
private Object obj = null; // additional key, such as socketfactory
--- 253,288 ----
} else {
push(new KeepAliveEntry(h, System.currentTimeMillis()));
}
}
+ /* remove an HttpClient */
+ synchronized boolean remove(HttpClient h) {
+ for (KeepAliveEntry curr : this) {
+ if (curr.hc == h) {
+ // as we return here, we should not see a
+ // ConcurrentModificationException
+ return super.remove(curr);
+ }
+ }
+ return false;
+ }
+
/*
* Do not serialize this class!
*/
! private void writeObject(ObjectOutputStream stream) throws IOException {
throw new NotSerializableException();
}
! private void readObject(ObjectInputStream stream)
! throws IOException, ClassNotFoundException
! {
throw new NotSerializableException();
}
}
class KeepAliveKey {
private String protocol = null;
private String host = null;
private int port = 0;
private Object obj = null; // additional key, such as socketfactory
< prev index next >