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 * https://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 }