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.IOException; 22 import java.io.InputStream; 23 import java.io.OutputStream; 24 25 /** 26 * InputStream proxy that transparently writes a copy of all bytes read 27 * from the proxied stream to a given OutputStream. Using {@link #skip(long)} 28 * or {@link #mark(int)}/{@link #reset()} on the stream will result on some 29 * bytes from the input stream being skipped or duplicated in the output 30 * stream. 31 * <p> 32 * The proxied input stream is closed when the {@link #close()} method is 33 * called on this proxy. You may configure whether the input stream closes the 34 * output stream. 35 * </p> 36 * 37 * @since 1.4 38 * @see ObservableInputStream 39 */ 40 public class TeeInputStream extends ProxyInputStream { 41 42 /** 43 * The output stream that will receive a copy of all bytes read from the 44 * proxied input stream. 45 */ 46 private final OutputStream branch; 47 48 /** 49 * Flag for closing the associated output stream when this stream is closed. 50 */ 51 private final boolean closeBranch; 52 53 /** 54 * Constructs a TeeInputStream that proxies the given {@link InputStream} 55 * and copies all read bytes to the given {@link OutputStream}. The given 56 * output stream will not be closed when this stream gets closed. 57 * 58 * @param input input stream to be proxied 59 * @param branch output stream that will receive a copy of all bytes read 60 */ 61 public TeeInputStream(final InputStream input, final OutputStream branch) { 62 this(input, branch, false); 63 } 64 65 /** 66 * Constructs a TeeInputStream that proxies the given {@link InputStream} 67 * and copies all read bytes to the given {@link OutputStream}. The given 68 * output stream will be closed when this stream gets closed if the 69 * closeBranch parameter is {@code true}. 70 * 71 * @param input input stream to be proxied 72 * @param branch output stream that will receive a copy of all bytes read 73 * @param closeBranch flag for closing also the output stream when this 74 * stream is closed 75 */ 76 public TeeInputStream( 77 final InputStream input, final OutputStream branch, final boolean closeBranch) { 78 super(input); 79 this.branch = branch; 80 this.closeBranch = closeBranch; 81 } 82 83 /** 84 * Closes the proxied input stream and, if so configured, the associated 85 * output stream. An exception thrown from one stream will not prevent 86 * closing of the other stream. 87 * 88 * @throws IOException if either of the streams could not be closed 89 */ 90 @Override 91 public void close() throws IOException { 92 try { 93 super.close(); 94 } finally { 95 if (closeBranch) { 96 branch.close(); 97 } 98 } 99 } 100 101 /** 102 * Reads a single byte from the proxied input stream and writes it to 103 * the associated output stream. 104 * 105 * @return next byte from the stream, or -1 if the stream has ended 106 * @throws IOException if the stream could not be read (or written) 107 */ 108 @Override 109 public int read() throws IOException { 110 final int ch = super.read(); 111 if (ch != EOF) { 112 branch.write(ch); 113 } 114 return ch; 115 } 116 117 /** 118 * Reads bytes from the proxied input stream and writes the read bytes 119 * to the associated output stream. 120 * 121 * @param bts byte buffer 122 * @return number of bytes read, or -1 if the stream has ended 123 * @throws IOException if the stream could not be read (or written) 124 */ 125 @Override 126 public int read(final byte[] bts) throws IOException { 127 final int n = super.read(bts); 128 if (n != EOF) { 129 branch.write(bts, 0, n); 130 } 131 return n; 132 } 133 134 /** 135 * Reads bytes from the proxied input stream and writes the read bytes 136 * to the associated output stream. 137 * 138 * @param bts byte buffer 139 * @param st start offset within the buffer 140 * @param end maximum number of bytes to read 141 * @return number of bytes read, or -1 if the stream has ended 142 * @throws IOException if the stream could not be read (or written) 143 */ 144 @Override 145 public int read(final byte[] bts, final int st, final int end) throws IOException { 146 final int n = super.read(bts, st, end); 147 if (n != EOF) { 148 branch.write(bts, st, n); 149 } 150 return n; 151 } 152 153 }