View Javadoc

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 }