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