001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 *
017 */
018
019 package org.apache.commons.exec;
020
021 import org.apache.commons.exec.util.DebugUtils;
022
023 import java.io.IOException;
024 import java.io.InputStream;
025 import java.io.OutputStream;
026
027 /**
028 * Copies standard output and error of subprocesses to standard output and error
029 * of the parent process. If output or error stream are set to null, any feedback
030 * from that stream will be lost.
031 */
032 public class PumpStreamHandler implements ExecuteStreamHandler {
033
034 private Thread outputThread;
035
036 private Thread errorThread;
037
038 private Thread inputThread;
039
040 private final OutputStream out;
041
042 private final OutputStream err;
043
044 private final InputStream input;
045
046 private InputStreamPumper inputStreamPumper;
047
048 /**
049 * Construct a new <CODE>PumpStreamHandler</CODE>.
050 *
051 * @param out
052 * the output <CODE>OutputStream</CODE>.
053 * @param err
054 * the error <CODE>OutputStream</CODE>.
055 * @param input
056 * the input <CODE>InputStream</CODE>.
057 */
058 public PumpStreamHandler(final OutputStream out, final OutputStream err,
059 final InputStream input) {
060
061 this.out = out;
062 this.err = err;
063 this.input = input;
064 }
065
066 /**
067 * Construct a new <CODE>PumpStreamHandler</CODE>.
068 *
069 * @param out
070 * the output <CODE>OutputStream</CODE>.
071 * @param err
072 * the error <CODE>OutputStream</CODE>.
073 */
074 public PumpStreamHandler(final OutputStream out, final OutputStream err) {
075 this(out, err, null);
076 }
077
078 /**
079 * Construct a new <CODE>PumpStreamHandler</CODE>.
080 *
081 * @param outAndErr
082 * the output/error <CODE>OutputStream</CODE>.
083 */
084 public PumpStreamHandler(final OutputStream outAndErr) {
085 this(outAndErr, outAndErr);
086 }
087
088 /**
089 * Construct a new <CODE>PumpStreamHandler</CODE>.
090 */
091 public PumpStreamHandler() {
092 this(System.out, System.err);
093 }
094
095 /**
096 * Set the <CODE>InputStream</CODE> from which to read the standard output
097 * of the process.
098 *
099 * @param is
100 * the <CODE>InputStream</CODE>.
101 */
102 public void setProcessOutputStream(final InputStream is) {
103 if (out != null) {
104 createProcessOutputPump(is, out);
105 }
106 }
107
108 /**
109 * Set the <CODE>InputStream</CODE> from which to read the standard error
110 * of the process.
111 *
112 * @param is
113 * the <CODE>InputStream</CODE>.
114 */
115 public void setProcessErrorStream(final InputStream is) {
116 if (err != null) {
117 createProcessErrorPump(is, err);
118 }
119 }
120
121 /**
122 * Set the <CODE>OutputStream</CODE> by means of which input can be sent
123 * to the process.
124 *
125 * @param os
126 * the <CODE>OutputStream</CODE>.
127 */
128 public void setProcessInputStream(final OutputStream os) {
129 if (input != null) {
130 if (input == System.in) {
131 inputThread = createSystemInPump(input, os);
132 } else {
133 inputThread = createPump(input, os, true);
134 } } else {
135 try {
136 os.close();
137 } catch (IOException e) {
138 String msg = "Got exception while closing output stream";
139 DebugUtils.handleException(msg ,e);
140 }
141 }
142 }
143
144 /**
145 * Start the <CODE>Thread</CODE>s.
146 */
147 public void start() {
148 if (outputThread != null) {
149 outputThread.start();
150 }
151 if (errorThread != null) {
152 errorThread.start();
153 }
154 if (inputThread != null) {
155 inputThread.start();
156 }
157 }
158
159 /**
160 * Stop pumping the streams.
161 */
162 public void stop() {
163
164 if (outputThread != null) {
165 try {
166 outputThread.join();
167 outputThread = null;
168 } catch (InterruptedException e) {
169 // ignore
170 }
171 }
172
173 if (errorThread != null) {
174 try {
175 errorThread.join();
176 errorThread = null;
177 } catch (InterruptedException e) {
178 // ignore
179 }
180 }
181
182 if (inputStreamPumper != null) {
183 inputStreamPumper.stopProcessing();
184 }
185
186 if (inputThread != null) {
187 try {
188 inputThread.join();
189 inputThread = null;
190 } catch (InterruptedException e) {
191 // ignore
192 }
193 }
194
195 if (err != null && err != out) {
196 try {
197 err.flush();
198 } catch (IOException e) {
199 String msg = "Got exception while flushing the error stream";
200 DebugUtils.handleException(msg ,e);
201 }
202 }
203
204 if (out != null) {
205 try {
206 out.flush();
207 } catch (IOException e) {
208 String msg = "Got exception while flushing the output stream";
209 DebugUtils.handleException(msg ,e);
210 }
211 }
212 }
213
214 /**
215 * Get the error stream.
216 *
217 * @return <CODE>OutputStream</CODE>.
218 */
219 protected OutputStream getErr() {
220 return err;
221 }
222
223 /**
224 * Get the output stream.
225 *
226 * @return <CODE>OutputStream</CODE>.
227 */
228 protected OutputStream getOut() {
229 return out;
230 }
231
232 /**
233 * Create the pump to handle process output.
234 *
235 * @param is
236 * the <CODE>InputStream</CODE>.
237 * @param os
238 * the <CODE>OutputStream</CODE>.
239 */
240 protected void createProcessOutputPump(final InputStream is,
241 final OutputStream os) {
242 outputThread = createPump(is, os);
243 }
244
245 /**
246 * Create the pump to handle error output.
247 *
248 * @param is
249 * the <CODE>InputStream</CODE>.
250 * @param os
251 * the <CODE>OutputStream</CODE>.
252 */
253 protected void createProcessErrorPump(final InputStream is,
254 final OutputStream os) {
255 errorThread = createPump(is, os);
256 }
257
258 /**
259 * Creates a stream pumper to copy the given input stream to the given
260 * output stream.
261 *
262 * @param is the input stream to copy from
263 * @param os the output stream to copy into
264 * @return the stream pumper thread
265 */
266 protected Thread createPump(final InputStream is, final OutputStream os) {
267 return createPump(is, os, false);
268 }
269
270 /**
271 * Creates a stream pumper to copy the given input stream to the given
272 * output stream.
273 *
274 * @param is the input stream to copy from
275 * @param os the output stream to copy into
276 * @param closeWhenExhausted close the output stream when the input stream is exhausted
277 * @return the stream pumper thread
278 */
279 protected Thread createPump(final InputStream is, final OutputStream os,
280 final boolean closeWhenExhausted) {
281 final Thread result = new Thread(new StreamPumper(is, os,
282 closeWhenExhausted));
283 result.setDaemon(true);
284 return result;
285 }
286
287
288 /**
289 * Creates a stream pumper to copy the given input stream to the given
290 * output stream.
291 *
292 * @param is the System.in input stream to copy from
293 * @param os the output stream to copy into
294 * @return the stream pumper thread
295 */
296 private Thread createSystemInPump(InputStream is, OutputStream os) {
297 inputStreamPumper = new InputStreamPumper(is, os);
298 final Thread result = new Thread(inputStreamPumper);
299 result.setDaemon(true);
300 return result;
301 }
302 }