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 }