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 /**
047 * Construct a new <CODE>PumpStreamHandler</CODE>.
048 *
049 * @param out
050 * the output <CODE>OutputStream</CODE>.
051 * @param err
052 * the error <CODE>OutputStream</CODE>.
053 * @param input
054 * the input <CODE>InputStream</CODE>.
055 */
056 public PumpStreamHandler(final OutputStream out, final OutputStream err,
057 final InputStream input) {
058
059 // see EXEC-33
060 if(input == System.in) {
061 String msg = "Using System.in is currently not supported since it would hang your application (see EXEC-33).";
062 throw new IllegalArgumentException(msg);
063 }
064
065 this.out = out;
066 this.err = err;
067 this.input = input;
068 }
069
070 /**
071 * Construct a new <CODE>PumpStreamHandler</CODE>.
072 *
073 * @param out
074 * the output <CODE>OutputStream</CODE>.
075 * @param err
076 * the error <CODE>OutputStream</CODE>.
077 */
078 public PumpStreamHandler(final OutputStream out, final OutputStream err) {
079 this(out, err, null);
080 }
081
082 /**
083 * Construct a new <CODE>PumpStreamHandler</CODE>.
084 *
085 * @param outAndErr
086 * the output/error <CODE>OutputStream</CODE>.
087 */
088 public PumpStreamHandler(final OutputStream outAndErr) {
089 this(outAndErr, outAndErr);
090 }
091
092 /**
093 * Construct a new <CODE>PumpStreamHandler</CODE>.
094 */
095 public PumpStreamHandler() {
096 this(System.out, System.err);
097 }
098
099 /**
100 * Set the <CODE>InputStream</CODE> from which to read the standard output
101 * of the process.
102 *
103 * @param is
104 * the <CODE>InputStream</CODE>.
105 */
106 public void setProcessOutputStream(final InputStream is) {
107 if (out != null) {
108 createProcessOutputPump(is, out);
109 }
110 }
111
112 /**
113 * Set the <CODE>InputStream</CODE> from which to read the standard error
114 * of the process.
115 *
116 * @param is
117 * the <CODE>InputStream</CODE>.
118 */
119 public void setProcessErrorStream(final InputStream is) {
120 if (err != null) {
121 createProcessErrorPump(is, err);
122 }
123 }
124
125 /**
126 * Set the <CODE>OutputStream</CODE> by means of which input can be sent
127 * to the process.
128 *
129 * @param os
130 * the <CODE>OutputStream</CODE>.
131 */
132 public void setProcessInputStream(final OutputStream os) {
133 if (input != null) {
134 inputThread = createPump(input, os, true);
135 } else {
136 try {
137 os.close();
138 } catch (IOException e) {
139 String msg = "Got exception while closing output stream";
140 DebugUtils.handleException(msg ,e);
141 }
142 }
143 }
144
145 /**
146 * Start the <CODE>Thread</CODE>s.
147 */
148 public void start() {
149 if (outputThread != null) {
150 outputThread.start();
151 }
152 if (errorThread != null) {
153 errorThread.start();
154 }
155 if (inputThread != null) {
156 inputThread.start();
157 }
158 }
159
160 /**
161 * Stop pumping the streams.
162 */
163 public void stop() {
164
165 if (outputThread != null) {
166 try {
167 outputThread.join();
168 outputThread = null;
169 } catch (InterruptedException e) {
170 // ignore
171 }
172 }
173
174 if (errorThread != null) {
175 try {
176 errorThread.join();
177 errorThread = null;
178 } catch (InterruptedException e) {
179 // ignore
180 }
181 }
182
183 if (inputThread != null) {
184 try {
185 inputThread.join();
186 inputThread = null;
187 } catch (InterruptedException e) {
188 // ignore
189 }
190 }
191
192 if (err != null && err != out) {
193 try {
194 err.flush();
195 } catch (IOException e) {
196 String msg = "Got exception while flushing the error stream";
197 DebugUtils.handleException(msg ,e);
198 }
199 }
200
201 if (out != null) {
202 try {
203 out.flush();
204 } catch (IOException e) {
205 String msg = "Got exception while flushing the output stream";
206 DebugUtils.handleException(msg ,e);
207 }
208 }
209 }
210
211 /**
212 * Get the error stream.
213 *
214 * @return <CODE>OutputStream</CODE>.
215 */
216 protected OutputStream getErr() {
217 return err;
218 }
219
220 /**
221 * Get the output stream.
222 *
223 * @return <CODE>OutputStream</CODE>.
224 */
225 protected OutputStream getOut() {
226 return out;
227 }
228
229 /**
230 * Create the pump to handle process output.
231 *
232 * @param is
233 * the <CODE>InputStream</CODE>.
234 * @param os
235 * the <CODE>OutputStream</CODE>.
236 */
237 protected void createProcessOutputPump(final InputStream is,
238 final OutputStream os) {
239 outputThread = createPump(is, os);
240 }
241
242 /**
243 * Create the pump to handle error output.
244 *
245 * @param is
246 * the <CODE>InputStream</CODE>.
247 * @param os
248 * the <CODE>OutputStream</CODE>.
249 */
250 protected void createProcessErrorPump(final InputStream is,
251 final OutputStream os) {
252 errorThread = createPump(is, os);
253 }
254
255 /**
256 * Creates a stream pumper to copy the given input stream to the given
257 * output stream.
258 *
259 * @param is the input stream to copy from
260 * @param os the output stream to copy into
261 * @return the stream pumper thread
262 */
263 protected Thread createPump(final InputStream is, final OutputStream os) {
264 return createPump(is, os, false);
265 }
266
267 /**
268 * Creates a stream pumper to copy the given input stream to the given
269 * output stream.
270 *
271 * @param is the input stream to copy from
272 * @param os the output stream to copy into
273 * @param closeWhenExhausted close the output stream when the input stream is exhausted
274 * @return the stream pumper thread
275 */
276 protected Thread createPump(final InputStream is, final OutputStream os,
277 final boolean closeWhenExhausted) {
278 final Thread result = new Thread(new StreamPumper(is, os,
279 closeWhenExhausted));
280 result.setDaemon(true);
281 return result;
282 }
283 }