michael@0: /*
michael@0: * $Revision $
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.conn;
michael@0:
michael@0: import java.io.IOException;
michael@0: import java.io.InputStream;
michael@0: import java.io.OutputStream;
michael@0:
michael@0: import ch.boye.httpclientandroidlib.annotation.NotThreadSafe;
michael@0:
michael@0: import ch.boye.httpclientandroidlib.HttpEntity;
michael@0: import ch.boye.httpclientandroidlib.entity.HttpEntityWrapper;
michael@0: import ch.boye.httpclientandroidlib.util.EntityUtils;
michael@0:
michael@0: /**
michael@0: * An entity that releases a {@link ManagedClientConnection connection}.
michael@0: * A {@link ManagedClientConnection} will
michael@0: * typically not return a managed entity, but you can replace
michael@0: * the unmanaged entity in the response with a managed one.
michael@0: *
michael@0: * @since 4.0
michael@0: */
michael@0: @NotThreadSafe
michael@0: public class BasicManagedEntity extends HttpEntityWrapper
michael@0: implements ConnectionReleaseTrigger, EofSensorWatcher {
michael@0:
michael@0: /** The connection to release. */
michael@0: protected ManagedClientConnection managedConn;
michael@0:
michael@0: /** Whether to keep the connection alive. */
michael@0: protected final boolean attemptReuse;
michael@0:
michael@0: /**
michael@0: * Creates a new managed entity that can release a connection.
michael@0: *
michael@0: * @param entity the entity of which to wrap the content.
michael@0: * Note that the argument entity can no longer be used
michael@0: * afterwards, since the content will be taken by this
michael@0: * managed entity.
michael@0: * @param conn the connection to release
michael@0: * @param reuse whether the connection should be re-used
michael@0: */
michael@0: public BasicManagedEntity(HttpEntity entity,
michael@0: ManagedClientConnection conn,
michael@0: boolean reuse) {
michael@0: super(entity);
michael@0:
michael@0: if (conn == null)
michael@0: throw new IllegalArgumentException
michael@0: ("Connection may not be null.");
michael@0:
michael@0: this.managedConn = conn;
michael@0: this.attemptReuse = reuse;
michael@0: }
michael@0:
michael@0: @Override
michael@0: public boolean isRepeatable() {
michael@0: return false;
michael@0: }
michael@0:
michael@0: @Override
michael@0: public InputStream getContent() throws IOException {
michael@0: return new EofSensorInputStream(wrappedEntity.getContent(), this);
michael@0: }
michael@0:
michael@0: private void ensureConsumed() throws IOException {
michael@0: if (managedConn == null)
michael@0: return;
michael@0:
michael@0: try {
michael@0: if (attemptReuse) {
michael@0: // this will not trigger a callback from EofSensorInputStream
michael@0: EntityUtils.consume(wrappedEntity);
michael@0: managedConn.markReusable();
michael@0: }
michael@0: } finally {
michael@0: releaseManagedConnection();
michael@0: }
michael@0: }
michael@0:
michael@0: @Deprecated
michael@0: @Override
michael@0: public void consumeContent() throws IOException {
michael@0: ensureConsumed();
michael@0: }
michael@0:
michael@0: @Override
michael@0: public void writeTo(final OutputStream outstream) throws IOException {
michael@0: super.writeTo(outstream);
michael@0: ensureConsumed();
michael@0: }
michael@0:
michael@0: public void releaseConnection() throws IOException {
michael@0: ensureConsumed();
michael@0: }
michael@0:
michael@0: public void abortConnection() throws IOException {
michael@0:
michael@0: if (managedConn != null) {
michael@0: try {
michael@0: managedConn.abortConnection();
michael@0: } finally {
michael@0: managedConn = null;
michael@0: }
michael@0: }
michael@0: }
michael@0:
michael@0: public boolean eofDetected(InputStream wrapped) throws IOException {
michael@0: try {
michael@0: if (attemptReuse && (managedConn != null)) {
michael@0: // there may be some cleanup required, such as
michael@0: // reading trailers after the response body:
michael@0: wrapped.close();
michael@0: managedConn.markReusable();
michael@0: }
michael@0: } finally {
michael@0: releaseManagedConnection();
michael@0: }
michael@0: return false;
michael@0: }
michael@0:
michael@0: public boolean streamClosed(InputStream wrapped) throws IOException {
michael@0: try {
michael@0: if (attemptReuse && (managedConn != null)) {
michael@0: // this assumes that closing the stream will
michael@0: // consume the remainder of the response body:
michael@0: wrapped.close();
michael@0: managedConn.markReusable();
michael@0: }
michael@0: } finally {
michael@0: releaseManagedConnection();
michael@0: }
michael@0: return false;
michael@0: }
michael@0:
michael@0: public boolean streamAbort(InputStream wrapped) throws IOException {
michael@0: if (managedConn != null) {
michael@0: managedConn.abortConnection();
michael@0: }
michael@0: return false;
michael@0: }
michael@0:
michael@0: /**
michael@0: * Releases the connection gracefully.
michael@0: * The connection attribute will be nullified.
michael@0: * Subsequent invocations are no-ops.
michael@0: *
michael@0: * @throws IOException in case of an IO problem.
michael@0: * The connection attribute will be nullified anyway.
michael@0: */
michael@0: protected void releaseManagedConnection()
michael@0: throws IOException {
michael@0:
michael@0: if (managedConn != null) {
michael@0: try {
michael@0: managedConn.releaseConnection();
michael@0: } finally {
michael@0: managedConn = null;
michael@0: }
michael@0: }
michael@0: }
michael@0:
michael@0: }