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  * @deprecated Please use {@link java.net.URL#openConnection} instead.
     64  *     Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a>
     65  *     for further details.
     66  */
     67 @Deprecated
     68 // don't use FilterInputStream as the base class, we'd have to
     69 // override markSupported(), mark(), and reset() to disable them
     70 public class EofSensorInputStream extends InputStream
     71     implements ConnectionReleaseTrigger {
     72 
     73     /**
     74      * The wrapped input stream, while accessible.
     75      * The value changes to <code>null</code> when the wrapped stream
     76      * becomes inaccessible.
     77      */
     78     protected InputStream wrappedStream;
     79 
     80 
     81     /**
     82      * Indicates whether this stream itself is closed.
     83      * If it isn't, but {@link #wrappedStream wrappedStream}
     84      * is <code>null</code>, we're running in EOF mode.
     85      * All read operations will indicate EOF without accessing
     86      * the underlying stream. After closing this stream, read
     87      * operations will trigger an {@link IOException IOException}.
     88      *
     89      * @see #isReadAllowed isReadAllowed
     90      */
     91     private boolean selfClosed;
     92 
     93     /** The watcher to be notified, if any. */
     94     private EofSensorWatcher eofWatcher;
     95 
     96 
     97     /**
     98      * Creates a new EOF sensor.
     99      * If no watcher is passed, the underlying stream will simply be
    100      * closed when EOF is detected or {@link #close close} is called.
    101      * Otherwise, the watcher decides whether the underlying stream
    102      * should be closed before detaching from it.
    103      *
    104      * @param in        the wrapped stream
    105      * @param watcher   the watcher for events, or <code>null</code> for
    106      *                  auto-close behavior without notification
    107      */
    108     public EofSensorInputStream(final InputStream in,
    109                                 final EofSensorWatcher watcher) {
    110         if (in == null) {
    111             throw new IllegalArgumentException
    112                 ("Wrapped stream may not be null.");
    113         }
    114 
    115         wrappedStream = in;
    116         selfClosed = false;
    117         eofWatcher = watcher;
    118     }
    119 
    120 
    121     /**
    122      * Checks whether the underlying stream can be read from.
    123      *
    124      * @return  <code>true</code> if the underlying stream is accessible,
    125      *          <code>false</code> if this stream is in EOF mode and
    126      *          detached from the underlying stream
    127      *
    128      * @throws IOException      if this stream is already closed
    129      */
    130     protected boolean isReadAllowed() throws IOException {
    131         if (selfClosed) {
    132             throw new IOException("Attempted read on closed stream.");
    133         }
    134         return (wrappedStream != null);
    135     }
    136 
    137 
    138     // non-javadoc, see base class InputStream
    139     @Override
    140     public int read() throws IOException {
    141         int l = -1;
    142 
    143         if (isReadAllowed()) {
    144             try {
    145                 l = wrappedStream.read();
    146                 checkEOF(l);
    147             } catch (IOException ex) {
    148                 checkAbort();
    149                 throw ex;
    150             }
    151         }
    152 
    153         return l;
    154     }
    155 
    156 
    157     // non-javadoc, see base class InputStream
    158     @Override
    159     public int read(byte[] b, int off, int len) throws IOException {
    160         int l = -1;
    161 
    162         if (isReadAllowed()) {
    163             try {
    164                 l = wrappedStream.read(b,  off,  len);
    165                 checkEOF(l);
    166             } catch (IOException ex) {
    167                 checkAbort();
    168                 throw ex;
    169             }
    170         }
    171 
    172         return l;
    173     }
    174 
    175 
    176     // non-javadoc, see base class InputStream
    177     @Override
    178     public int read(byte[] b) throws IOException {
    179         int l = -1;
    180 
    181         if (isReadAllowed()) {
    182             try {
    183                 l = wrappedStream.read(b);
    184                 checkEOF(l);
    185             } catch (IOException ex) {
    186                 checkAbort();
    187                 throw ex;
    188             }
    189         }
    190         return l;
    191     }
    192 
    193 
    194     // non-javadoc, see base class InputStream
    195     @Override
    196     public int available() throws IOException {
    197         int a = 0; // not -1
    198 
    199         if (isReadAllowed()) {
    200             try {
    201                 a = wrappedStream.available();
    202                 // no checkEOF() here, available() can't trigger EOF
    203             } catch (IOException ex) {
    204                 checkAbort();
    205                 throw ex;
    206             }
    207         }
    208 
    209         return a;
    210     }
    211 
    212 
    213     // non-javadoc, see base class InputStream
    214     @Override
    215     public void close() throws IOException {
    216         // tolerate multiple calls to close()
    217         selfClosed = true;
    218         checkClose();
    219     }
    220 
    221 
    222     /**
    223      * Detects EOF and notifies the watcher.
    224      * This method should only be called while the underlying stream is
    225      * still accessible. Use {@link #isReadAllowed isReadAllowed} to
    226      * check that condition.
    227      * <br/>
    228      * If EOF is detected, the watcher will be notified and this stream
    229      * is detached from the underlying stream. This prevents multiple
    230      * notifications from this stream.
    231      *
    232      * @param eof       the result of the calling read operation.
    233      *                  A negative value indicates that EOF is reached.
    234      *
    235      * @throws IOException
    236      *          in case of an IO problem on closing the underlying stream
    237      */
    238     protected void checkEOF(int eof) throws IOException {
    239 
    240         if ((wrappedStream != null) && (eof < 0)) {
    241             try {
    242                 boolean scws = true; // should close wrapped stream?
    243                 if (eofWatcher != null)
    244                     scws = eofWatcher.eofDetected(wrappedStream);
    245                 if (scws)
    246                     wrappedStream.close();
    247             } finally {
    248                 wrappedStream = null;
    249             }
    250         }
    251     }
    252 
    253 
    254     /**
    255      * Detects stream close and notifies the watcher.
    256      * There's not much to detect since this is called by {@link #close close}.
    257      * The watcher will only be notified if this stream is closed
    258      * for the first time and before EOF has been detected.
    259      * This stream will be detached from the underlying stream to prevent
    260      * multiple notifications to the watcher.
    261      *
    262      * @throws IOException
    263      *          in case of an IO problem on closing the underlying stream
    264      */
    265     protected void checkClose() throws IOException {
    266 
    267         if (wrappedStream != null) {
    268             try {
    269                 boolean scws = true; // should close wrapped stream?
    270                 if (eofWatcher != null)
    271                     scws = eofWatcher.streamClosed(wrappedStream);
    272                 if (scws)
    273                     wrappedStream.close();
    274             } finally {
    275                 wrappedStream = null;
    276             }
    277         }
    278     }
    279 
    280 
    281     /**
    282      * Detects stream abort and notifies the watcher.
    283      * There's not much to detect since this is called by
    284      * {@link #abortConnection abortConnection}.
    285      * The watcher will only be notified if this stream is aborted
    286      * for the first time and before EOF has been detected or the
    287      * stream has been {@link #close closed} gracefully.
    288      * This stream will be detached from the underlying stream to prevent
    289      * multiple notifications to the watcher.
    290      *
    291      * @throws IOException
    292      *          in case of an IO problem on closing the underlying stream
    293      */
    294     protected void checkAbort() throws IOException {
    295 
    296         if (wrappedStream != null) {
    297             try {
    298                 boolean scws = true; // should close wrapped stream?
    299                 if (eofWatcher != null)
    300                     scws = eofWatcher.streamAbort(wrappedStream);
    301                 if (scws)
    302                     wrappedStream.close();
    303             } finally {
    304                 wrappedStream = null;
    305             }
    306         }
    307     }
    308 
    309 
    310     /**
    311      * Same as {@link #close close()}.
    312      */
    313     public void releaseConnection() throws IOException {
    314         this.close();
    315     }
    316 
    317     /**
    318      * Aborts this stream.
    319      * This is a special version of {@link #close close()} which prevents
    320      * re-use of the underlying connection, if any. Calling this method
    321      * indicates that there should be no attempt to read until the end of
    322      * the stream.
    323      */
    324     public void abortConnection() throws IOException {
    325         // tolerate multiple calls
    326         selfClosed = true;
    327         checkAbort();
    328     }
    329 
    330 } // class EOFSensorInputStream
    331 
    332