@@ -112,49 +112,56 @@ public KeepAliveCache() {}
112
112
* @param url The URL contains info about the host and port
113
113
* @param http The HttpClient to be cached
114
114
*/
115
- public synchronized void put (final URL url , Object obj , HttpClient http ) {
116
- boolean startThread = (keepAliveTimer == null );
117
- if (!startThread ) {
118
- if (!keepAliveTimer .isAlive ()) {
119
- startThread = true ;
115
+ public void put (final URL url , Object obj , HttpClient http ) {
116
+ // this method may need to close an HttpClient, either because
117
+ // it is not cacheable, or because the cache is at its capacity.
118
+ // In the latter case, we close the least recently used client.
119
+ // The client to close is stored in oldClient, and is closed
120
+ // after cacheLock is released.
121
+ HttpClient oldClient = null ;
122
+ synchronized (this ) {
123
+ boolean startThread = (keepAliveTimer == null );
124
+ if (!startThread ) {
125
+ if (!keepAliveTimer .isAlive ()) {
126
+ startThread = true ;
127
+ }
120
128
}
121
- }
122
- if (startThread ) {
123
- clear ();
124
- /* Unfortunately, we can't always believe the keep-alive timeout we got
125
- * back from the server. If I'm connected through a Netscape proxy
126
- * to a server that sent me a keep-alive
127
- * time of 15 sec, the proxy unilaterally terminates my connection
128
- * The robustness to get around this is in HttpClient.parseHTTP()
129
- */
130
- final KeepAliveCache cache = this ;
131
- AccessController .doPrivileged (new PrivilegedAction <Void >() {
132
- public Void run () {
133
- // We want to create the Keep-Alive-Timer in the
134
- // system threadgroup
135
- ThreadGroup grp = Thread .currentThread ().getThreadGroup ();
136
- ThreadGroup parent = null ;
137
- while ((parent = grp .getParent ()) != null ) {
138
- grp = parent ;
139
- }
129
+ if (startThread ) {
130
+ clear ();
131
+ /* Unfortunately, we can't always believe the keep-alive timeout we got
132
+ * back from the server. If I'm connected through a Netscape proxy
133
+ * to a server that sent me a keep-alive
134
+ * time of 15 sec, the proxy unilaterally terminates my connection
135
+ * The robustness to get around this is in HttpClient.parseHTTP()
136
+ */
137
+ final KeepAliveCache cache = this ;
138
+ AccessController .doPrivileged (new PrivilegedAction <Void >() {
139
+ public Void run () {
140
+ // We want to create the Keep-Alive-Timer in the
141
+ // system threadgroup
142
+ ThreadGroup grp = Thread .currentThread ().getThreadGroup ();
143
+ ThreadGroup parent = null ;
144
+ while ((parent = grp .getParent ()) != null ) {
145
+ grp = parent ;
146
+ }
140
147
141
- keepAliveTimer = new Thread (grp , cache , "Keep-Alive-Timer" );
142
- keepAliveTimer .setDaemon (true );
143
- keepAliveTimer .setPriority (Thread .MAX_PRIORITY - 2 );
144
- // Set the context class loader to null in order to avoid
145
- // keeping a strong reference to an application classloader.
146
- keepAliveTimer .setContextClassLoader (null );
147
- keepAliveTimer .start ();
148
- return null ;
149
- }
150
- });
151
- }
148
+ keepAliveTimer = new Thread (grp , cache , "Keep-Alive-Timer" );
149
+ keepAliveTimer .setDaemon (true );
150
+ keepAliveTimer .setPriority (Thread .MAX_PRIORITY - 2 );
151
+ // Set the context class loader to null in order to avoid
152
+ // keeping a strong reference to an application classloader.
153
+ keepAliveTimer .setContextClassLoader (null );
154
+ keepAliveTimer .start ();
155
+ return null ;
156
+ }
157
+ });
158
+ }
152
159
153
- KeepAliveKey key = new KeepAliveKey (url , obj );
154
- ClientVector v = super .get (key );
160
+ KeepAliveKey key = new KeepAliveKey (url , obj );
161
+ ClientVector v = super .get (key );
155
162
156
- if (v == null ) {
157
- int keepAliveTimeout = http .getKeepAliveTimeout ();
163
+ if (v == null ) {
164
+ int keepAliveTimeout = http .getKeepAliveTimeout ();
158
165
if (keepAliveTimeout == 0 ) {
159
166
keepAliveTimeout = getUserKeepAlive (http .getUsingProxy ());
160
167
if (keepAliveTimeout == -1 ) {
@@ -174,14 +181,19 @@ public Void run() {
174
181
// alive, which could be 0, if the user specified 0 for the property
175
182
assert keepAliveTimeout >= 0 ;
176
183
if (keepAliveTimeout == 0 ) {
177
- http . closeServer () ;
184
+ oldClient = http ;
178
185
} else {
179
186
v = new ClientVector (keepAliveTimeout * 1000 );
180
187
v .put (http );
181
188
super .put (key , v );
182
189
}
183
- } else {
184
- v .put (http );
190
+ } else {
191
+ oldClient = v .put (http );
192
+ }
193
+ }
194
+ // close after releasing locks
195
+ if (oldClient != null ) {
196
+ oldClient .closeServer ();
185
197
}
186
198
}
187
199
@@ -213,7 +225,6 @@ synchronized void removeVector(KeepAliveKey k) {
213
225
* Check to see if this URL has a cached HttpClient
214
226
*/
215
227
public synchronized HttpClient get (URL url , Object obj ) {
216
-
217
228
KeepAliveKey key = new KeepAliveKey (url , obj );
218
229
ClientVector v = super .get (key );
219
230
if (v == null ) { // nothing in cache yet
@@ -232,6 +243,7 @@ public void run() {
232
243
try {
233
244
Thread .sleep (LIFETIME );
234
245
} catch (InterruptedException e ) {}
246
+ List <HttpClient > closeList = null ;
235
247
236
248
// Remove all outdated HttpClients.
237
249
synchronized (this ) {
@@ -241,15 +253,18 @@ public void run() {
241
253
for (KeepAliveKey key : keySet ()) {
242
254
ClientVector v = get (key );
243
255
synchronized (v ) {
244
- KeepAliveEntry e = v .peek ();
256
+ KeepAliveEntry e = v .peekLast ();
245
257
while (e != null ) {
246
258
if ((currentTime - e .idleStartTime ) > v .nap ) {
247
- v .poll ();
248
- e .hc .closeServer ();
259
+ v .pollLast ();
260
+ if (closeList == null ) {
261
+ closeList = new ArrayList <>();
262
+ }
263
+ closeList .add (e .hc );
249
264
} else {
250
265
break ;
251
266
}
252
- e = v .peek ();
267
+ e = v .peekLast ();
253
268
}
254
269
255
270
if (v .isEmpty ()) {
@@ -262,6 +277,12 @@ public void run() {
262
277
removeVector (key );
263
278
}
264
279
}
280
+ // close connections outside cacheLock
281
+ if (closeList != null ) {
282
+ for (HttpClient hc : closeList ) {
283
+ hc .closeServer ();
284
+ }
285
+ }
265
286
} while (!isEmpty ());
266
287
}
267
288
@@ -279,8 +300,8 @@ private void readObject(ObjectInputStream stream)
279
300
}
280
301
}
281
302
282
- /* FILO order for recycling HttpClients, should run in a thread
283
- * to time them out. If > maxConns are in use, block .
303
+ /* LIFO order for reusing HttpClients. Most recent entries at the front.
304
+ * If > maxConns are in use, discard oldest .
284
305
*/
285
306
class ClientVector extends ArrayDeque <KeepAliveEntry > {
286
307
private static final long serialVersionUID = -8680532108106489459L ;
@@ -293,36 +314,37 @@ class ClientVector extends ArrayDeque<KeepAliveEntry> {
293
314
}
294
315
295
316
synchronized HttpClient get () {
296
- if (isEmpty ()) {
317
+ // check the most recent connection, use if still valid
318
+ KeepAliveEntry e = peekFirst ();
319
+ if (e == null ) {
297
320
return null ;
298
321
}
299
322
300
- // Loop until we find a connection that has not timed out
301
- HttpClient hc = null ;
302
323
long currentTime = System .currentTimeMillis ();
303
- do {
304
- KeepAliveEntry e = pop ();
305
- if ((currentTime - e .idleStartTime ) > nap ) {
306
- e .hc .closeServer ();
307
- } else {
308
- hc = e .hc ;
309
- if (KeepAliveCache .logger .isLoggable (PlatformLogger .Level .FINEST )) {
310
- String msg = "cached HttpClient was idle for "
324
+ if ((currentTime - e .idleStartTime ) > nap ) {
325
+ return null ; // all connections stale - will be cleaned up later
326
+ } else {
327
+ pollFirst ();
328
+ if (KeepAliveCache .logger .isLoggable (PlatformLogger .Level .FINEST )) {
329
+ String msg = "cached HttpClient was idle for "
311
330
+ Long .toString (currentTime - e .idleStartTime );
312
- KeepAliveCache .logger .finest (msg );
313
- }
331
+ KeepAliveCache .logger .finest (msg );
314
332
}
315
- } while (( hc == null ) && (! isEmpty ())) ;
316
- return hc ;
333
+ return e . hc ;
334
+ }
317
335
}
318
336
319
337
/* return a still valid, unused HttpClient */
320
- synchronized void put (HttpClient h ) {
338
+ synchronized HttpClient put (HttpClient h ) {
339
+ HttpClient staleClient = null ;
340
+ assert KeepAliveCache .getMaxConnections () > 0 ;
321
341
if (size () >= KeepAliveCache .getMaxConnections ()) {
322
- h .closeServer (); // otherwise the connection remains in limbo
323
- } else {
324
- push (new KeepAliveEntry (h , System .currentTimeMillis ()));
342
+ // remove oldest connection
343
+ staleClient = removeLast ().hc ;
325
344
}
345
+ addFirst (new KeepAliveEntry (h , System .currentTimeMillis ()));
346
+ // close after releasing the locks
347
+ return staleClient ;
326
348
}
327
349
328
350
/* remove an HttpClient */
@@ -350,10 +372,10 @@ private void readObject(ObjectInputStream stream)
350
372
}
351
373
352
374
class KeepAliveKey {
353
- private String protocol = null ;
354
- private String host = null ;
355
- private int port = 0 ;
356
- private Object obj = null ; // additional key, such as socketfactory
375
+ private final String protocol ;
376
+ private final String host ;
377
+ private final int port ;
378
+ private final Object obj ; // additional key, such as socketfactory
357
379
358
380
/**
359
381
* Constructor
@@ -394,8 +416,8 @@ public int hashCode() {
394
416
}
395
417
396
418
class KeepAliveEntry {
397
- HttpClient hc ;
398
- long idleStartTime ;
419
+ final HttpClient hc ;
420
+ final long idleStartTime ;
399
421
400
422
KeepAliveEntry (HttpClient hc , long idleStartTime ) {
401
423
this .hc = hc ;
1 commit comments
openjdk-notifier[bot] commentedon May 14, 2024
Review
Issues