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 all data from an input stream to an output stream.
029     */
030    public class StreamPumper implements Runnable {
031    
032        /** the default size of the internal buffer for copying the streams */
033        private static final int DEFAULT_SIZE = 1024;
034    
035        /** the input stream to pump from */
036        private final InputStream is;
037    
038        /** the output stream to pmp into */
039        private final OutputStream os;
040    
041        /** the size of the internal buffer for copying the streams */ 
042        private final int size;
043    
044        /** was the end of the stream reached */
045        private boolean finished;
046    
047        /** close the output stream when exhausted */
048        private final boolean closeWhenExhausted;
049        
050        /**
051         * Create a new stream pumper.
052         * 
053         * @param is input stream to read data from
054         * @param os output stream to write data to.
055         * @param closeWhenExhausted if true, the output stream will be closed when the input is exhausted.
056         */
057        public StreamPumper(final InputStream is, final OutputStream os,
058                final boolean closeWhenExhausted) {
059            this.is = is;
060            this.os = os;
061            this.size = DEFAULT_SIZE;
062            this.closeWhenExhausted = closeWhenExhausted;
063        }
064    
065        /**
066         * Create a new stream pumper.
067         *
068         * @param is input stream to read data from
069         * @param os output stream to write data to.
070         * @param closeWhenExhausted if true, the output stream will be closed when the input is exhausted.
071         * @param size the size of the internal buffer for copying the streams
072         */
073        public StreamPumper(final InputStream is, final OutputStream os,
074                final boolean closeWhenExhausted, final int size) {
075            this.is = is;
076            this.os = os;
077            this.size = (size > 0 ? size : DEFAULT_SIZE);
078            this.closeWhenExhausted = closeWhenExhausted;
079        }
080    
081        /**
082         * Create a new stream pumper.
083         * 
084         * @param is input stream to read data from
085         * @param os output stream to write data to.
086         */
087        public StreamPumper(final InputStream is, final OutputStream os) {
088            this(is, os, false);
089        }
090    
091        /**
092         * Copies data from the input stream to the output stream. Terminates as
093         * soon as the input stream is closed or an error occurs.
094         */
095        public void run() {
096            synchronized (this) {
097                // Just in case this object is reused in the future
098                finished = false;
099            }
100    
101            final byte[] buf = new byte[this.size];
102    
103            int length;
104            try {
105                while ((length = is.read(buf)) > 0) {
106                    os.write(buf, 0, length);
107                }
108            } catch (Exception e) {
109                // nothing to do - happens quite often with watchdog
110            } finally {
111                if (closeWhenExhausted) {
112                    try {
113                        os.close();
114                    } catch (IOException e) {
115                        String msg = "Got exception while closing exhausted output stream";
116                        DebugUtils.handleException(msg ,e);
117                    }
118                }
119                synchronized (this) {
120                    finished = true;
121                    notifyAll();
122                }
123            }
124        }
125    
126        /**
127         * Tells whether the end of the stream has been reached.
128         * 
129         * @return true is the stream has been exhausted.
130         */
131        public synchronized boolean isFinished() {
132            return finished;
133        }
134    
135        /**
136         * This method blocks until the stream pumper finishes.
137         * 
138         * @see #isFinished()
139         */
140        public synchronized void waitFor() throws InterruptedException {
141            while (!isFinished()) {
142                wait();
143            }
144        }
145    }