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.Closeable;
31 import java.io.IOException;
32 import java.net.InetSocketAddress;
33 import java.util.Date;
34 import java.util.concurrent.TimeUnit;
35 import java.util.concurrent.atomic.AtomicBoolean;
36
37 import org.apache.commons.logging.Log;
38 import org.apache.commons.logging.LogFactory;
39 import org.apache.http.HttpClientConnection;
40 import org.apache.http.HttpHost;
41 import org.apache.http.annotation.Contract;
42 import org.apache.http.annotation.ThreadingBehavior;
43 import org.apache.http.config.ConnectionConfig;
44 import org.apache.http.config.Lookup;
45 import org.apache.http.config.Registry;
46 import org.apache.http.config.RegistryBuilder;
47 import org.apache.http.config.SocketConfig;
48 import org.apache.http.conn.ConnectionRequest;
49 import org.apache.http.conn.DnsResolver;
50 import org.apache.http.conn.HttpClientConnectionManager;
51 import org.apache.http.conn.HttpClientConnectionOperator;
52 import org.apache.http.conn.HttpConnectionFactory;
53 import org.apache.http.conn.ManagedHttpClientConnection;
54 import org.apache.http.conn.SchemePortResolver;
55 import org.apache.http.conn.routing.HttpRoute;
56 import org.apache.http.conn.socket.ConnectionSocketFactory;
57 import org.apache.http.conn.socket.PlainConnectionSocketFactory;
58 import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
59 import org.apache.http.protocol.HttpContext;
60 import org.apache.http.util.Args;
61 import org.apache.http.util.Asserts;
62 import org.apache.http.util.LangUtils;
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82 @Contract(threading = ThreadingBehavior.SAFE_CONDITIONAL)
83 public class BasicHttpClientConnectionManager implements HttpClientConnectionManager, Closeable {
84
85 private final Log log = LogFactory.getLog(getClass());
86
87 private final HttpClientConnectionOperator connectionOperator;
88 private final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory;
89
90 private ManagedHttpClientConnection conn;
91 private HttpRoute route;
92 private Object state;
93 private long updated;
94 private long expiry;
95 private boolean leased;
96 private SocketConfig socketConfig;
97 private ConnectionConfig connConfig;
98
99 private final AtomicBoolean isShutdown;
100
101 private static Registry<ConnectionSocketFactory> getDefaultRegistry() {
102 return RegistryBuilder.<ConnectionSocketFactory>create()
103 .register("http", PlainConnectionSocketFactory.getSocketFactory())
104 .register("https", SSLConnectionSocketFactory.getSocketFactory())
105 .build();
106 }
107
108 public BasicHttpClientConnectionManager(
109 final Lookup<ConnectionSocketFactory> socketFactoryRegistry,
110 final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory,
111 final SchemePortResolver schemePortResolver,
112 final DnsResolver dnsResolver) {
113 this(
114 new DefaultHttpClientConnectionOperator(socketFactoryRegistry, schemePortResolver, dnsResolver),
115 connFactory
116 );
117 }
118
119
120
121
122 public BasicHttpClientConnectionManager(
123 final HttpClientConnectionOperator httpClientConnectionOperator,
124 final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory) {
125 super();
126 this.connectionOperator = Args.notNull(httpClientConnectionOperator, "Connection operator");
127 this.connFactory = connFactory != null ? connFactory : ManagedHttpClientConnectionFactory.INSTANCE;
128 this.expiry = Long.MAX_VALUE;
129 this.socketConfig = SocketConfig.DEFAULT;
130 this.connConfig = ConnectionConfig.DEFAULT;
131 this.isShutdown = new AtomicBoolean(false);
132 }
133
134 public BasicHttpClientConnectionManager(
135 final Lookup<ConnectionSocketFactory> socketFactoryRegistry,
136 final HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connFactory) {
137 this(socketFactoryRegistry, connFactory, null, null);
138 }
139
140 public BasicHttpClientConnectionManager(
141 final Lookup<ConnectionSocketFactory> socketFactoryRegistry) {
142 this(socketFactoryRegistry, null, null, null);
143 }
144
145 public BasicHttpClientConnectionManager() {
146 this(getDefaultRegistry(), null, null, null);
147 }
148
149 @Override
150 protected void finalize() throws Throwable {
151 try {
152 shutdown();
153 } finally {
154 super.finalize();
155 }
156 }
157
158 @Override
159 public void close() {
160 if (this.isShutdown.compareAndSet(false, true)) {
161 closeConnection();
162 }
163 }
164
165 HttpRoute getRoute() {
166 return route;
167 }
168
169 Object getState() {
170 return state;
171 }
172
173 public synchronized SocketConfig getSocketConfig() {
174 return socketConfig;
175 }
176
177 public synchronized void setSocketConfig(final SocketConfig socketConfig) {
178 this.socketConfig = socketConfig != null ? socketConfig : SocketConfig.DEFAULT;
179 }
180
181 public synchronized ConnectionConfig getConnectionConfig() {
182 return connConfig;
183 }
184
185 public synchronized void setConnectionConfig(final ConnectionConfig connConfig) {
186 this.connConfig = connConfig != null ? connConfig : ConnectionConfig.DEFAULT;
187 }
188
189 @Override
190 public final ConnectionRequest requestConnection(
191 final HttpRoute route,
192 final Object state) {
193 Args.notNull(route, "Route");
194 return new ConnectionRequest() {
195
196 @Override
197 public boolean cancel() {
198
199 return false;
200 }
201
202 @Override
203 public HttpClientConnection get(final long timeout, final TimeUnit timeUnit) {
204 return BasicHttpClientConnectionManager.this.getConnection(
205 route, state);
206 }
207
208 };
209 }
210
211 private synchronized void closeConnection() {
212 if (this.conn != null) {
213 this.log.debug("Closing connection");
214 try {
215 this.conn.close();
216 } catch (final IOException iox) {
217 if (this.log.isDebugEnabled()) {
218 this.log.debug("I/O exception closing connection", iox);
219 }
220 }
221 this.conn = null;
222 }
223 }
224
225 private void checkExpiry() {
226 if (this.conn != null && System.currentTimeMillis() >= this.expiry) {
227 if (this.log.isDebugEnabled()) {
228 this.log.debug("Connection expired @ " + new Date(this.expiry));
229 }
230 closeConnection();
231 }
232 }
233
234 synchronized HttpClientConnection getConnection(final HttpRoute route, final Object state) {
235 Asserts.check(!this.isShutdown.get(), "Connection manager has been shut down");
236 if (this.log.isDebugEnabled()) {
237 this.log.debug("Get connection for route " + route);
238 }
239 Asserts.check(!this.leased, "Connection is still allocated");
240 if (!LangUtils.equals(this.route, route) || !LangUtils.equals(this.state, state)) {
241 closeConnection();
242 }
243 this.route = route;
244 this.state = state;
245 checkExpiry();
246 if (this.conn == null) {
247 this.conn = this.connFactory.create(route, this.connConfig);
248 }
249 this.conn.setSocketTimeout(this.socketConfig.getSoTimeout());
250 this.leased = true;
251 return this.conn;
252 }
253
254 @Override
255 public synchronized void releaseConnection(
256 final HttpClientConnection conn,
257 final Object state,
258 final long keepalive, final TimeUnit timeUnit) {
259 Args.notNull(conn, "Connection");
260 Asserts.check(conn == this.conn, "Connection not obtained from this manager");
261 if (this.log.isDebugEnabled()) {
262 this.log.debug("Releasing connection " + conn);
263 }
264 if (this.isShutdown.get()) {
265 return;
266 }
267 try {
268 this.updated = System.currentTimeMillis();
269 if (!this.conn.isOpen()) {
270 this.conn = null;
271 this.route = null;
272 this.conn = null;
273 this.expiry = Long.MAX_VALUE;
274 } else {
275 this.state = state;
276 this.conn.setSocketTimeout(0);
277 if (this.log.isDebugEnabled()) {
278 final String s;
279 if (keepalive > 0) {
280 s = "for " + keepalive + " " + timeUnit;
281 } else {
282 s = "indefinitely";
283 }
284 this.log.debug("Connection can be kept alive " + s);
285 }
286 if (keepalive > 0) {
287 this.expiry = this.updated + timeUnit.toMillis(keepalive);
288 } else {
289 this.expiry = Long.MAX_VALUE;
290 }
291 }
292 } finally {
293 this.leased = false;
294 }
295 }
296
297 @Override
298 public void connect(
299 final HttpClientConnection conn,
300 final HttpRoute route,
301 final int connectTimeout,
302 final HttpContext context) throws IOException {
303 Args.notNull(conn, "Connection");
304 Args.notNull(route, "HTTP route");
305 Asserts.check(conn == this.conn, "Connection not obtained from this manager");
306 final HttpHost host;
307 if (route.getProxyHost() != null) {
308 host = route.getProxyHost();
309 } else {
310 host = route.getTargetHost();
311 }
312 final InetSocketAddress localAddress = route.getLocalSocketAddress();
313 this.connectionOperator.connect(this.conn, host, localAddress,
314 connectTimeout, this.socketConfig, context);
315 }
316
317 @Override
318 public void upgrade(
319 final HttpClientConnection conn,
320 final HttpRoute route,
321 final HttpContext context) throws IOException {
322 Args.notNull(conn, "Connection");
323 Args.notNull(route, "HTTP route");
324 Asserts.check(conn == this.conn, "Connection not obtained from this manager");
325 this.connectionOperator.upgrade(this.conn, route.getTargetHost(), context);
326 }
327
328 @Override
329 public void routeComplete(
330 final HttpClientConnection conn,
331 final HttpRoute route,
332 final HttpContext context) throws IOException {
333 }
334
335 @Override
336 public synchronized void closeExpiredConnections() {
337 if (this.isShutdown.get()) {
338 return;
339 }
340 if (!this.leased) {
341 checkExpiry();
342 }
343 }
344
345 @Override
346 public synchronized void closeIdleConnections(final long idletime, final TimeUnit timeUnit) {
347 Args.notNull(timeUnit, "Time unit");
348 if (this.isShutdown.get()) {
349 return;
350 }
351 if (!this.leased) {
352 long time = timeUnit.toMillis(idletime);
353 if (time < 0) {
354 time = 0;
355 }
356 final long deadline = System.currentTimeMillis() - time;
357 if (this.updated <= deadline) {
358 closeConnection();
359 }
360 }
361 }
362
363 @Override
364 public void shutdown() {
365 if (this.isShutdown.compareAndSet(false, true)) {
366 if (this.conn != null) {
367 this.log.debug("Shutting down connection");
368 try {
369 this.conn.shutdown();
370 } catch (final IOException iox) {
371 if (this.log.isDebugEnabled()) {
372 this.log.debug("I/O exception shutting down connection", iox);
373 }
374 }
375 this.conn = null;
376 }
377 }
378 }
379
380 }