View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   https://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.commons.exec;
21  
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.OutputStream;
25  
26  import org.apache.commons.exec.util.DebugUtils;
27  
28  /**
29   * Copies all data from an input stream to an output stream.
30   */
31  public class StreamPumper implements Runnable {
32  
33      /** The default size of the internal buffer for copying the streams. */
34      private static final int DEFAULT_SIZE = 1024;
35  
36      /** The input stream to pump from. */
37      private final InputStream is;
38  
39      /** The output stream to pmp into. */
40      private final OutputStream os;
41  
42      /** The size of the internal buffer for copying the streams. */
43      private final int size;
44  
45      /** Was the end of the stream reached. */
46      private boolean finished;
47  
48      /** Close the output stream when exhausted. */
49      private final boolean closeWhenExhausted;
50  
51      /**
52       * Constructs a new stream pumper.
53       *
54       * @param is input stream to read data from.
55       * @param os output stream to write data to.
56       */
57      public StreamPumper(final InputStream is, final OutputStream os) {
58          this(is, os, false);
59      }
60  
61      /**
62       * Constructs a new stream pumper.
63       *
64       * @param is                 input stream to read data from.
65       * @param os                 output stream to write data to.
66       * @param closeWhenExhausted if true, the output stream will be closed when the input is exhausted.
67       */
68      public StreamPumper(final InputStream is, final OutputStream os, final boolean closeWhenExhausted) {
69          this.is = is;
70          this.os = os;
71          this.size = DEFAULT_SIZE;
72          this.closeWhenExhausted = closeWhenExhausted;
73      }
74  
75      /**
76       * Constructs a new stream pumper.
77       *
78       * @param is                 input stream to read data from.
79       * @param os                 output stream to write data to.
80       * @param closeWhenExhausted if true, the output stream will be closed when the input is exhausted.
81       * @param size               the size of the internal buffer for copying the streams.
82       */
83      public StreamPumper(final InputStream is, final OutputStream os, final boolean closeWhenExhausted, final int size) {
84          this.is = is;
85          this.os = os;
86          this.size = size > 0 ? size : DEFAULT_SIZE;
87          this.closeWhenExhausted = closeWhenExhausted;
88      }
89  
90      /**
91       * Tests whether the end of the stream has been reached.
92       *
93       * @return true is the stream has been exhausted.
94       */
95      public synchronized boolean isFinished() {
96          return finished;
97      }
98  
99      /**
100      * Copies data from the input stream to the output stream. Terminates as soon as the input stream is closed or an error occurs.
101      */
102     @Override
103     public void run() {
104         synchronized (this) {
105             // Just in case this object is reused in the future
106             finished = false;
107         }
108 
109         final byte[] buf = new byte[this.size];
110 
111         int length;
112         try {
113             while ((length = is.read(buf)) > 0) {
114                 os.write(buf, 0, length);
115             }
116         } catch (final Exception ignored) {
117             // nothing to do - happens quite often with watchdog
118         } finally {
119             if (closeWhenExhausted) {
120                 try {
121                     os.close();
122                 } catch (final IOException e) {
123                     final String msg = "Got exception while closing exhausted output stream";
124                     DebugUtils.handleException(msg, e);
125                 }
126             }
127             synchronized (this) {
128                 finished = true;
129                 notifyAll();
130             }
131         }
132     }
133 
134     /**
135      * This method blocks until the stream pumper finishes.
136      *
137      * @throws InterruptedException if any thread interrupted the current thread before or while the current thread was waiting for a notification.
138      * @see #isFinished()
139      */
140     public synchronized void waitFor() throws InterruptedException {
141         while (!isFinished()) {
142             wait();
143         }
144     }
145 }