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  
18  package org.apache.commons.exec;
19  
20  import java.io.IOException;
21  import java.io.InputStream;
22  import java.io.OutputStream;
23  
24  import org.apache.commons.exec.util.DebugUtils;
25  
26  /**
27   * Copies all data from an input stream to an output stream.
28   */
29  public class StreamPumper implements Runnable {
30  
31      /** The default size of the internal buffer for copying the streams. */
32      private static final int DEFAULT_SIZE = 1024;
33  
34      /** The input stream to pump from. */
35      private final InputStream is;
36  
37      /** The output stream to pmp into. */
38      private final OutputStream os;
39  
40      /** The size of the internal buffer for copying the streams. */
41      private final int size;
42  
43      /** Was the end of the stream reached. */
44      private boolean finished;
45  
46      /** Close the output stream when exhausted. */
47      private final boolean closeWhenExhausted;
48  
49      /**
50       * Constructs a new stream pumper.
51       *
52       * @param is input stream to read data from.
53       * @param os output stream to write data to.
54       */
55      public StreamPumper(final InputStream is, final OutputStream os) {
56          this(is, os, false);
57      }
58  
59      /**
60       * Constructs a new stream pumper.
61       *
62       * @param is                 input stream to read data from.
63       * @param os                 output stream to write data to.
64       * @param closeWhenExhausted if true, the output stream will be closed when the input is exhausted.
65       */
66      public StreamPumper(final InputStream is, final OutputStream os, final boolean closeWhenExhausted) {
67          this.is = is;
68          this.os = os;
69          this.size = DEFAULT_SIZE;
70          this.closeWhenExhausted = closeWhenExhausted;
71      }
72  
73      /**
74       * Constructs a new stream pumper.
75       *
76       * @param is                 input stream to read data from.
77       * @param os                 output stream to write data to.
78       * @param closeWhenExhausted if true, the output stream will be closed when the input is exhausted.
79       * @param size               the size of the internal buffer for copying the streams.
80       */
81      public StreamPumper(final InputStream is, final OutputStream os, final boolean closeWhenExhausted, final int size) {
82          this.is = is;
83          this.os = os;
84          this.size = size > 0 ? size : DEFAULT_SIZE;
85          this.closeWhenExhausted = closeWhenExhausted;
86      }
87  
88      /**
89       * Tests whether the end of the stream has been reached.
90       *
91       * @return true is the stream has been exhausted.
92       */
93      public synchronized boolean isFinished() {
94          return finished;
95      }
96  
97      /**
98       * Copies data from the input stream to the output stream. Terminates as soon as the input stream is closed or an error occurs.
99       */
100     @Override
101     public void run() {
102         synchronized (this) {
103             // Just in case this object is reused in the future
104             finished = false;
105         }
106 
107         final byte[] buf = new byte[this.size];
108 
109         int length;
110         try {
111             while ((length = is.read(buf)) > 0) {
112                 os.write(buf, 0, length);
113             }
114         } catch (final Exception ignored) {
115             // nothing to do - happens quite often with watchdog
116         } finally {
117             if (closeWhenExhausted) {
118                 try {
119                     os.close();
120                 } catch (final IOException e) {
121                     final String msg = "Got exception while closing exhausted output stream";
122                     DebugUtils.handleException(msg, e);
123                 }
124             }
125             synchronized (this) {
126                 finished = true;
127                 notifyAll();
128             }
129         }
130     }
131 
132     /**
133      * This method blocks until the stream pumper finishes.
134      *
135      * @throws InterruptedException if any thread interrupted the current thread before or while the current thread was waiting for a notification.
136      * @see #isFinished()
137      */
138     public synchronized void waitFor() throws InterruptedException {
139         while (!isFinished()) {
140             wait();
141         }
142     }
143 }