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