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 }