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    }