• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * $HeadURL: http://svn.apache.org/repos/asf/httpcomponents/httpclient/trunk/module-client/src/main/java/org/apache/http/conn/EofSensorInputStream.java $
3  * $Revision: 672367 $
4  * $Date: 2008-06-27 12:49:20 -0700 (Fri, 27 Jun 2008) $
5  *
6  * ====================================================================
7  *
8  *  Licensed to the Apache Software Foundation (ASF) under one or more
9  *  contributor license agreements.  See the NOTICE file distributed with
10  *  this work for additional information regarding copyright ownership.
11  *  The ASF licenses this file to You under the Apache License, Version 2.0
12  *  (the "License"); you may not use this file except in compliance with
13  *  the License.  You may obtain a copy of the License at
14  *
15  *      http://www.apache.org/licenses/LICENSE-2.0
16  *
17  *  Unless required by applicable law or agreed to in writing, software
18  *  distributed under the License is distributed on an "AS IS" BASIS,
19  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  *  See the License for the specific language governing permissions and
21  *  limitations under the License.
22  * ====================================================================
23  *
24  * This software consists of voluntary contributions made by many
25  * individuals on behalf of the Apache Software Foundation.  For more
26  * information on the Apache Software Foundation, please see
27  * <http://www.apache.org/>.
28  *
29  */
30 
31 package org.apache.http.conn;
32 
33 import java.io.InputStream;
34 import java.io.IOException;
35 
36 
37 /**
38  * A stream wrapper that triggers actions on {@link #close close()} and EOF.
39  * Primarily used to auto-release an underlying
40  * {@link ManagedClientConnection connection}
41  * when the response body is consumed or no longer needed.
42  *
43  * <p>
44  * This class is based on <code>AutoCloseInputStream</code> in HttpClient 3.1,
45  * but has notable differences. It does not allow mark/reset, distinguishes
46  * different kinds of event, and does not always close the underlying stream
47  * on EOF. That decision is left to the {@link EofSensorWatcher watcher}.
48  * </p>
49  *
50  * @see EofSensorWatcher EofSensorWatcher
51  *
52  * @author <a href="mailto:rolandw at apache.org">Roland Weber</a>
53  * @author Ortwin Glueck
54  * @author Eric Johnson
55  * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
56  *
57  *
58  * <!-- empty lines to avoid svn diff problems -->
59  * @version $Revision: 672367 $
60  *
61  * @since 4.0
62  */
63 // don't use FilterInputStream as the base class, we'd have to
64 // override markSupported(), mark(), and reset() to disable them
65 public class EofSensorInputStream extends InputStream
66     implements ConnectionReleaseTrigger {
67 
68     /**
69      * The wrapped input stream, while accessible.
70      * The value changes to <code>null</code> when the wrapped stream
71      * becomes inaccessible.
72      */
73     protected InputStream wrappedStream;
74 
75 
76     /**
77      * Indicates whether this stream itself is closed.
78      * If it isn't, but {@link #wrappedStream wrappedStream}
79      * is <code>null</code>, we're running in EOF mode.
80      * All read operations will indicate EOF without accessing
81      * the underlying stream. After closing this stream, read
82      * operations will trigger an {@link IOException IOException}.
83      *
84      * @see #isReadAllowed isReadAllowed
85      */
86     private boolean selfClosed;
87 
88     /** The watcher to be notified, if any. */
89     private EofSensorWatcher eofWatcher;
90 
91 
92     /**
93      * Creates a new EOF sensor.
94      * If no watcher is passed, the underlying stream will simply be
95      * closed when EOF is detected or {@link #close close} is called.
96      * Otherwise, the watcher decides whether the underlying stream
97      * should be closed before detaching from it.
98      *
99      * @param in        the wrapped stream
100      * @param watcher   the watcher for events, or <code>null</code> for
101      *                  auto-close behavior without notification
102      */
EofSensorInputStream(final InputStream in, final EofSensorWatcher watcher)103     public EofSensorInputStream(final InputStream in,
104                                 final EofSensorWatcher watcher) {
105         if (in == null) {
106             throw new IllegalArgumentException
107                 ("Wrapped stream may not be null.");
108         }
109 
110         wrappedStream = in;
111         selfClosed = false;
112         eofWatcher = watcher;
113     }
114 
115 
116     /**
117      * Checks whether the underlying stream can be read from.
118      *
119      * @return  <code>true</code> if the underlying stream is accessible,
120      *          <code>false</code> if this stream is in EOF mode and
121      *          detached from the underlying stream
122      *
123      * @throws IOException      if this stream is already closed
124      */
isReadAllowed()125     protected boolean isReadAllowed() throws IOException {
126         if (selfClosed) {
127             throw new IOException("Attempted read on closed stream.");
128         }
129         return (wrappedStream != null);
130     }
131 
132 
133     // non-javadoc, see base class InputStream
134     @Override
read()135     public int read() throws IOException {
136         int l = -1;
137 
138         if (isReadAllowed()) {
139             try {
140                 l = wrappedStream.read();
141                 checkEOF(l);
142             } catch (IOException ex) {
143                 checkAbort();
144                 throw ex;
145             }
146         }
147 
148         return l;
149     }
150 
151 
152     // non-javadoc, see base class InputStream
153     @Override
read(byte[] b, int off, int len)154     public int read(byte[] b, int off, int len) throws IOException {
155         int l = -1;
156 
157         if (isReadAllowed()) {
158             try {
159                 l = wrappedStream.read(b,  off,  len);
160                 checkEOF(l);
161             } catch (IOException ex) {
162                 checkAbort();
163                 throw ex;
164             }
165         }
166 
167         return l;
168     }
169 
170 
171     // non-javadoc, see base class InputStream
172     @Override
read(byte[] b)173     public int read(byte[] b) throws IOException {
174         int l = -1;
175 
176         if (isReadAllowed()) {
177             try {
178                 l = wrappedStream.read(b);
179                 checkEOF(l);
180             } catch (IOException ex) {
181                 checkAbort();
182                 throw ex;
183             }
184         }
185         return l;
186     }
187 
188 
189     // non-javadoc, see base class InputStream
190     @Override
available()191     public int available() throws IOException {
192         int a = 0; // not -1
193 
194         if (isReadAllowed()) {
195             try {
196                 a = wrappedStream.available();
197                 // no checkEOF() here, available() can't trigger EOF
198             } catch (IOException ex) {
199                 checkAbort();
200                 throw ex;
201             }
202         }
203 
204         return a;
205     }
206 
207 
208     // non-javadoc, see base class InputStream
209     @Override
close()210     public void close() throws IOException {
211         // tolerate multiple calls to close()
212         selfClosed = true;
213         checkClose();
214     }
215 
216 
217     /**
218      * Detects EOF and notifies the watcher.
219      * This method should only be called while the underlying stream is
220      * still accessible. Use {@link #isReadAllowed isReadAllowed} to
221      * check that condition.
222      * <br/>
223      * If EOF is detected, the watcher will be notified and this stream
224      * is detached from the underlying stream. This prevents multiple
225      * notifications from this stream.
226      *
227      * @param eof       the result of the calling read operation.
228      *                  A negative value indicates that EOF is reached.
229      *
230      * @throws IOException
231      *          in case of an IO problem on closing the underlying stream
232      */
checkEOF(int eof)233     protected void checkEOF(int eof) throws IOException {
234 
235         if ((wrappedStream != null) && (eof < 0)) {
236             try {
237                 boolean scws = true; // should close wrapped stream?
238                 if (eofWatcher != null)
239                     scws = eofWatcher.eofDetected(wrappedStream);
240                 if (scws)
241                     wrappedStream.close();
242             } finally {
243                 wrappedStream = null;
244             }
245         }
246     }
247 
248 
249     /**
250      * Detects stream close and notifies the watcher.
251      * There's not much to detect since this is called by {@link #close close}.
252      * The watcher will only be notified if this stream is closed
253      * for the first time and before EOF has been detected.
254      * This stream will be detached from the underlying stream to prevent
255      * multiple notifications to the watcher.
256      *
257      * @throws IOException
258      *          in case of an IO problem on closing the underlying stream
259      */
checkClose()260     protected void checkClose() throws IOException {
261 
262         if (wrappedStream != null) {
263             try {
264                 boolean scws = true; // should close wrapped stream?
265                 if (eofWatcher != null)
266                     scws = eofWatcher.streamClosed(wrappedStream);
267                 if (scws)
268                     wrappedStream.close();
269             } finally {
270                 wrappedStream = null;
271             }
272         }
273     }
274 
275 
276     /**
277      * Detects stream abort and notifies the watcher.
278      * There's not much to detect since this is called by
279      * {@link #abortConnection abortConnection}.
280      * The watcher will only be notified if this stream is aborted
281      * for the first time and before EOF has been detected or the
282      * stream has been {@link #close closed} gracefully.
283      * This stream will be detached from the underlying stream to prevent
284      * multiple notifications to the watcher.
285      *
286      * @throws IOException
287      *          in case of an IO problem on closing the underlying stream
288      */
checkAbort()289     protected void checkAbort() throws IOException {
290 
291         if (wrappedStream != null) {
292             try {
293                 boolean scws = true; // should close wrapped stream?
294                 if (eofWatcher != null)
295                     scws = eofWatcher.streamAbort(wrappedStream);
296                 if (scws)
297                     wrappedStream.close();
298             } finally {
299                 wrappedStream = null;
300             }
301         }
302     }
303 
304 
305     /**
306      * Same as {@link #close close()}.
307      */
releaseConnection()308     public void releaseConnection() throws IOException {
309         this.close();
310     }
311 
312     /**
313      * Aborts this stream.
314      * This is a special version of {@link #close close()} which prevents
315      * re-use of the underlying connection, if any. Calling this method
316      * indicates that there should be no attempt to read until the end of
317      * the stream.
318      */
abortConnection()319     public void abortConnection() throws IOException {
320         // tolerate multiple calls
321         selfClosed = true;
322         checkAbort();
323     }
324 
325 } // class EOFSensorInputStream
326 
327