michael@0: /* michael@0: * ==================================================================== michael@0: * michael@0: * Licensed to the Apache Software Foundation (ASF) under one or more michael@0: * contributor license agreements. See the NOTICE file distributed with michael@0: * this work for additional information regarding copyright ownership. michael@0: * The ASF licenses this file to You under the Apache License, Version 2.0 michael@0: * (the "License"); you may not use this file except in compliance with michael@0: * the License. You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: * ==================================================================== michael@0: * michael@0: * This software consists of voluntary contributions made by many michael@0: * individuals on behalf of the Apache Software Foundation. For more michael@0: * information on the Apache Software Foundation, please see michael@0: * . michael@0: * michael@0: */ michael@0: michael@0: package ch.boye.httpclientandroidlib.impl.conn; michael@0: michael@0: import java.io.IOException; michael@0: import java.io.InterruptedIOException; michael@0: import java.net.InetAddress; michael@0: import java.net.Socket; michael@0: import java.util.concurrent.TimeUnit; michael@0: michael@0: import javax.net.ssl.SSLSocket; michael@0: import javax.net.ssl.SSLSession; michael@0: michael@0: import ch.boye.httpclientandroidlib.HttpException; michael@0: import ch.boye.httpclientandroidlib.HttpRequest; michael@0: import ch.boye.httpclientandroidlib.HttpEntityEnclosingRequest; michael@0: import ch.boye.httpclientandroidlib.HttpResponse; michael@0: import ch.boye.httpclientandroidlib.HttpConnectionMetrics; michael@0: import ch.boye.httpclientandroidlib.conn.OperatedClientConnection; michael@0: import ch.boye.httpclientandroidlib.conn.ManagedClientConnection; michael@0: import ch.boye.httpclientandroidlib.conn.ClientConnectionManager; michael@0: import ch.boye.httpclientandroidlib.protocol.HttpContext; michael@0: michael@0: /** michael@0: * Abstract adapter from {@link OperatedClientConnection operated} to michael@0: * {@link ManagedClientConnection managed} client connections. michael@0: * Read and write methods are delegated to the wrapped connection. michael@0: * Operations affecting the connection state have to be implemented michael@0: * by derived classes. Operations for querying the connection state michael@0: * are delegated to the wrapped connection if there is one, or michael@0: * return a default value if there is none. michael@0: *

michael@0: * This adapter tracks the checkpoints for reusable communication states, michael@0: * as indicated by {@link #markReusable markReusable} and queried by michael@0: * {@link #isMarkedReusable isMarkedReusable}. michael@0: * All send and receive operations will automatically clear the mark. michael@0: *

michael@0: * Connection release calls are delegated to the connection manager, michael@0: * if there is one. {@link #abortConnection abortConnection} will michael@0: * clear the reusability mark first. The connection manager is michael@0: * expected to tolerate multiple calls to the release method. michael@0: * michael@0: * @since 4.0 michael@0: */ michael@0: public abstract class AbstractClientConnAdapter michael@0: implements ManagedClientConnection, HttpContext { michael@0: michael@0: /** michael@0: * The connection manager, if any. michael@0: * This attribute MUST NOT be final, so the adapter can be detached michael@0: * from the connection manager without keeping a hard reference there. michael@0: */ michael@0: private volatile ClientConnectionManager connManager; michael@0: michael@0: /** The wrapped connection. */ michael@0: private volatile OperatedClientConnection wrappedConnection; michael@0: michael@0: /** The reusability marker. */ michael@0: private volatile boolean markedReusable; michael@0: michael@0: /** True if the connection has been shut down or released. */ michael@0: private volatile boolean released; michael@0: michael@0: /** The duration this is valid for while idle (in ms). */ michael@0: private volatile long duration; michael@0: michael@0: /** michael@0: * Creates a new connection adapter. michael@0: * The adapter is initially not michael@0: * {@link #isMarkedReusable marked} as reusable. michael@0: * michael@0: * @param mgr the connection manager, or null michael@0: * @param conn the connection to wrap, or null michael@0: */ michael@0: protected AbstractClientConnAdapter(ClientConnectionManager mgr, michael@0: OperatedClientConnection conn) { michael@0: super(); michael@0: connManager = mgr; michael@0: wrappedConnection = conn; michael@0: markedReusable = false; michael@0: released = false; michael@0: duration = Long.MAX_VALUE; michael@0: } michael@0: michael@0: /** michael@0: * Detaches this adapter from the wrapped connection. michael@0: * This adapter becomes useless. michael@0: */ michael@0: protected synchronized void detach() { michael@0: wrappedConnection = null; michael@0: connManager = null; // base class attribute michael@0: duration = Long.MAX_VALUE; michael@0: } michael@0: michael@0: protected OperatedClientConnection getWrappedConnection() { michael@0: return wrappedConnection; michael@0: } michael@0: michael@0: protected ClientConnectionManager getManager() { michael@0: return connManager; michael@0: } michael@0: michael@0: /** michael@0: * @deprecated use {@link #assertValid(OperatedClientConnection)} michael@0: */ michael@0: @Deprecated michael@0: protected final void assertNotAborted() throws InterruptedIOException { michael@0: if (isReleased()) { michael@0: throw new InterruptedIOException("Connection has been shut down"); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * @since 4.1 michael@0: * @return value of released flag michael@0: */ michael@0: protected boolean isReleased() { michael@0: return released; michael@0: } michael@0: michael@0: /** michael@0: * Asserts that there is a valid wrapped connection to delegate to. michael@0: * michael@0: * @throws ConnectionShutdownException if there is no wrapped connection michael@0: * or connection has been aborted michael@0: */ michael@0: protected final void assertValid( michael@0: final OperatedClientConnection wrappedConn) throws ConnectionShutdownException { michael@0: if (isReleased() || wrappedConn == null) { michael@0: throw new ConnectionShutdownException(); michael@0: } michael@0: } michael@0: michael@0: public boolean isOpen() { michael@0: OperatedClientConnection conn = getWrappedConnection(); michael@0: if (conn == null) michael@0: return false; michael@0: michael@0: return conn.isOpen(); michael@0: } michael@0: michael@0: public boolean isStale() { michael@0: if (isReleased()) michael@0: return true; michael@0: OperatedClientConnection conn = getWrappedConnection(); michael@0: if (conn == null) michael@0: return true; michael@0: michael@0: return conn.isStale(); michael@0: } michael@0: michael@0: public void setSocketTimeout(int timeout) { michael@0: OperatedClientConnection conn = getWrappedConnection(); michael@0: assertValid(conn); michael@0: conn.setSocketTimeout(timeout); michael@0: } michael@0: michael@0: public int getSocketTimeout() { michael@0: OperatedClientConnection conn = getWrappedConnection(); michael@0: assertValid(conn); michael@0: return conn.getSocketTimeout(); michael@0: } michael@0: michael@0: public HttpConnectionMetrics getMetrics() { michael@0: OperatedClientConnection conn = getWrappedConnection(); michael@0: assertValid(conn); michael@0: return conn.getMetrics(); michael@0: } michael@0: michael@0: public void flush() throws IOException { michael@0: OperatedClientConnection conn = getWrappedConnection(); michael@0: assertValid(conn); michael@0: conn.flush(); michael@0: } michael@0: michael@0: public boolean isResponseAvailable(int timeout) throws IOException { michael@0: OperatedClientConnection conn = getWrappedConnection(); michael@0: assertValid(conn); michael@0: return conn.isResponseAvailable(timeout); michael@0: } michael@0: michael@0: public void receiveResponseEntity(HttpResponse response) michael@0: throws HttpException, IOException { michael@0: OperatedClientConnection conn = getWrappedConnection(); michael@0: assertValid(conn); michael@0: unmarkReusable(); michael@0: conn.receiveResponseEntity(response); michael@0: } michael@0: michael@0: public HttpResponse receiveResponseHeader() michael@0: throws HttpException, IOException { michael@0: OperatedClientConnection conn = getWrappedConnection(); michael@0: assertValid(conn); michael@0: unmarkReusable(); michael@0: return conn.receiveResponseHeader(); michael@0: } michael@0: michael@0: public void sendRequestEntity(HttpEntityEnclosingRequest request) michael@0: throws HttpException, IOException { michael@0: OperatedClientConnection conn = getWrappedConnection(); michael@0: assertValid(conn); michael@0: unmarkReusable(); michael@0: conn.sendRequestEntity(request); michael@0: } michael@0: michael@0: public void sendRequestHeader(HttpRequest request) michael@0: throws HttpException, IOException { michael@0: OperatedClientConnection conn = getWrappedConnection(); michael@0: assertValid(conn); michael@0: unmarkReusable(); michael@0: conn.sendRequestHeader(request); michael@0: } michael@0: michael@0: public InetAddress getLocalAddress() { michael@0: OperatedClientConnection conn = getWrappedConnection(); michael@0: assertValid(conn); michael@0: return conn.getLocalAddress(); michael@0: } michael@0: michael@0: public int getLocalPort() { michael@0: OperatedClientConnection conn = getWrappedConnection(); michael@0: assertValid(conn); michael@0: return conn.getLocalPort(); michael@0: } michael@0: michael@0: public InetAddress getRemoteAddress() { michael@0: OperatedClientConnection conn = getWrappedConnection(); michael@0: assertValid(conn); michael@0: return conn.getRemoteAddress(); michael@0: } michael@0: michael@0: public int getRemotePort() { michael@0: OperatedClientConnection conn = getWrappedConnection(); michael@0: assertValid(conn); michael@0: return conn.getRemotePort(); michael@0: } michael@0: michael@0: public boolean isSecure() { michael@0: OperatedClientConnection conn = getWrappedConnection(); michael@0: assertValid(conn); michael@0: return conn.isSecure(); michael@0: } michael@0: michael@0: public SSLSession getSSLSession() { michael@0: OperatedClientConnection conn = getWrappedConnection(); michael@0: assertValid(conn); michael@0: if (!isOpen()) michael@0: return null; michael@0: michael@0: SSLSession result = null; michael@0: Socket sock = conn.getSocket(); michael@0: if (sock instanceof SSLSocket) { michael@0: result = ((SSLSocket)sock).getSession(); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: public void markReusable() { michael@0: markedReusable = true; michael@0: } michael@0: michael@0: public void unmarkReusable() { michael@0: markedReusable = false; michael@0: } michael@0: michael@0: public boolean isMarkedReusable() { michael@0: return markedReusable; michael@0: } michael@0: michael@0: public void setIdleDuration(long duration, TimeUnit unit) { michael@0: if(duration > 0) { michael@0: this.duration = unit.toMillis(duration); michael@0: } else { michael@0: this.duration = -1; michael@0: } michael@0: } michael@0: michael@0: public synchronized void releaseConnection() { michael@0: if (released) { michael@0: return; michael@0: } michael@0: released = true; michael@0: if (connManager != null) { michael@0: connManager.releaseConnection(this, duration, TimeUnit.MILLISECONDS); michael@0: } michael@0: } michael@0: michael@0: public synchronized void abortConnection() { michael@0: if (released) { michael@0: return; michael@0: } michael@0: released = true; michael@0: unmarkReusable(); michael@0: try { michael@0: shutdown(); michael@0: } catch (IOException ignore) { michael@0: } michael@0: if (connManager != null) { michael@0: connManager.releaseConnection(this, duration, TimeUnit.MILLISECONDS); michael@0: } michael@0: } michael@0: michael@0: public synchronized Object getAttribute(final String id) { michael@0: OperatedClientConnection conn = getWrappedConnection(); michael@0: assertValid(conn); michael@0: if (conn instanceof HttpContext) { michael@0: return ((HttpContext) conn).getAttribute(id); michael@0: } else { michael@0: return null; michael@0: } michael@0: } michael@0: michael@0: public synchronized Object removeAttribute(final String id) { michael@0: OperatedClientConnection conn = getWrappedConnection(); michael@0: assertValid(conn); michael@0: if (conn instanceof HttpContext) { michael@0: return ((HttpContext) conn).removeAttribute(id); michael@0: } else { michael@0: return null; michael@0: } michael@0: } michael@0: michael@0: public synchronized void setAttribute(final String id, final Object obj) { michael@0: OperatedClientConnection conn = getWrappedConnection(); michael@0: assertValid(conn); michael@0: if (conn instanceof HttpContext) { michael@0: ((HttpContext) conn).setAttribute(id, obj); michael@0: } michael@0: } michael@0: michael@0: }