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