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