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 package org.apache.http.impl.conn;
28
29 import java.io.IOException;
30 import java.util.concurrent.ExecutionException;
31 import java.util.concurrent.Future;
32 import java.util.concurrent.TimeUnit;
33 import java.util.concurrent.TimeoutException;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 import org.apache.http.annotation.ThreadSafe;
38 import org.apache.http.conn.routing.HttpRoute;
39 import org.apache.http.conn.scheme.SchemeRegistry;
40 import org.apache.http.conn.ClientConnectionManager;
41 import org.apache.http.conn.ClientConnectionOperator;
42 import org.apache.http.conn.ClientConnectionRequest;
43 import org.apache.http.conn.ConnectionPoolTimeoutException;
44 import org.apache.http.conn.ManagedClientConnection;
45 import org.apache.http.conn.OperatedClientConnection;
46 import org.apache.http.pool.ConnPoolControl;
47 import org.apache.http.pool.PoolStats;
48 import org.apache.http.impl.conn.DefaultClientConnectionOperator;
49 import org.apache.http.impl.conn.SchemeRegistryFactory;
50 import org.apache.http.conn.DnsResolver;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 @ThreadSafe
71 public class PoolingClientConnectionManager implements ClientConnectionManager, ConnPoolControl<HttpRoute> {
72
73 private final Log log = LogFactory.getLog(getClass());
74
75 private final SchemeRegistry schemeRegistry;
76
77 private final HttpConnPool pool;
78
79 private final ClientConnectionOperator operator;
80
81
82 private final DnsResolver dnsResolver;
83
84 public PoolingClientConnectionManager(final SchemeRegistry schreg) {
85 this(schreg, -1, TimeUnit.MILLISECONDS);
86 }
87
88 public PoolingClientConnectionManager(final SchemeRegistry schreg,final DnsResolver dnsResolver) {
89 this(schreg, -1, TimeUnit.MILLISECONDS,dnsResolver);
90 }
91
92 public PoolingClientConnectionManager() {
93 this(SchemeRegistryFactory.createDefault());
94 }
95
96 public PoolingClientConnectionManager(
97 final SchemeRegistry schemeRegistry,
98 final long timeToLive, final TimeUnit tunit) {
99 this(schemeRegistry, timeToLive, tunit, new SystemDefaultDnsResolver());
100 }
101
102 public PoolingClientConnectionManager(final SchemeRegistry schemeRegistry,
103 final long timeToLive, final TimeUnit tunit,
104 final DnsResolver dnsResolver) {
105 super();
106 if (schemeRegistry == null) {
107 throw new IllegalArgumentException("Scheme registry may not be null");
108 }
109 if (dnsResolver == null) {
110 throw new IllegalArgumentException("DNS resolver may not be null");
111 }
112 this.schemeRegistry = schemeRegistry;
113 this.dnsResolver = dnsResolver;
114 this.operator = createConnectionOperator(schemeRegistry);
115 this.pool = new HttpConnPool(this.log, this.operator, 2, 20, timeToLive, tunit);
116 }
117
118 @Override
119 protected void finalize() throws Throwable {
120 try {
121 shutdown();
122 } finally {
123 super.finalize();
124 }
125 }
126
127
128
129
130
131
132
133
134
135
136
137
138
139 protected ClientConnectionOperator createConnectionOperator(SchemeRegistry schreg) {
140 return new DefaultClientConnectionOperator(schreg, this.dnsResolver);
141 }
142
143 public SchemeRegistry getSchemeRegistry() {
144 return this.schemeRegistry;
145 }
146
147 private String format(final HttpRoute route, final Object state) {
148 StringBuilder buf = new StringBuilder();
149 buf.append("[route: ").append(route).append("]");
150 if (state != null) {
151 buf.append("[state: ").append(state).append("]");
152 }
153 return buf.toString();
154 }
155
156 private String formatStats(final HttpRoute route) {
157 StringBuilder buf = new StringBuilder();
158 PoolStats totals = this.pool.getTotalStats();
159 PoolStats stats = this.pool.getStats(route);
160 buf.append("[total kept alive: ").append(totals.getAvailable()).append("; ");
161 buf.append("route allocated: ").append(stats.getLeased() + stats.getAvailable());
162 buf.append(" of ").append(stats.getMax()).append("; ");
163 buf.append("total allocated: ").append(totals.getLeased() + totals.getAvailable());
164 buf.append(" of ").append(totals.getMax()).append("]");
165 return buf.toString();
166 }
167
168 private String format(final HttpPoolEntry entry) {
169 StringBuilder buf = new StringBuilder();
170 buf.append("[id: ").append(entry.getId()).append("]");
171 buf.append("[route: ").append(entry.getRoute()).append("]");
172 Object state = entry.getState();
173 if (state != null) {
174 buf.append("[state: ").append(state).append("]");
175 }
176 return buf.toString();
177 }
178
179 public ClientConnectionRequest requestConnection(
180 final HttpRoute route,
181 final Object state) {
182 if (route == null) {
183 throw new IllegalArgumentException("HTTP route may not be null");
184 }
185 if (this.log.isDebugEnabled()) {
186 this.log.debug("Connection request: " + format(route, state) + formatStats(route));
187 }
188 final Future<HttpPoolEntry> future = this.pool.lease(route, state);
189
190 return new ClientConnectionRequest() {
191
192 public void abortRequest() {
193 future.cancel(true);
194 }
195
196 public ManagedClientConnection getConnection(
197 final long timeout,
198 final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException {
199 return leaseConnection(future, timeout, tunit);
200 }
201
202 };
203
204 }
205
206 ManagedClientConnection leaseConnection(
207 final Future<HttpPoolEntry> future,
208 final long timeout,
209 final TimeUnit tunit) throws InterruptedException, ConnectionPoolTimeoutException {
210 HttpPoolEntry entry;
211 try {
212 entry = future.get(timeout, tunit);
213 if (entry == null || future.isCancelled()) {
214 throw new InterruptedException();
215 }
216 if (entry.getConnection() == null) {
217 throw new IllegalStateException("Pool entry with no connection");
218 }
219 if (this.log.isDebugEnabled()) {
220 this.log.debug("Connection leased: " + format(entry) + formatStats(entry.getRoute()));
221 }
222 return new ManagedClientConnectionImpl(this, this.operator, entry);
223 } catch (ExecutionException ex) {
224 Throwable cause = ex.getCause();
225 if (cause == null) {
226 cause = ex;
227 }
228 this.log.error("Unexpected exception leasing connection from pool", cause);
229
230 throw new InterruptedException();
231 } catch (TimeoutException ex) {
232 throw new ConnectionPoolTimeoutException("Timeout waiting for connection from pool");
233 }
234 }
235
236 public void releaseConnection(
237 final ManagedClientConnection conn, final long keepalive, final TimeUnit tunit) {
238
239 if (!(conn instanceof ManagedClientConnectionImpl)) {
240 throw new IllegalArgumentException
241 ("Connection class mismatch, " +
242 "connection not obtained from this manager.");
243 }
244 ManagedClientConnectionImpl managedConn = (ManagedClientConnectionImpl) conn;
245 if (managedConn.getManager() != this) {
246 throw new IllegalStateException("Connection not obtained from this manager.");
247 }
248
249 synchronized (managedConn) {
250 HttpPoolEntry entry = managedConn.detach();
251 if (entry == null) {
252 return;
253 }
254 try {
255 if (managedConn.isOpen() && !managedConn.isMarkedReusable()) {
256 try {
257 managedConn.shutdown();
258 } catch (IOException iox) {
259 if (this.log.isDebugEnabled()) {
260 this.log.debug("I/O exception shutting down released connection", iox);
261 }
262 }
263 }
264
265 if (managedConn.isMarkedReusable()) {
266 entry.updateExpiry(keepalive, tunit != null ? tunit : TimeUnit.MILLISECONDS);
267 if (this.log.isDebugEnabled()) {
268 String s;
269 if (keepalive > 0) {
270 s = "for " + keepalive + " " + tunit;
271 } else {
272 s = "indefinitely";
273 }
274 this.log.debug("Connection " + format(entry) + " can be kept alive " + s);
275 }
276 }
277 } finally {
278 this.pool.release(entry, managedConn.isMarkedReusable());
279 }
280 if (this.log.isDebugEnabled()) {
281 this.log.debug("Connection released: " + format(entry) + formatStats(entry.getRoute()));
282 }
283 }
284 }
285
286 public void shutdown() {
287 this.log.debug("Connection manager is shutting down");
288 try {
289 this.pool.shutdown();
290 } catch (IOException ex) {
291 this.log.debug("I/O exception shutting down connection manager", ex);
292 }
293 this.log.debug("Connection manager shut down");
294 }
295
296 public void closeIdleConnections(long idleTimeout, TimeUnit tunit) {
297 if (this.log.isDebugEnabled()) {
298 this.log.debug("Closing connections idle longer than " + idleTimeout + " " + tunit);
299 }
300 this.pool.closeIdle(idleTimeout, tunit);
301 }
302
303 public void closeExpiredConnections() {
304 this.log.debug("Closing expired connections");
305 this.pool.closeExpired();
306 }
307
308 public int getMaxTotal() {
309 return this.pool.getMaxTotal();
310 }
311
312 public void setMaxTotal(int max) {
313 this.pool.setMaxTotal(max);
314 }
315
316 public int getDefaultMaxPerRoute() {
317 return this.pool.getDefaultMaxPerRoute();
318 }
319
320 public void setDefaultMaxPerRoute(int max) {
321 this.pool.setDefaultMaxPerRoute(max);
322 }
323
324 public int getMaxPerRoute(final HttpRoute route) {
325 return this.pool.getMaxPerRoute(route);
326 }
327
328 public void setMaxPerRoute(final HttpRoute route, int max) {
329 this.pool.setMaxPerRoute(route, max);
330 }
331
332 public PoolStats getTotalStats() {
333 return this.pool.getTotalStats();
334 }
335
336 public PoolStats getStats(final HttpRoute route) {
337 return this.pool.getStats(route);
338 }
339
340 }
341