View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.io.input;
18  
19  import static org.apache.commons.io.IOUtils.EOF;
20  
21  import java.io.FilterInputStream;
22  import java.io.IOException;
23  import java.io.InputStream;
24  
25  import org.apache.commons.io.IOUtils;
26  
27  /**
28   * A proxy stream which acts as a {@link FilterInputStream}, by passing all method calls on to the proxied stream, not changing which methods are called.
29   * <p>
30   * It is an alternative base class to {@link FilterInputStream} to increase reusability, because {@link FilterInputStream} changes the methods being called,
31   * such as read(byte[]) to read(byte[], int, int).
32   * </p>
33   * <p>
34   * In addition, this class allows you to:
35   * </p>
36   * <ul>
37   * <li>notify a subclass that <em>n</em> bytes are about to be read through {@link #beforeRead(int)}</li>
38   * <li>notify a subclass that <em>n</em> bytes were read through {@link #afterRead(int)}</li>
39   * <li>notify a subclass that an exception was caught through {@link #handleIOException(IOException)}</li>
40   * <li>{@link #unwrap()} itself</li>
41   * </ul>
42   * <p>
43   * This class does not add any state (no additional instance variables).
44   * </p>
45   */
46  public abstract class ProxyInputStream extends FilterInputStream {
47  
48      /**
49       * Constructs a new ProxyInputStream.
50       *
51       * @param proxy  the InputStream to delegate to
52       */
53      public ProxyInputStream(final InputStream proxy) {
54          super(proxy);
55          // the proxy is stored in a protected superclass variable named 'in'
56      }
57  
58      /**
59       * Invoked by the {@code read} methods after the proxied call has returned
60       * successfully. The number of bytes returned to the caller (or -1 if
61       * the end of stream was reached) is given as an argument.
62       * <p>
63       * Subclasses can override this method to add common post-processing
64       * functionality without having to override all the read methods.
65       * The default implementation does nothing.
66       * </p>
67       * <p>
68       * Note this method is <em>not</em> called from {@link #skip(long)} or
69       * {@link #reset()}. You need to explicitly override those methods if
70       * you want to add post-processing steps also to them.
71       * </p>
72       *
73       * @since 2.0
74       * @param n number of bytes read, or -1 if the end of stream was reached.
75       * @throws IOException if the post-processing fails in a subclass.
76       */
77      @SuppressWarnings("unused") // Possibly thrown from subclasses.
78      protected void afterRead(final int n) throws IOException {
79          // no-op default
80      }
81  
82      /**
83       * Invokes the delegate's {@code available()} method.
84       *
85       * @return the number of available bytes
86       * @throws IOException if an I/O error occurs.
87       */
88      @Override
89      public int available() throws IOException {
90          try {
91              return super.available();
92          } catch (final IOException e) {
93              handleIOException(e);
94              return 0;
95          }
96      }
97  
98      /**
99       * Invoked by the {@code read} methods before the call is proxied. The number
100      * of bytes that the caller wanted to read (1 for the {@link #read()}
101      * method, buffer length for {@link #read(byte[])}, etc.) is given as
102      * an argument.
103      * <p>
104      * Subclasses can override this method to add common pre-processing
105      * functionality without having to override all the read methods.
106      * The default implementation does nothing.
107      * </p>
108      * <p>
109      * Note this method is <em>not</em> called from {@link #skip(long)} or
110      * {@link #reset()}. You need to explicitly override those methods if
111      * you want to add pre-processing steps also to them.
112      * </p>
113      *
114      * @since 2.0
115      * @param n number of bytes that the caller asked to be read.
116      * @throws IOException if the pre-processing fails in a subclass.
117      */
118     @SuppressWarnings("unused") // Possibly thrown from subclasses.
119     protected void beforeRead(final int n) throws IOException {
120         // no-op default
121     }
122 
123     /**
124      * Invokes the delegate's {@code close()} method.
125      *
126      * @throws IOException if an I/O error occurs.
127      */
128     @Override
129     public void close() throws IOException {
130         IOUtils.close(in, this::handleIOException);
131     }
132 
133     /**
134      * Handles any IOExceptions thrown; by default, throws the given exception.
135      * <p>
136      * This method provides a point to implement custom exception
137      * handling. The default behavior is to re-throw the exception.
138      * </p>
139      *
140      * @param e The IOException thrown
141      * @throws IOException if an I/O error occurs.
142      * @since 2.0
143      */
144     protected void handleIOException(final IOException e) throws IOException {
145         throw e;
146     }
147 
148     /**
149      * Invokes the delegate's {@code mark(int)} method.
150      *
151      * @param readLimit read ahead limit
152      */
153     @Override
154     public synchronized void mark(final int readLimit) {
155         in.mark(readLimit);
156     }
157 
158     /**
159      * Invokes the delegate's {@code markSupported()} method.
160      *
161      * @return true if mark is supported, otherwise false
162      */
163     @Override
164     public boolean markSupported() {
165         return in.markSupported();
166     }
167 
168     /**
169      * Invokes the delegate's {@code read()} method.
170      *
171      * @return the byte read or -1 if the end of stream
172      * @throws IOException if an I/O error occurs.
173      */
174     @Override
175     public int read() throws IOException {
176         try {
177             beforeRead(1);
178             final int b = in.read();
179             afterRead(b != EOF ? 1 : EOF);
180             return b;
181         } catch (final IOException e) {
182             handleIOException(e);
183             return EOF;
184         }
185     }
186 
187     /**
188      * Invokes the delegate's {@code read(byte[])} method.
189      *
190      * @param b the buffer to read the bytes into
191      * @return the number of bytes read or EOF if the end of stream
192      * @throws IOException if an I/O error occurs.
193      */
194     @Override
195     public int read(final byte[] b) throws IOException {
196         try {
197             beforeRead(IOUtils.length(b));
198             final int n = in.read(b);
199             afterRead(n);
200             return n;
201         } catch (final IOException e) {
202             handleIOException(e);
203             return EOF;
204         }
205     }
206 
207     /**
208      * Invokes the delegate's {@code read(byte[], int, int)} method.
209      *
210      * @param b the buffer to read the bytes into
211      * @param off The start offset
212      * @param len The number of bytes to read
213      * @return the number of bytes read or -1 if the end of stream
214      * @throws IOException if an I/O error occurs.
215      */
216     @Override
217     public int read(final byte[] b, final int off, final int len) throws IOException {
218         try {
219             beforeRead(len);
220             final int n = in.read(b, off, len);
221             afterRead(n);
222             return n;
223         } catch (final IOException e) {
224             handleIOException(e);
225             return EOF;
226         }
227     }
228 
229     /**
230      * Invokes the delegate's {@code reset()} method.
231      *
232      * @throws IOException if an I/O error occurs.
233      */
234     @Override
235     public synchronized void reset() throws IOException {
236         try {
237             in.reset();
238         } catch (final IOException e) {
239             handleIOException(e);
240         }
241     }
242 
243     /**
244      * Invokes the delegate's {@code skip(long)} method.
245      *
246      * @param n the number of bytes to skip
247      * @return the actual number of bytes skipped
248      * @throws IOException if an I/O error occurs.
249      */
250     @Override
251     public long skip(final long n) throws IOException {
252         try {
253             return in.skip(n);
254         } catch (final IOException e) {
255             handleIOException(e);
256             return 0;
257         }
258     }
259 
260     /**
261      * Unwraps this instance by returning the underlying InputStream.
262      * <p>
263      * Use with caution; useful to query the underlying InputStream.
264      * </p>
265      * @return the underlying InputStream.
266      * @since 2.16.0
267      */
268     public InputStream unwrap() {
269         return in;
270     }
271 }