001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.io.input; 018 019import static org.apache.commons.io.IOUtils.EOF; 020 021import java.io.FilterInputStream; 022import java.io.IOException; 023import java.io.InputStream; 024 025import org.apache.commons.io.IOUtils; 026 027/** 028 * A Proxy stream which acts as expected, that is it passes the method 029 * calls on to the proxied stream and doesn't change which methods are 030 * being called. 031 * <p> 032 * It is an alternative base class to FilterInputStream 033 * to increase reusability, because FilterInputStream changes the 034 * methods being called, such as read(byte[]) to read(byte[], int, int). 035 * </p> 036 * <p> 037 * See the protected methods for ways in which a subclass can easily decorate 038 * a stream with custom pre-, post- or error processing functionality. 039 * </p> 040 */ 041public abstract class ProxyInputStream extends FilterInputStream { 042 043 /** 044 * Constructs a new ProxyInputStream. 045 * 046 * @param proxy the InputStream to delegate to 047 */ 048 public ProxyInputStream(final InputStream proxy) { 049 super(proxy); 050 // the proxy is stored in a protected superclass variable named 'in' 051 } 052 053 /** 054 * Invoked by the read methods after the proxied call has returned 055 * successfully. The number of bytes returned to the caller (or -1 if 056 * the end of stream was reached) is given as an argument. 057 * <p> 058 * Subclasses can override this method to add common post-processing 059 * functionality without having to override all the read methods. 060 * The default implementation does nothing. 061 * </p> 062 * <p> 063 * Note this method is <em>not</em> called from {@link #skip(long)} or 064 * {@link #reset()}. You need to explicitly override those methods if 065 * you want to add post-processing steps also to them. 066 * </p> 067 * @since 2.0 068 * @param n number of bytes read, or -1 if the end of stream was reached 069 * @throws IOException if the post-processing fails 070 */ 071 @SuppressWarnings("unused") // Possibly thrown from subclasses. 072 protected void afterRead(final int n) throws IOException { 073 // no-op 074 } 075 076 /** 077 * Invokes the delegate's {@code available()} method. 078 * @return the number of available bytes 079 * @throws IOException if an I/O error occurs. 080 */ 081 @Override 082 public int available() throws IOException { 083 try { 084 return super.available(); 085 } catch (final IOException e) { 086 handleIOException(e); 087 return 0; 088 } 089 } 090 091 /** 092 * Invoked by the read methods before the call is proxied. The number 093 * of bytes that the caller wanted to read (1 for the {@link #read()} 094 * method, buffer length for {@link #read(byte[])}, etc.) is given as 095 * an argument. 096 * <p> 097 * Subclasses can override this method to add common pre-processing 098 * functionality without having to override all the read methods. 099 * The default implementation does nothing. 100 * </p> 101 * <p> 102 * Note this method is <em>not</em> called from {@link #skip(long)} or 103 * {@link #reset()}. You need to explicitly override those methods if 104 * you want to add pre-processing steps also to them. 105 * </p> 106 * @since 2.0 107 * @param n number of bytes that the caller asked to be read 108 * @throws IOException if the pre-processing fails 109 */ 110 @SuppressWarnings("unused") // Possibly thrown from subclasses. 111 protected void beforeRead(final int n) throws IOException { 112 // no-op 113 } 114 115 /** 116 * Invokes the delegate's {@code close()} method. 117 * @throws IOException if an I/O error occurs. 118 */ 119 @Override 120 public void close() throws IOException { 121 IOUtils.close(in, this::handleIOException); 122 } 123 124 /** 125 * Handle any IOExceptions thrown; by default, throws the given exception. 126 * <p> 127 * This method provides a point to implement custom exception 128 * handling. The default behavior is to re-throw the exception. 129 * </p> 130 * @param e The IOException thrown 131 * @throws IOException if an I/O error occurs. 132 * @since 2.0 133 */ 134 protected void handleIOException(final IOException e) throws IOException { 135 throw e; 136 } 137 138 /** 139 * Invokes the delegate's {@code mark(int)} method. 140 * @param readlimit read ahead limit 141 */ 142 @Override 143 public synchronized void mark(final int readlimit) { 144 in.mark(readlimit); 145 } 146 147 /** 148 * Invokes the delegate's {@code markSupported()} method. 149 * @return true if mark is supported, otherwise false 150 */ 151 @Override 152 public boolean markSupported() { 153 return in.markSupported(); 154 } 155 156 /** 157 * Invokes the delegate's {@code read()} method. 158 * @return the byte read or -1 if the end of stream 159 * @throws IOException if an I/O error occurs. 160 */ 161 @Override 162 public int read() throws IOException { 163 try { 164 beforeRead(1); 165 final int b = in.read(); 166 afterRead(b != EOF ? 1 : EOF); 167 return b; 168 } catch (final IOException e) { 169 handleIOException(e); 170 return EOF; 171 } 172 } 173 174 /** 175 * Invokes the delegate's {@code read(byte[])} method. 176 * @param bts the buffer to read the bytes into 177 * @return the number of bytes read or EOF if the end of stream 178 * @throws IOException if an I/O error occurs. 179 */ 180 @Override 181 public int read(final byte[] bts) throws IOException { 182 try { 183 beforeRead(IOUtils.length(bts)); 184 final int n = in.read(bts); 185 afterRead(n); 186 return n; 187 } catch (final IOException e) { 188 handleIOException(e); 189 return EOF; 190 } 191 } 192 193 /** 194 * Invokes the delegate's {@code read(byte[], int, int)} method. 195 * @param bts the buffer to read the bytes into 196 * @param off The start offset 197 * @param len The number of bytes to read 198 * @return the number of bytes read or -1 if the end of stream 199 * @throws IOException if an I/O error occurs. 200 */ 201 @Override 202 public int read(final byte[] bts, final int off, final int len) throws IOException { 203 try { 204 beforeRead(len); 205 final int n = in.read(bts, off, len); 206 afterRead(n); 207 return n; 208 } catch (final IOException e) { 209 handleIOException(e); 210 return EOF; 211 } 212 } 213 214 /** 215 * Invokes the delegate's {@code reset()} method. 216 * @throws IOException if an I/O error occurs. 217 */ 218 @Override 219 public synchronized void reset() throws IOException { 220 try { 221 in.reset(); 222 } catch (final IOException e) { 223 handleIOException(e); 224 } 225 } 226 227 /** 228 * Invokes the delegate's {@code skip(long)} method. 229 * @param ln the number of bytes to skip 230 * @return the actual number of bytes skipped 231 * @throws IOException if an I/O error occurs. 232 */ 233 @Override 234 public long skip(final long ln) throws IOException { 235 try { 236 return in.skip(ln); 237 } catch (final IOException e) { 238 handleIOException(e); 239 return 0; 240 } 241 } 242 243}