    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    }