Home | History | Annotate | Download | only in conn
      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 (at) 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      */
    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      */
    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
    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
    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
    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
    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
    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      */
    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      */
    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      */
    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      */
    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      */
    319     public void abortConnection() throws IOException {
    320         // tolerate multiple calls
    321         selfClosed = true;
    322         checkAbort();
    323     }
    324 
    325 } // class EOFSensorInputStream
    326 
    327