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: * 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: }