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 }