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    }