entity with connection release

git-svn-id: https://svn.apache.org/repos/asf/jakarta/httpcomponents/httpclient/trunk@508937 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Roland Weber 2007-02-18 18:36:21 +00:00
parent 6175558fb5
commit 7f2fad2321
7 changed files with 243 additions and 59 deletions

View File

@ -32,8 +32,9 @@
package org.apache.http.examples.client;
import org.apache.http.HttpHost;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpEntity;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
@ -47,7 +48,7 @@ import org.apache.http.protocol.RequestContent;
import org.apache.http.protocol.RequestExpectContinue;
import org.apache.http.protocol.RequestTargetHost;
import org.apache.http.protocol.RequestUserAgent;
//import org.apache.http.util.EntityUtils;
import org.apache.http.util.EntityUtils;
import org.apache.http.conn.Scheme;
import org.apache.http.conn.SchemeRegistry;
@ -94,7 +95,7 @@ public class ClientExecuteDirect {
public final static void main(String[] args)
throws Exception {
final HttpHost target = new HttpHost("jakarta.apache.org", 80, "http");
final HttpHost target = new HttpHost("issues.apache.org", 80, "http");
setup(); // some general setup
@ -103,8 +104,10 @@ public class ClientExecuteDirect {
HttpRequest req = createRequest(target);
System.out.println("executing request to " + target);
HttpEntity entity = null;
try {
HttpResponse rsp = client.execute(target, req);
entity = rsp.getEntity();
System.out.println("----------------------------------------");
System.out.println(rsp.getStatusLine());
@ -114,24 +117,18 @@ public class ClientExecuteDirect {
}
System.out.println("----------------------------------------");
if (rsp.getEntity() != null) {
// the entity is probably tied to the connection
rsp.getEntity().getContent().close();
// If you want to see the content, use the line below
// _instead_ of the close() call above. The utility class
// will read to the end of the stream and close it,
// thereby releasing the underlying connection.
//System.out.println(EntityUtils.toString(rsp.getEntity()));
//@@@ in case of an exception from EntityUtils.toString,
//@@@ there currently is no way of releasing the connection.
//@@@ The stream can be obtained from the entity only once.
if (entity != null) {
System.out.println(EntityUtils.toString(rsp.getEntity()));
}
// if there is no entity, the connection is already released
} finally {
//@@@ any kind of cleanup that should be performed?
//@@@ if content is read instead of being ignored, this is
//@@@ the place for connection release in case of an error
// If we could be sure that the stream of the entity has been
// closed, we wouldn't need this code to release the connection.
// However, EntityUtils.toString(...) can throw an exception.
// if there is no entity, the connection is already released
if (entity != null)
entity.consumeContent(); // release connection gracefully
}
} // main

View File

@ -32,8 +32,9 @@
package org.apache.http.examples.client;
import org.apache.http.HttpHost;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.HttpEntity;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
@ -47,7 +48,7 @@ import org.apache.http.protocol.RequestContent;
import org.apache.http.protocol.RequestExpectContinue;
import org.apache.http.protocol.RequestTargetHost;
import org.apache.http.protocol.RequestUserAgent;
//import org.apache.http.util.EntityUtils;
import org.apache.http.util.EntityUtils;
import org.apache.http.conn.Scheme;
import org.apache.http.conn.SchemeRegistry;
@ -115,8 +116,10 @@ public class ClientExecuteProxy {
final RoutedRequest roureq = new RoutedRequest.Impl(req, route);
System.out.println("executing request to " + target + " via " + proxy);
HttpEntity entity = null;
try {
HttpResponse rsp = client.execute(roureq, null);
entity = rsp.getEntity();
System.out.println("----------------------------------------");
System.out.println(rsp.getStatusLine());
@ -127,23 +130,17 @@ public class ClientExecuteProxy {
System.out.println("----------------------------------------");
if (rsp.getEntity() != null) {
// the entity is probably tied to the connection
rsp.getEntity().getContent().close();
// If you want to see the content, use the line below
// _instead_ of the close() call above. The utility class
// will read to the end of the stream and close it,
// thereby releasing the underlying connection.
//System.out.println(EntityUtils.toString(rsp.getEntity()));
//@@@ in case of an exception from EntityUtils.toString,
//@@@ there currently is no way of releasing the connection.
//@@@ The stream can be obtained from the entity only once.
System.out.println(EntityUtils.toString(rsp.getEntity()));
}
// if there is no entity, the connection is already released
} finally {
//@@@ any kind of cleanup that should be performed?
//@@@ if content is read instead of being ignored, this is
//@@@ the place for connection release in case of an error
// If we could be sure that the stream of the entity has been
// closed, we wouldn't need this code to release the connection.
// However, EntityUtils.toString(...) can throw an exception.
// if there is no entity, the connection is already released
if (entity != null)
entity.consumeContent(); // release connection gracefully
}
} // main

View File

@ -0,0 +1,205 @@
/*
* $HeadURL$
* $Revision $
* $Date$
*
* ====================================================================
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.http.conn;
import java.io.InputStream;
import java.io.IOException;
import org.apache.http.HttpEntity;
import org.apache.http.entity.HttpEntityWrapper;
/**
* An entity that releases a {@link ManagedClientConnection connection}.
*
* @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
*
*
* <!-- empty lines to avoid svn diff problems -->
* @version $Revision$
*
* @since 4.0
*/
public class BasicManagedEntity extends HttpEntityWrapper
implements HttpEntity, ConnectionReleaseTrigger, EofSensorWatcher {
/** The connection to release. */
protected ManagedClientConnection managedConn;
/** Whether to keep the connection alive. */
protected boolean attemptReuse;
/**
* Creates a new managed entity that can release a connection.
*
* @param entity the entity of which to wrap the content.
* Note that the argument entity can no longer be used
* afterwards, since the content will be taken by this
* managed entity.
* @param conn the connection to release
* @param reuse whether the connection should be re-used
*/
public BasicManagedEntity(HttpEntity entity,
ManagedClientConnection conn,
boolean reuse) {
super(entity);
if (conn == null)
throw new IllegalArgumentException
("Connection may not be null.");
managedConn = conn;
attemptReuse = reuse;
}
// non-javadoc, see interface HttpEntity
public boolean isRepeatable() {
return false;
}
// non-javadoc, see interface HttpEntity
public InputStream getContent() throws IOException {
return new EofSensorInputStream(wrappedEntity.getContent(), this);
}
// non-javadoc, see interface HttpEntity
public void consumeContent() throws IOException {
if (managedConn == null)
return;
try {
if (attemptReuse) {
// this will not trigger a callback from EofSensorInputStream
wrappedEntity.consumeContent();
managedConn.markReusable();
}
} finally {
releaseManagedConnection();
}
}
// non-javadoc, see interface ConnectionReleaseTrigger
public void releaseConnection()
throws IOException {
this.consumeContent();
}
// non-javadoc, see interface ConnectionReleaseTrigger
public void abortConnection()
throws IOException {
if (managedConn != null) {
try {
managedConn.abortConnection();
} finally {
managedConn = null;
}
}
}
// non-javadoc, see interface EofSensorWatcher
public boolean eofDetected(InputStream wrapped)
throws IOException {
try {
if (attemptReuse && (managedConn != null)) {
// there may be some cleanup required, such as
// reading trailers after the response body:
wrapped.close();
managedConn.markReusable();
}
} finally {
releaseManagedConnection();
}
return false;
}
// non-javadoc, see interface EofSensorWatcher
public boolean streamClosed(InputStream wrapped)
throws IOException {
try {
if (attemptReuse && (managedConn != null)) {
// this assumes that closing the stream will
// consume the remainder of the response body:
wrapped.close();
managedConn.markReusable();
}
} finally {
releaseManagedConnection();
}
return false;
}
// non-javadoc, see interface EofSensorWatcher
public boolean streamAbort(InputStream wrapped)
throws IOException {
if (managedConn != null) {
managedConn.abortConnection();
}
return false;
}
/**
* Releases the connection gracefully.
* The connection attribute will be nullified.
* Subsequent invocations are no-ops.
*
* @throws IOException in case of an IO problem.
* The connection attribute will be nullified anyway.
*/
protected void releaseManagedConnection()
throws IOException {
if (managedConn != null) {
try {
managedConn.releaseConnection();
} finally {
managedConn = null;
}
}
}
} // class BasicManagedEntity

View File

@ -86,6 +86,8 @@ public interface ClientConnectionManager {
/**
* Releases a connection for use by others.
* If the argument connection has been released before,
* the call will be ignored.
*
* @param conn the connection to release
*/

View File

@ -36,7 +36,9 @@ import java.io.IOException;
/**
* Interface for releasing a connection.
* This can be implemented by various "trigger" objects which are
* associated with a connection, for example a stream or an entity
* associated with a connection, for example
* a {@link EofSensorInputStream stream}
* or an entity
* or the {@link ManagedClientConnection connection} itself.
* <br/>
* The methods in this interface can safely be called multiple times.

View File

@ -47,9 +47,7 @@ import org.apache.http.conn.HttpRoute;
import org.apache.http.conn.RouteDirector;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.ManagedClientConnection;
import org.apache.http.conn.EofSensorInputStream;
import org.apache.http.conn.EofSensorWatcher;
import org.apache.http.conn.BasicEofSensorWatcher;
import org.apache.http.conn.BasicManagedEntity;
import org.apache.http.client.RoutedRequest;
import org.apache.http.client.ClientRequestDirector;
@ -413,24 +411,11 @@ public class DefaultClientRequestDirector
mcc.markReusable();
connManager.releaseConnection(mcc);
} else {
// install an auto-release entity
HttpEntity entity = response.getEntity();
if (!(entity instanceof BasicHttpEntity)) {
//@@@ fall back to an entity wrapper in this case?
managedConn = null;
connManager.releaseConnection(mcc);
throw new IllegalStateException
("Unsupported entity type: " +
entity.getClass().getName());
}
// install an auto-release handler
BasicHttpEntity bhe = (BasicHttpEntity) entity;
//@@@ evaluate connection re-use strategy
boolean reuse = false;
BasicEofSensorWatcher esw =
new BasicEofSensorWatcher(mcc, reuse);
EofSensorInputStream esis =
new EofSensorInputStream(bhe.getContent(), esw);
bhe.setContent(esis);
response.setEntity(new BasicManagedEntity(entity, mcc, reuse));
}
} else {
// we got here as the result of an exception
@ -438,16 +423,12 @@ public class DefaultClientRequestDirector
managedConn = null;
//@@@ is the connection in a re-usable state? consume response?
//@@@ for now, just shut it down
// Do not delegate this to the connection manager, can't
// tell whether it would shut down or close gracefully.
try {
if (mcc.isOpen())
mcc.shutdown();
mcc.abortConnection();
} catch (IOException ignore) {
// can't allow exception while handling exception
//@@@ log as debug or warning?
}
connManager.releaseConnection(mcc);
}
} // cleanupConnection

View File

@ -369,7 +369,7 @@ public class ThreadSafeClientConnManager
}
// In MTHCM, method releasePoolEntry below would call
// SimpleHttpConnectionManager.finishLastResponse(conn);
//@@@ consume response here or outside? (rolandw: outside)
// Consuming the response is handled outside in 4.0.
// make sure this connection will not be re-used
//@@@ Can we set some kind of flag before shutting down?