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
019package org.apache.commons.exec;
020
021import org.apache.commons.exec.util.DebugUtils;
022
023import java.io.IOException;
024import java.io.InputStream;
025import java.io.OutputStream;
026
027/**
028 * Copies all data from an input stream to an output stream.
029 */
030public 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}