|
1 /* |
|
2 * ==================================================================== |
|
3 * |
|
4 * Licensed to the Apache Software Foundation (ASF) under one or more |
|
5 * contributor license agreements. See the NOTICE file distributed with |
|
6 * this work for additional information regarding copyright ownership. |
|
7 * The ASF licenses this file to You under the Apache License, Version 2.0 |
|
8 * (the "License"); you may not use this file except in compliance with |
|
9 * the License. You may obtain a copy of the License at |
|
10 * |
|
11 * http://www.apache.org/licenses/LICENSE-2.0 |
|
12 * |
|
13 * Unless required by applicable law or agreed to in writing, software |
|
14 * distributed under the License is distributed on an "AS IS" BASIS, |
|
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
16 * See the License for the specific language governing permissions and |
|
17 * limitations under the License. |
|
18 * ==================================================================== |
|
19 * |
|
20 * This software consists of voluntary contributions made by many |
|
21 * individuals on behalf of the Apache Software Foundation. For more |
|
22 * information on the Apache Software Foundation, please see |
|
23 * <http://www.apache.org/>. |
|
24 * |
|
25 */ |
|
26 |
|
27 package ch.boye.httpclientandroidlib.impl.conn; |
|
28 |
|
29 import java.io.IOException; |
|
30 import java.io.InterruptedIOException; |
|
31 import java.net.InetAddress; |
|
32 import java.net.Socket; |
|
33 import java.util.concurrent.TimeUnit; |
|
34 |
|
35 import javax.net.ssl.SSLSocket; |
|
36 import javax.net.ssl.SSLSession; |
|
37 |
|
38 import ch.boye.httpclientandroidlib.HttpException; |
|
39 import ch.boye.httpclientandroidlib.HttpRequest; |
|
40 import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; |
|
41 import ch.boye.httpclientandroidlib.HttpResponse; |
|
42 import ch.boye.httpclientandroidlib.HttpConnectionMetrics; |
|
43 import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; |
|
44 import ch.boye.httpclientandroidlib.conn.ManagedClientConnection; |
|
45 import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; |
|
46 import ch.boye.httpclientandroidlib.protocol.HttpContext; |
|
47 |
|
48 /** |
|
49 * Abstract adapter from {@link OperatedClientConnection operated} to |
|
50 * {@link ManagedClientConnection managed} client connections. |
|
51 * Read and write methods are delegated to the wrapped connection. |
|
52 * Operations affecting the connection state have to be implemented |
|
53 * by derived classes. Operations for querying the connection state |
|
54 * are delegated to the wrapped connection if there is one, or |
|
55 * return a default value if there is none. |
|
56 * <p> |
|
57 * This adapter tracks the checkpoints for reusable communication states, |
|
58 * as indicated by {@link #markReusable markReusable} and queried by |
|
59 * {@link #isMarkedReusable isMarkedReusable}. |
|
60 * All send and receive operations will automatically clear the mark. |
|
61 * <p> |
|
62 * Connection release calls are delegated to the connection manager, |
|
63 * if there is one. {@link #abortConnection abortConnection} will |
|
64 * clear the reusability mark first. The connection manager is |
|
65 * expected to tolerate multiple calls to the release method. |
|
66 * |
|
67 * @since 4.0 |
|
68 */ |
|
69 public abstract class AbstractClientConnAdapter |
|
70 implements ManagedClientConnection, HttpContext { |
|
71 |
|
72 /** |
|
73 * The connection manager, if any. |
|
74 * This attribute MUST NOT be final, so the adapter can be detached |
|
75 * from the connection manager without keeping a hard reference there. |
|
76 */ |
|
77 private volatile ClientConnectionManager connManager; |
|
78 |
|
79 /** The wrapped connection. */ |
|
80 private volatile OperatedClientConnection wrappedConnection; |
|
81 |
|
82 /** The reusability marker. */ |
|
83 private volatile boolean markedReusable; |
|
84 |
|
85 /** True if the connection has been shut down or released. */ |
|
86 private volatile boolean released; |
|
87 |
|
88 /** The duration this is valid for while idle (in ms). */ |
|
89 private volatile long duration; |
|
90 |
|
91 /** |
|
92 * Creates a new connection adapter. |
|
93 * The adapter is initially <i>not</i> |
|
94 * {@link #isMarkedReusable marked} as reusable. |
|
95 * |
|
96 * @param mgr the connection manager, or <code>null</code> |
|
97 * @param conn the connection to wrap, or <code>null</code> |
|
98 */ |
|
99 protected AbstractClientConnAdapter(ClientConnectionManager mgr, |
|
100 OperatedClientConnection conn) { |
|
101 super(); |
|
102 connManager = mgr; |
|
103 wrappedConnection = conn; |
|
104 markedReusable = false; |
|
105 released = false; |
|
106 duration = Long.MAX_VALUE; |
|
107 } |
|
108 |
|
109 /** |
|
110 * Detaches this adapter from the wrapped connection. |
|
111 * This adapter becomes useless. |
|
112 */ |
|
113 protected synchronized void detach() { |
|
114 wrappedConnection = null; |
|
115 connManager = null; // base class attribute |
|
116 duration = Long.MAX_VALUE; |
|
117 } |
|
118 |
|
119 protected OperatedClientConnection getWrappedConnection() { |
|
120 return wrappedConnection; |
|
121 } |
|
122 |
|
123 protected ClientConnectionManager getManager() { |
|
124 return connManager; |
|
125 } |
|
126 |
|
127 /** |
|
128 * @deprecated use {@link #assertValid(OperatedClientConnection)} |
|
129 */ |
|
130 @Deprecated |
|
131 protected final void assertNotAborted() throws InterruptedIOException { |
|
132 if (isReleased()) { |
|
133 throw new InterruptedIOException("Connection has been shut down"); |
|
134 } |
|
135 } |
|
136 |
|
137 /** |
|
138 * @since 4.1 |
|
139 * @return value of released flag |
|
140 */ |
|
141 protected boolean isReleased() { |
|
142 return released; |
|
143 } |
|
144 |
|
145 /** |
|
146 * Asserts that there is a valid wrapped connection to delegate to. |
|
147 * |
|
148 * @throws ConnectionShutdownException if there is no wrapped connection |
|
149 * or connection has been aborted |
|
150 */ |
|
151 protected final void assertValid( |
|
152 final OperatedClientConnection wrappedConn) throws ConnectionShutdownException { |
|
153 if (isReleased() || wrappedConn == null) { |
|
154 throw new ConnectionShutdownException(); |
|
155 } |
|
156 } |
|
157 |
|
158 public boolean isOpen() { |
|
159 OperatedClientConnection conn = getWrappedConnection(); |
|
160 if (conn == null) |
|
161 return false; |
|
162 |
|
163 return conn.isOpen(); |
|
164 } |
|
165 |
|
166 public boolean isStale() { |
|
167 if (isReleased()) |
|
168 return true; |
|
169 OperatedClientConnection conn = getWrappedConnection(); |
|
170 if (conn == null) |
|
171 return true; |
|
172 |
|
173 return conn.isStale(); |
|
174 } |
|
175 |
|
176 public void setSocketTimeout(int timeout) { |
|
177 OperatedClientConnection conn = getWrappedConnection(); |
|
178 assertValid(conn); |
|
179 conn.setSocketTimeout(timeout); |
|
180 } |
|
181 |
|
182 public int getSocketTimeout() { |
|
183 OperatedClientConnection conn = getWrappedConnection(); |
|
184 assertValid(conn); |
|
185 return conn.getSocketTimeout(); |
|
186 } |
|
187 |
|
188 public HttpConnectionMetrics getMetrics() { |
|
189 OperatedClientConnection conn = getWrappedConnection(); |
|
190 assertValid(conn); |
|
191 return conn.getMetrics(); |
|
192 } |
|
193 |
|
194 public void flush() throws IOException { |
|
195 OperatedClientConnection conn = getWrappedConnection(); |
|
196 assertValid(conn); |
|
197 conn.flush(); |
|
198 } |
|
199 |
|
200 public boolean isResponseAvailable(int timeout) throws IOException { |
|
201 OperatedClientConnection conn = getWrappedConnection(); |
|
202 assertValid(conn); |
|
203 return conn.isResponseAvailable(timeout); |
|
204 } |
|
205 |
|
206 public void receiveResponseEntity(HttpResponse response) |
|
207 throws HttpException, IOException { |
|
208 OperatedClientConnection conn = getWrappedConnection(); |
|
209 assertValid(conn); |
|
210 unmarkReusable(); |
|
211 conn.receiveResponseEntity(response); |
|
212 } |
|
213 |
|
214 public HttpResponse receiveResponseHeader() |
|
215 throws HttpException, IOException { |
|
216 OperatedClientConnection conn = getWrappedConnection(); |
|
217 assertValid(conn); |
|
218 unmarkReusable(); |
|
219 return conn.receiveResponseHeader(); |
|
220 } |
|
221 |
|
222 public void sendRequestEntity(HttpEntityEnclosingRequest request) |
|
223 throws HttpException, IOException { |
|
224 OperatedClientConnection conn = getWrappedConnection(); |
|
225 assertValid(conn); |
|
226 unmarkReusable(); |
|
227 conn.sendRequestEntity(request); |
|
228 } |
|
229 |
|
230 public void sendRequestHeader(HttpRequest request) |
|
231 throws HttpException, IOException { |
|
232 OperatedClientConnection conn = getWrappedConnection(); |
|
233 assertValid(conn); |
|
234 unmarkReusable(); |
|
235 conn.sendRequestHeader(request); |
|
236 } |
|
237 |
|
238 public InetAddress getLocalAddress() { |
|
239 OperatedClientConnection conn = getWrappedConnection(); |
|
240 assertValid(conn); |
|
241 return conn.getLocalAddress(); |
|
242 } |
|
243 |
|
244 public int getLocalPort() { |
|
245 OperatedClientConnection conn = getWrappedConnection(); |
|
246 assertValid(conn); |
|
247 return conn.getLocalPort(); |
|
248 } |
|
249 |
|
250 public InetAddress getRemoteAddress() { |
|
251 OperatedClientConnection conn = getWrappedConnection(); |
|
252 assertValid(conn); |
|
253 return conn.getRemoteAddress(); |
|
254 } |
|
255 |
|
256 public int getRemotePort() { |
|
257 OperatedClientConnection conn = getWrappedConnection(); |
|
258 assertValid(conn); |
|
259 return conn.getRemotePort(); |
|
260 } |
|
261 |
|
262 public boolean isSecure() { |
|
263 OperatedClientConnection conn = getWrappedConnection(); |
|
264 assertValid(conn); |
|
265 return conn.isSecure(); |
|
266 } |
|
267 |
|
268 public SSLSession getSSLSession() { |
|
269 OperatedClientConnection conn = getWrappedConnection(); |
|
270 assertValid(conn); |
|
271 if (!isOpen()) |
|
272 return null; |
|
273 |
|
274 SSLSession result = null; |
|
275 Socket sock = conn.getSocket(); |
|
276 if (sock instanceof SSLSocket) { |
|
277 result = ((SSLSocket)sock).getSession(); |
|
278 } |
|
279 return result; |
|
280 } |
|
281 |
|
282 public void markReusable() { |
|
283 markedReusable = true; |
|
284 } |
|
285 |
|
286 public void unmarkReusable() { |
|
287 markedReusable = false; |
|
288 } |
|
289 |
|
290 public boolean isMarkedReusable() { |
|
291 return markedReusable; |
|
292 } |
|
293 |
|
294 public void setIdleDuration(long duration, TimeUnit unit) { |
|
295 if(duration > 0) { |
|
296 this.duration = unit.toMillis(duration); |
|
297 } else { |
|
298 this.duration = -1; |
|
299 } |
|
300 } |
|
301 |
|
302 public synchronized void releaseConnection() { |
|
303 if (released) { |
|
304 return; |
|
305 } |
|
306 released = true; |
|
307 if (connManager != null) { |
|
308 connManager.releaseConnection(this, duration, TimeUnit.MILLISECONDS); |
|
309 } |
|
310 } |
|
311 |
|
312 public synchronized void abortConnection() { |
|
313 if (released) { |
|
314 return; |
|
315 } |
|
316 released = true; |
|
317 unmarkReusable(); |
|
318 try { |
|
319 shutdown(); |
|
320 } catch (IOException ignore) { |
|
321 } |
|
322 if (connManager != null) { |
|
323 connManager.releaseConnection(this, duration, TimeUnit.MILLISECONDS); |
|
324 } |
|
325 } |
|
326 |
|
327 public synchronized Object getAttribute(final String id) { |
|
328 OperatedClientConnection conn = getWrappedConnection(); |
|
329 assertValid(conn); |
|
330 if (conn instanceof HttpContext) { |
|
331 return ((HttpContext) conn).getAttribute(id); |
|
332 } else { |
|
333 return null; |
|
334 } |
|
335 } |
|
336 |
|
337 public synchronized Object removeAttribute(final String id) { |
|
338 OperatedClientConnection conn = getWrappedConnection(); |
|
339 assertValid(conn); |
|
340 if (conn instanceof HttpContext) { |
|
341 return ((HttpContext) conn).removeAttribute(id); |
|
342 } else { |
|
343 return null; |
|
344 } |
|
345 } |
|
346 |
|
347 public synchronized void setAttribute(final String id, final Object obj) { |
|
348 OperatedClientConnection conn = getWrappedConnection(); |
|
349 assertValid(conn); |
|
350 if (conn instanceof HttpContext) { |
|
351 ((HttpContext) conn).setAttribute(id, obj); |
|
352 } |
|
353 } |
|
354 |
|
355 } |