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