1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.http.impl.conn;
29
30 import java.io.IOException;
31 import java.util.concurrent.TimeUnit;
32
33 import org.apache.http.annotation.GuardedBy;
34 import org.apache.http.annotation.ThreadSafe;
35
36 import org.apache.commons.logging.Log;
37 import org.apache.commons.logging.LogFactory;
38 import org.apache.http.conn.ClientConnectionManager;
39 import org.apache.http.conn.ClientConnectionOperator;
40 import org.apache.http.conn.ClientConnectionRequest;
41 import org.apache.http.conn.ManagedClientConnection;
42 import org.apache.http.conn.routing.HttpRoute;
43 import org.apache.http.conn.routing.RouteTracker;
44 import org.apache.http.conn.scheme.SchemeRegistry;
45 import org.apache.http.params.HttpParams;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 @ThreadSafe
64 @Deprecated
65 public class SingleClientConnManager implements ClientConnectionManager {
66
67 private final Log log = LogFactory.getLog(getClass());
68
69
70 public final static String MISUSE_MESSAGE =
71 "Invalid use of SingleClientConnManager: connection still allocated.\n" +
72 "Make sure to release the connection before allocating another one.";
73
74
75 protected final SchemeRegistry schemeRegistry;
76
77
78 protected final ClientConnectionOperator connOperator;
79
80
81 protected final boolean alwaysShutDown;
82
83
84 @GuardedBy("this")
85 protected volatile PoolEntry uniquePoolEntry;
86
87
88 @GuardedBy("this")
89 protected volatile ConnAdapter managedConn;
90
91
92 @GuardedBy("this")
93 protected volatile long lastReleaseTime;
94
95
96 @GuardedBy("this")
97 protected volatile long connectionExpiresTime;
98
99
100 protected volatile boolean isShutDown;
101
102
103
104
105
106
107
108
109
110 public SingleClientConnManager(HttpParams params,
111 SchemeRegistry schreg) {
112 this(schreg);
113 }
114
115
116
117
118
119 public SingleClientConnManager(final SchemeRegistry schreg) {
120 if (schreg == null) {
121 throw new IllegalArgumentException
122 ("Scheme registry must not be null.");
123 }
124 this.schemeRegistry = schreg;
125 this.connOperator = createConnectionOperator(schreg);
126 this.uniquePoolEntry = new PoolEntry();
127 this.managedConn = null;
128 this.lastReleaseTime = -1L;
129 this.alwaysShutDown = false;
130 this.isShutDown = false;
131 }
132
133
134
135
136 public SingleClientConnManager() {
137 this(SchemeRegistryFactory.createDefault());
138 }
139
140 @Override
141 protected void finalize() throws Throwable {
142 try {
143 shutdown();
144 } finally {
145 super.finalize();
146 }
147 }
148
149 public SchemeRegistry getSchemeRegistry() {
150 return this.schemeRegistry;
151 }
152
153
154
155
156
157
158
159
160
161
162
163
164
165 protected ClientConnectionOperator
166 createConnectionOperator(SchemeRegistry schreg) {
167 return new DefaultClientConnectionOperator(schreg);
168 }
169
170
171
172
173
174
175 protected final void assertStillUp() throws IllegalStateException {
176 if (this.isShutDown)
177 throw new IllegalStateException("Manager is shut down.");
178 }
179
180 public final ClientConnectionRequest requestConnection(
181 final HttpRoute route,
182 final Object state) {
183
184 return new ClientConnectionRequest() {
185
186 public void abortRequest() {
187
188 }
189
190 public ManagedClientConnection getConnection(
191 long timeout, TimeUnit tunit) {
192 return SingleClientConnManager.this.getConnection(
193 route, state);
194 }
195
196 };
197 }
198
199
200
201
202
203
204
205
206
207 public ManagedClientConnection getConnection(HttpRoute route, Object state) {
208 if (route == null) {
209 throw new IllegalArgumentException("Route may not be null.");
210 }
211 assertStillUp();
212
213 if (log.isDebugEnabled()) {
214 log.debug("Get connection for route " + route);
215 }
216
217 synchronized (this) {
218 if (managedConn != null)
219 throw new IllegalStateException(MISUSE_MESSAGE);
220
221
222 boolean recreate = false;
223 boolean shutdown = false;
224
225
226 closeExpiredConnections();
227
228 if (uniquePoolEntry.connection.isOpen()) {
229 RouteTracker tracker = uniquePoolEntry.tracker;
230 shutdown = (tracker == null ||
231 !tracker.toRoute().equals(route));
232 } else {
233
234
235
236
237
238 recreate = true;
239 }
240
241 if (shutdown) {
242 recreate = true;
243 try {
244 uniquePoolEntry.shutdown();
245 } catch (IOException iox) {
246 log.debug("Problem shutting down connection.", iox);
247 }
248 }
249
250 if (recreate)
251 uniquePoolEntry = new PoolEntry();
252
253 managedConn = new ConnAdapter(uniquePoolEntry, route);
254
255 return managedConn;
256 }
257 }
258
259 public void releaseConnection(
260 ManagedClientConnection conn,
261 long validDuration, TimeUnit timeUnit) {
262 assertStillUp();
263
264 if (!(conn instanceof ConnAdapter)) {
265 throw new IllegalArgumentException
266 ("Connection class mismatch, " +
267 "connection not obtained from this manager.");
268 }
269
270 if (log.isDebugEnabled()) {
271 log.debug("Releasing connection " + conn);
272 }
273
274 ConnAdapter sca = (ConnAdapter) conn;
275 synchronized (sca) {
276 if (sca.poolEntry == null)
277 return;
278 ClientConnectionManager manager = sca.getManager();
279 if (manager != null && manager != this) {
280 throw new IllegalArgumentException
281 ("Connection not obtained from this manager.");
282 }
283 try {
284
285 if (sca.isOpen() && (this.alwaysShutDown ||
286 !sca.isMarkedReusable())
287 ) {
288 if (log.isDebugEnabled()) {
289 log.debug
290 ("Released connection open but not reusable.");
291 }
292
293
294
295
296 sca.shutdown();
297 }
298 } catch (IOException iox) {
299 if (log.isDebugEnabled())
300 log.debug("Exception shutting down released connection.",
301 iox);
302 } finally {
303 sca.detach();
304 synchronized (this) {
305 managedConn = null;
306 lastReleaseTime = System.currentTimeMillis();
307 if(validDuration > 0)
308 connectionExpiresTime = timeUnit.toMillis(validDuration) + lastReleaseTime;
309 else
310 connectionExpiresTime = Long.MAX_VALUE;
311 }
312 }
313 }
314 }
315
316 public void closeExpiredConnections() {
317 long time = connectionExpiresTime;
318 if (System.currentTimeMillis() >= time) {
319 closeIdleConnections(0, TimeUnit.MILLISECONDS);
320 }
321 }
322
323 public void closeIdleConnections(long idletime, TimeUnit tunit) {
324 assertStillUp();
325
326
327 if (tunit == null) {
328 throw new IllegalArgumentException("Time unit must not be null.");
329 }
330
331 synchronized (this) {
332 if ((managedConn == null) && uniquePoolEntry.connection.isOpen()) {
333 final long cutoff =
334 System.currentTimeMillis() - tunit.toMillis(idletime);
335 if (lastReleaseTime <= cutoff) {
336 try {
337 uniquePoolEntry.close();
338 } catch (IOException iox) {
339
340 log.debug("Problem closing idle connection.", iox);
341 }
342 }
343 }
344 }
345 }
346
347 public void shutdown() {
348 this.isShutDown = true;
349 synchronized (this) {
350 try {
351 if (uniquePoolEntry != null)
352 uniquePoolEntry.shutdown();
353 } catch (IOException iox) {
354
355 log.debug("Problem while shutting down manager.", iox);
356 } finally {
357 uniquePoolEntry = null;
358 managedConn = null;
359 }
360 }
361 }
362
363 protected void revokeConnection() {
364 ConnAdapter conn = managedConn;
365 if (conn == null)
366 return;
367 conn.detach();
368
369 synchronized (this) {
370 try {
371 uniquePoolEntry.shutdown();
372 } catch (IOException iox) {
373
374 log.debug("Problem while shutting down connection.", iox);
375 }
376 }
377 }
378
379
380
381
382 protected class PoolEntry extends AbstractPoolEntry {
383
384
385
386
387
388 protected PoolEntry() {
389 super(SingleClientConnManager.this.connOperator, null);
390 }
391
392
393
394
395 protected void close() throws IOException {
396 shutdownEntry();
397 if (connection.isOpen())
398 connection.close();
399 }
400
401
402
403
404 protected void shutdown() throws IOException {
405 shutdownEntry();
406 if (connection.isOpen())
407 connection.shutdown();
408 }
409
410 }
411
412
413
414
415 protected class ConnAdapter extends AbstractPooledConnAdapter {
416
417
418
419
420
421
422
423 protected ConnAdapter(PoolEntry entry, HttpRoute route) {
424 super(SingleClientConnManager.this, entry);
425 markReusable();
426 entry.route = route;
427 }
428
429 }
430
431 }