1 /*
2 * $Revision $
3 * ====================================================================
4 *
5 * Licensed to the Apache Software Foundation (ASF) under one or more
6 * contributor license agreements. See the NOTICE file distributed with
7 * this work for additional information regarding copyright ownership.
8 * The ASF licenses this file to You under the Apache License, Version 2.0
9 * (the "License"); you may not use this file except in compliance with
10 * the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 * ====================================================================
20 *
21 * This software consists of voluntary contributions made by many
22 * individuals on behalf of the Apache Software Foundation. For more
23 * information on the Apache Software Foundation, please see
24 * <http://www.apache.org/>.
25 *
26 */
27
28 package org.apache.http.conn;
29
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.net.SocketException;
34
35 import org.apache.http.annotation.NotThreadSafe;
36
37 import org.apache.http.HttpEntity;
38 import org.apache.http.entity.HttpEntityWrapper;
39 import org.apache.http.util.EntityUtils;
40
41 /**
42 * An entity that releases a {@link ManagedClientConnection connection}.
43 * A {@link ManagedClientConnection} will
44 * typically <i>not</i> return a managed entity, but you can replace
45 * the unmanaged entity in the response with a managed one.
46 *
47 * @since 4.0
48 */
49 @NotThreadSafe
50 public class BasicManagedEntity extends HttpEntityWrapper
51 implements ConnectionReleaseTrigger, EofSensorWatcher {
52
53 /** The connection to release. */
54 protected ManagedClientConnection managedConn;
55
56 /** Whether to keep the connection alive. */
57 protected final boolean attemptReuse;
58
59 /**
60 * Creates a new managed entity that can release a connection.
61 *
62 * @param entity the entity of which to wrap the content.
63 * Note that the argument entity can no longer be used
64 * afterwards, since the content will be taken by this
65 * managed entity.
66 * @param conn the connection to release
67 * @param reuse whether the connection should be re-used
68 */
69 public BasicManagedEntity(HttpEntity entity,
70 ManagedClientConnection conn,
71 boolean reuse) {
72 super(entity);
73
74 if (conn == null)
75 throw new IllegalArgumentException
76 ("Connection may not be null.");
77
78 this.managedConn = conn;
79 this.attemptReuse = reuse;
80 }
81
82 @Override
83 public boolean isRepeatable() {
84 return false;
85 }
86
87 @Override
88 public InputStream getContent() throws IOException {
89 return new EofSensorInputStream(wrappedEntity.getContent(), this);
90 }
91
92 private void ensureConsumed() throws IOException {
93 if (managedConn == null)
94 return;
95
96 try {
97 if (attemptReuse) {
98 // this will not trigger a callback from EofSensorInputStream
99 EntityUtils.consume(wrappedEntity);
100 managedConn.markReusable();
101 } else {
102 managedConn.unmarkReusable();
103 }
104 } finally {
105 releaseManagedConnection();
106 }
107 }
108
109 /**
110 * @deprecated (4.1) Use {@link EntityUtils#consume(HttpEntity)}
111 */
112 @Override
113 public void consumeContent() throws IOException {
114 ensureConsumed();
115 }
116
117 @Override
118 public void writeTo(final OutputStream outstream) throws IOException {
119 super.writeTo(outstream);
120 ensureConsumed();
121 }
122
123 public void releaseConnection() throws IOException {
124 ensureConsumed();
125 }
126
127 public void abortConnection() throws IOException {
128
129 if (managedConn != null) {
130 try {
131 managedConn.abortConnection();
132 } finally {
133 managedConn = null;
134 }
135 }
136 }
137
138 public boolean eofDetected(InputStream wrapped) throws IOException {
139 try {
140 if (managedConn != null) {
141 if (attemptReuse) {
142 // there may be some cleanup required, such as
143 // reading trailers after the response body:
144 wrapped.close();
145 managedConn.markReusable();
146 } else {
147 managedConn.unmarkReusable();
148 }
149 }
150 } finally {
151 releaseManagedConnection();
152 }
153 return false;
154 }
155
156 public boolean streamClosed(InputStream wrapped) throws IOException {
157 try {
158 if (managedConn != null) {
159 if (attemptReuse) {
160 boolean valid = managedConn.isOpen();
161 // this assumes that closing the stream will
162 // consume the remainder of the response body:
163 try {
164 wrapped.close();
165 managedConn.markReusable();
166 } catch (SocketException ex) {
167 if (valid) {
168 throw ex;
169 }
170 }
171 } else {
172 managedConn.unmarkReusable();
173 }
174 }
175 } finally {
176 releaseManagedConnection();
177 }
178 return false;
179 }
180
181 public boolean streamAbort(InputStream wrapped) throws IOException {
182 if (managedConn != null) {
183 managedConn.abortConnection();
184 }
185 return false;
186 }
187
188 /**
189 * Releases the connection gracefully.
190 * The connection attribute will be nullified.
191 * Subsequent invocations are no-ops.
192 *
193 * @throws IOException in case of an IO problem.
194 * The connection attribute will be nullified anyway.
195 */
196 protected void releaseManagedConnection()
197 throws IOException {
198
199 if (managedConn != null) {
200 try {
201 managedConn.releaseConnection();
202 } finally {
203 managedConn = null;
204 }
205 }
206 }
207
208 }