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 import java.util.concurrent.atomic.AtomicLong;
33
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36 import org.apache.http.HttpClientConnection;
37 import org.apache.http.annotation.GuardedBy;
38 import org.apache.http.annotation.ThreadSafe;
39 import org.apache.http.conn.ClientConnectionManager;
40 import org.apache.http.conn.ClientConnectionOperator;
41 import org.apache.http.conn.ClientConnectionRequest;
42 import org.apache.http.conn.ManagedClientConnection;
43 import org.apache.http.conn.OperatedClientConnection;
44 import org.apache.http.conn.routing.HttpRoute;
45 import org.apache.http.conn.scheme.SchemeRegistry;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 @ThreadSafe
64 public class BasicClientConnectionManager implements ClientConnectionManager {
65
66 private final Log log = LogFactory.getLog(getClass());
67
68 private static final AtomicLong COUNTER = new AtomicLong();
69
70
71 public final static String MISUSE_MESSAGE =
72 "Invalid use of BasicClientConnManager: connection still allocated.\n" +
73 "Make sure to release the connection before allocating another one.";
74
75
76 private final SchemeRegistry schemeRegistry;
77
78
79 private final ClientConnectionOperator connOperator;
80
81
82 @GuardedBy("this")
83 private HttpPoolEntry poolEntry;
84
85
86 @GuardedBy("this")
87 private ManagedClientConnectionImpl conn;
88
89
90 @GuardedBy("this")
91 private volatile boolean shutdown;
92
93
94
95
96
97
98 public BasicClientConnectionManager(final SchemeRegistry schreg) {
99 if (schreg == null) {
100 throw new IllegalArgumentException("Scheme registry may not be null");
101 }
102 this.schemeRegistry = schreg;
103 this.connOperator = createConnectionOperator(schreg);
104 }
105
106 public BasicClientConnectionManager() {
107 this(SchemeRegistryFactory.createDefault());
108 }
109
110 @Override
111 protected void finalize() throws Throwable {
112 try {
113 shutdown();
114 } finally {
115 super.finalize();
116 }
117 }
118
119 public SchemeRegistry getSchemeRegistry() {
120 return this.schemeRegistry;
121 }
122
123 protected ClientConnectionOperator createConnectionOperator(final SchemeRegistry schreg) {
124 return new DefaultClientConnectionOperator(schreg);
125 }
126
127 public final ClientConnectionRequest requestConnection(
128 final HttpRoute route,
129 final Object state) {
130
131 return new ClientConnectionRequest() {
132
133 public void abortRequest() {
134
135 }
136
137 public ManagedClientConnection getConnection(
138 long timeout, TimeUnit tunit) {
139 return BasicClientConnectionManager.this.getConnection(
140 route, state);
141 }
142
143 };
144 }
145
146 private void assertNotShutdown() {
147 if (this.shutdown) {
148 throw new IllegalStateException("Connection manager has been shut down");
149 }
150 }
151
152 ManagedClientConnection getConnection(final HttpRoute route, final Object state) {
153 if (route == null) {
154 throw new IllegalArgumentException("Route may not be null.");
155 }
156 synchronized (this) {
157 assertNotShutdown();
158 if (this.log.isDebugEnabled()) {
159 this.log.debug("Get connection for route " + route);
160 }
161 if (this.conn != null) {
162 throw new IllegalStateException(MISUSE_MESSAGE);
163 }
164 if (this.poolEntry != null && !this.poolEntry.getPlannedRoute().equals(route)) {
165 this.poolEntry.close();
166 this.poolEntry = null;
167 }
168 if (this.poolEntry == null) {
169 String id = Long.toString(COUNTER.getAndIncrement());
170 OperatedClientConnection conn = this.connOperator.createConnection();
171 this.poolEntry = new HttpPoolEntry(this.log, id, route, conn, 0, TimeUnit.MILLISECONDS);
172 }
173 long now = System.currentTimeMillis();
174 if (this.poolEntry.isExpired(now)) {
175 this.poolEntry.close();
176 this.poolEntry.getTracker().reset();
177 }
178 this.conn = new ManagedClientConnectionImpl(this, this.connOperator, this.poolEntry);
179 return this.conn;
180 }
181 }
182
183 private void shutdownConnection(final HttpClientConnection conn) {
184 try {
185 conn.shutdown();
186 } catch (IOException iox) {
187 if (this.log.isDebugEnabled()) {
188 this.log.debug("I/O exception shutting down connection", iox);
189 }
190 }
191 }
192
193 public void releaseConnection(final ManagedClientConnection conn, long keepalive, TimeUnit tunit) {
194 if (!(conn instanceof ManagedClientConnectionImpl)) {
195 throw new IllegalArgumentException("Connection class mismatch, " +
196 "connection not obtained from this manager");
197 }
198 ManagedClientConnectionImpl managedConn = (ManagedClientConnectionImpl) conn;
199 synchronized (managedConn) {
200 if (this.log.isDebugEnabled()) {
201 this.log.debug("Releasing connection " + conn);
202 }
203 if (managedConn.getPoolEntry() == null) {
204 return;
205 }
206 ClientConnectionManager manager = managedConn.getManager();
207 if (manager != null && manager != this) {
208 throw new IllegalStateException("Connection not obtained from this manager");
209 }
210 synchronized (this) {
211 if (this.shutdown) {
212 shutdownConnection(managedConn);
213 return;
214 }
215 try {
216 if (managedConn.isOpen() && !managedConn.isMarkedReusable()) {
217 shutdownConnection(managedConn);
218 }
219 if (managedConn.isMarkedReusable()) {
220 this.poolEntry.updateExpiry(keepalive, tunit != null ? tunit : TimeUnit.MILLISECONDS);
221 if (this.log.isDebugEnabled()) {
222 String s;
223 if (keepalive > 0) {
224 s = "for " + keepalive + " " + tunit;
225 } else {
226 s = "indefinitely";
227 }
228 this.log.debug("Connection can be kept alive " + s);
229 }
230 }
231 } finally {
232 managedConn.detach();
233 this.conn = null;
234 if (this.poolEntry.isClosed()) {
235 this.poolEntry = null;
236 }
237 }
238 }
239 }
240 }
241
242 public void closeExpiredConnections() {
243 synchronized (this) {
244 assertNotShutdown();
245 long now = System.currentTimeMillis();
246 if (this.poolEntry != null && this.poolEntry.isExpired(now)) {
247 this.poolEntry.close();
248 this.poolEntry.getTracker().reset();
249 }
250 }
251 }
252
253 public void closeIdleConnections(long idletime, TimeUnit tunit) {
254 if (tunit == null) {
255 throw new IllegalArgumentException("Time unit must not be null.");
256 }
257 synchronized (this) {
258 assertNotShutdown();
259 long time = tunit.toMillis(idletime);
260 if (time < 0) {
261 time = 0;
262 }
263 long deadline = System.currentTimeMillis() - time;
264 if (this.poolEntry != null && this.poolEntry.getUpdated() <= deadline) {
265 this.poolEntry.close();
266 this.poolEntry.getTracker().reset();
267 }
268 }
269 }
270
271 public void shutdown() {
272 synchronized (this) {
273 this.shutdown = true;
274 try {
275 if (this.poolEntry != null) {
276 this.poolEntry.close();
277 }
278 } finally {
279 this.poolEntry = null;
280 this.conn = null;
281 }
282 }
283 }
284
285 }