84 /** |
87 /** |
85 * Register this URL and HttpClient (that supports keep-alive) with the cache |
88 * Register this URL and HttpClient (that supports keep-alive) with the cache |
86 * @param url The URL contains info about the host and port |
89 * @param url The URL contains info about the host and port |
87 * @param http The HttpClient to be cached |
90 * @param http The HttpClient to be cached |
88 */ |
91 */ |
89 public synchronized void put(final URL url, Object obj, HttpClient http) { |
92 public void put(final URL url, Object obj, HttpClient http) { |
90 boolean startThread = (keepAliveTimer == null); |
93 cacheLock.lock(); |
91 if (!startThread) { |
94 try { |
92 if (!keepAliveTimer.isAlive()) { |
95 boolean startThread = (keepAliveTimer == null); |
93 startThread = true; |
96 if (!startThread) { |
94 } |
97 if (!keepAliveTimer.isAlive()) { |
95 } |
98 startThread = true; |
96 if (startThread) { |
99 } |
97 clear(); |
100 } |
98 /* Unfortunately, we can't always believe the keep-alive timeout we got |
101 if (startThread) { |
99 * back from the server. If I'm connected through a Netscape proxy |
102 clear(); |
100 * to a server that sent me a keep-alive |
103 /* Unfortunately, we can't always believe the keep-alive timeout we got |
101 * time of 15 sec, the proxy unilaterally terminates my connection |
104 * back from the server. If I'm connected through a Netscape proxy |
102 * The robustness to get around this is in HttpClient.parseHTTP() |
105 * to a server that sent me a keep-alive |
103 */ |
106 * time of 15 sec, the proxy unilaterally terminates my connection |
104 final KeepAliveCache cache = this; |
107 * The robustness to get around this is in HttpClient.parseHTTP() |
105 AccessController.doPrivileged(new PrivilegedAction<>() { |
108 */ |
106 public Void run() { |
109 final KeepAliveCache cache = this; |
107 keepAliveTimer = InnocuousThread.newSystemThread("Keep-Alive-Timer", cache); |
110 AccessController.doPrivileged(new PrivilegedAction<>() { |
108 keepAliveTimer.setDaemon(true); |
111 public Void run() { |
109 keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2); |
112 keepAliveTimer = InnocuousThread.newSystemThread("Keep-Alive-Timer", cache); |
110 keepAliveTimer.start(); |
113 keepAliveTimer.setDaemon(true); |
111 return null; |
114 keepAliveTimer.setPriority(Thread.MAX_PRIORITY - 2); |
112 } |
115 keepAliveTimer.start(); |
113 }); |
116 return null; |
114 } |
117 } |
115 |
118 }); |
116 KeepAliveKey key = new KeepAliveKey(url, obj); |
119 } |
117 ClientVector v = super.get(key); |
120 |
118 |
121 KeepAliveKey key = new KeepAliveKey(url, obj); |
119 if (v == null) { |
122 ClientVector v = super.get(key); |
120 int keepAliveTimeout = http.getKeepAliveTimeout(); |
123 |
121 v = new ClientVector(keepAliveTimeout > 0 ? |
124 if (v == null) { |
122 keepAliveTimeout * 1000 : LIFETIME); |
125 int keepAliveTimeout = http.getKeepAliveTimeout(); |
123 v.put(http); |
126 v = new ClientVector(keepAliveTimeout > 0 ? |
124 super.put(key, v); |
127 keepAliveTimeout * 1000 : LIFETIME); |
125 } else { |
128 v.put(http); |
126 v.put(http); |
129 super.put(key, v); |
|
130 } else { |
|
131 v.put(http); |
|
132 } |
|
133 } finally { |
|
134 cacheLock.unlock(); |
127 } |
135 } |
128 } |
136 } |
129 |
137 |
130 /* remove an obsolete HttpClient from its VectorCache */ |
138 /* remove an obsolete HttpClient from its VectorCache */ |
131 public synchronized void remove(HttpClient h, Object obj) { |
139 public void remove(HttpClient h, Object obj) { |
132 KeepAliveKey key = new KeepAliveKey(h.url, obj); |
140 cacheLock.lock(); |
133 ClientVector v = super.get(key); |
141 try { |
134 if (v != null) { |
142 KeepAliveKey key = new KeepAliveKey(h.url, obj); |
135 v.remove(h); |
143 ClientVector v = super.get(key); |
136 if (v.isEmpty()) { |
144 if (v != null) { |
137 removeVector(key); |
145 v.remove(h); |
138 } |
146 if (v.isEmpty()) { |
139 } |
147 super.remove(key); |
140 } |
148 } |
141 |
149 } |
142 /* called by a clientVector thread when all its connections have timed out |
150 } finally { |
143 * and that vector of connections should be removed. |
151 cacheLock.unlock(); |
144 */ |
152 } |
145 synchronized void removeVector(KeepAliveKey k) { |
|
146 super.remove(k); |
|
147 } |
153 } |
148 |
154 |
149 /** |
155 /** |
150 * Check to see if this URL has a cached HttpClient |
156 * Check to see if this URL has a cached HttpClient |
151 */ |
157 */ |
152 public synchronized HttpClient get(URL url, Object obj) { |
158 public HttpClient get(URL url, Object obj) { |
153 KeepAliveKey key = new KeepAliveKey(url, obj); |
159 cacheLock.lock(); |
154 ClientVector v = super.get(key); |
160 try { |
155 if (v == null) { // nothing in cache yet |
161 KeepAliveKey key = new KeepAliveKey(url, obj); |
156 return null; |
162 ClientVector v = super.get(key); |
157 } |
163 if (v == null) { // nothing in cache yet |
158 return v.get(); |
164 return null; |
|
165 } |
|
166 return v.get(); |
|
167 } finally { |
|
168 cacheLock.unlock(); |
|
169 } |
159 } |
170 } |
160 |
171 |
161 /* Sleeps for an alloted timeout, then checks for timed out connections. |
172 /* Sleeps for an alloted timeout, then checks for timed out connections. |
162 * Errs on the side of caution (leave connections idle for a relatively |
173 * Errs on the side of caution (leave connections idle for a relatively |
163 * short time). |
174 * short time). |
168 try { |
179 try { |
169 Thread.sleep(LIFETIME); |
180 Thread.sleep(LIFETIME); |
170 } catch (InterruptedException e) {} |
181 } catch (InterruptedException e) {} |
171 |
182 |
172 // Remove all outdated HttpClients. |
183 // Remove all outdated HttpClients. |
173 synchronized (this) { |
184 cacheLock.lock(); |
|
185 try { |
174 long currentTime = System.currentTimeMillis(); |
186 long currentTime = System.currentTimeMillis(); |
175 List<KeepAliveKey> keysToRemove = new ArrayList<>(); |
187 List<KeepAliveKey> keysToRemove = new ArrayList<>(); |
176 |
188 |
177 for (KeepAliveKey key : keySet()) { |
189 for (KeepAliveKey key : keySet()) { |
178 ClientVector v = get(key); |
190 ClientVector v = get(key); |
179 synchronized (v) { |
191 final Lock lock = v.lock; |
|
192 lock.lock(); |
|
193 try { |
180 KeepAliveEntry e = v.peek(); |
194 KeepAliveEntry e = v.peek(); |
181 while (e != null) { |
195 while (e != null) { |
182 if ((currentTime - e.idleStartTime) > v.nap) { |
196 if ((currentTime - e.idleStartTime) > v.nap) { |
183 v.poll(); |
197 v.poll(); |
184 e.hc.closeServer(); |
198 e.hc.closeServer(); |
221 * to time them out. If > maxConns are in use, block. |
239 * to time them out. If > maxConns are in use, block. |
222 */ |
240 */ |
223 class ClientVector extends ArrayDeque<KeepAliveEntry> { |
241 class ClientVector extends ArrayDeque<KeepAliveEntry> { |
224 @java.io.Serial |
242 @java.io.Serial |
225 private static final long serialVersionUID = -8680532108106489459L; |
243 private static final long serialVersionUID = -8680532108106489459L; |
|
244 final ReentrantLock lock = new ReentrantLock(); |
226 |
245 |
227 // sleep time in milliseconds, before cache clear |
246 // sleep time in milliseconds, before cache clear |
228 int nap; |
247 int nap; |
229 |
248 |
230 ClientVector(int nap) { |
249 ClientVector(int nap) { |
231 this.nap = nap; |
250 this.nap = nap; |
232 } |
251 } |
233 |
252 |
234 synchronized HttpClient get() { |
253 HttpClient get() { |
235 if (isEmpty()) { |
254 lock.lock(); |
236 return null; |
255 try { |
237 } |
256 if (isEmpty()) { |
238 |
257 return null; |
239 // Loop until we find a connection that has not timed out |
258 } |
240 HttpClient hc = null; |
259 |
241 long currentTime = System.currentTimeMillis(); |
260 // Loop until we find a connection that has not timed out |
242 do { |
261 HttpClient hc = null; |
243 KeepAliveEntry e = pop(); |
262 long currentTime = System.currentTimeMillis(); |
244 if ((currentTime - e.idleStartTime) > nap) { |
263 do { |
245 e.hc.closeServer(); |
264 KeepAliveEntry e = pop(); |
|
265 if ((currentTime - e.idleStartTime) > nap) { |
|
266 e.hc.closeServer(); |
|
267 } else { |
|
268 hc = e.hc; |
|
269 } |
|
270 } while ((hc == null) && (!isEmpty())); |
|
271 return hc; |
|
272 } finally { |
|
273 lock.unlock(); |
|
274 } |
|
275 } |
|
276 |
|
277 /* return a still valid, unused HttpClient */ |
|
278 void put(HttpClient h) { |
|
279 lock.lock(); |
|
280 try { |
|
281 if (size() >= KeepAliveCache.getMaxConnections()) { |
|
282 h.closeServer(); // otherwise the connection remains in limbo |
246 } else { |
283 } else { |
247 hc = e.hc; |
284 push(new KeepAliveEntry(h, System.currentTimeMillis())); |
248 } |
285 } |
249 } while ((hc == null) && (!isEmpty())); |
286 } finally { |
250 return hc; |
287 lock.unlock(); |
251 } |
|
252 |
|
253 /* return a still valid, unused HttpClient */ |
|
254 synchronized void put(HttpClient h) { |
|
255 if (size() >= KeepAliveCache.getMaxConnections()) { |
|
256 h.closeServer(); // otherwise the connection remains in limbo |
|
257 } else { |
|
258 push(new KeepAliveEntry(h, System.currentTimeMillis())); |
|
259 } |
288 } |
260 } |
289 } |
261 |
290 |
262 /* remove an HttpClient */ |
291 /* remove an HttpClient */ |
263 synchronized boolean remove(HttpClient h) { |
292 boolean remove(HttpClient h) { |
264 for (KeepAliveEntry curr : this) { |
293 lock.lock(); |
265 if (curr.hc == h) { |
294 try { |
266 return super.remove(curr); |
295 for (KeepAliveEntry curr : this) { |
267 } |
296 if (curr.hc == h) { |
268 } |
297 return super.remove(curr); |
269 return false; |
298 } |
|
299 } |
|
300 return false; |
|
301 } finally { |
|
302 lock.unlock(); |
|
303 } |
270 } |
304 } |
271 |
305 |
272 /* |
306 /* |
273 * Do not serialize this class! |
307 * Do not serialize this class! |
274 */ |
308 */ |