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 /** 047 * Construct a new <CODE>PumpStreamHandler</CODE>. 048 * 049 * @param out 050 * the output <CODE>OutputStream</CODE>. 051 * @param err 052 * the error <CODE>OutputStream</CODE>. 053 * @param input 054 * the input <CODE>InputStream</CODE>. 055 */ 056 public PumpStreamHandler(final OutputStream out, final OutputStream err, 057 final InputStream input) { 058 059 // see EXEC-33 060 if(input == System.in) { 061 String msg = "Using System.in is currently not supported since it would hang your application (see EXEC-33)."; 062 throw new IllegalArgumentException(msg); 063 } 064 065 this.out = out; 066 this.err = err; 067 this.input = input; 068 } 069 070 /** 071 * Construct a new <CODE>PumpStreamHandler</CODE>. 072 * 073 * @param out 074 * the output <CODE>OutputStream</CODE>. 075 * @param err 076 * the error <CODE>OutputStream</CODE>. 077 */ 078 public PumpStreamHandler(final OutputStream out, final OutputStream err) { 079 this(out, err, null); 080 } 081 082 /** 083 * Construct a new <CODE>PumpStreamHandler</CODE>. 084 * 085 * @param outAndErr 086 * the output/error <CODE>OutputStream</CODE>. 087 */ 088 public PumpStreamHandler(final OutputStream outAndErr) { 089 this(outAndErr, outAndErr); 090 } 091 092 /** 093 * Construct a new <CODE>PumpStreamHandler</CODE>. 094 */ 095 public PumpStreamHandler() { 096 this(System.out, System.err); 097 } 098 099 /** 100 * Set the <CODE>InputStream</CODE> from which to read the standard output 101 * of the process. 102 * 103 * @param is 104 * the <CODE>InputStream</CODE>. 105 */ 106 public void setProcessOutputStream(final InputStream is) { 107 if (out != null) { 108 createProcessOutputPump(is, out); 109 } 110 } 111 112 /** 113 * Set the <CODE>InputStream</CODE> from which to read the standard error 114 * of the process. 115 * 116 * @param is 117 * the <CODE>InputStream</CODE>. 118 */ 119 public void setProcessErrorStream(final InputStream is) { 120 if (err != null) { 121 createProcessErrorPump(is, err); 122 } 123 } 124 125 /** 126 * Set the <CODE>OutputStream</CODE> by means of which input can be sent 127 * to the process. 128 * 129 * @param os 130 * the <CODE>OutputStream</CODE>. 131 */ 132 public void setProcessInputStream(final OutputStream os) { 133 if (input != null) { 134 inputThread = createPump(input, os, true); 135 } else { 136 try { 137 os.close(); 138 } catch (IOException e) { 139 String msg = "Got exception while closing output stream"; 140 DebugUtils.handleException(msg ,e); 141 } 142 } 143 } 144 145 /** 146 * Start the <CODE>Thread</CODE>s. 147 */ 148 public void start() { 149 if (outputThread != null) { 150 outputThread.start(); 151 } 152 if (errorThread != null) { 153 errorThread.start(); 154 } 155 if (inputThread != null) { 156 inputThread.start(); 157 } 158 } 159 160 /** 161 * Stop pumping the streams. 162 */ 163 public void stop() { 164 165 if (outputThread != null) { 166 try { 167 outputThread.join(); 168 outputThread = null; 169 } catch (InterruptedException e) { 170 // ignore 171 } 172 } 173 174 if (errorThread != null) { 175 try { 176 errorThread.join(); 177 errorThread = null; 178 } catch (InterruptedException e) { 179 // ignore 180 } 181 } 182 183 if (inputThread != null) { 184 try { 185 inputThread.join(); 186 inputThread = null; 187 } catch (InterruptedException e) { 188 // ignore 189 } 190 } 191 192 if (err != null && err != out) { 193 try { 194 err.flush(); 195 } catch (IOException e) { 196 String msg = "Got exception while flushing the error stream"; 197 DebugUtils.handleException(msg ,e); 198 } 199 } 200 201 if (out != null) { 202 try { 203 out.flush(); 204 } catch (IOException e) { 205 String msg = "Got exception while flushing the output stream"; 206 DebugUtils.handleException(msg ,e); 207 } 208 } 209 } 210 211 /** 212 * Get the error stream. 213 * 214 * @return <CODE>OutputStream</CODE>. 215 */ 216 protected OutputStream getErr() { 217 return err; 218 } 219 220 /** 221 * Get the output stream. 222 * 223 * @return <CODE>OutputStream</CODE>. 224 */ 225 protected OutputStream getOut() { 226 return out; 227 } 228 229 /** 230 * Create the pump to handle process output. 231 * 232 * @param is 233 * the <CODE>InputStream</CODE>. 234 * @param os 235 * the <CODE>OutputStream</CODE>. 236 */ 237 protected void createProcessOutputPump(final InputStream is, 238 final OutputStream os) { 239 outputThread = createPump(is, os); 240 } 241 242 /** 243 * Create the pump to handle error output. 244 * 245 * @param is 246 * the <CODE>InputStream</CODE>. 247 * @param os 248 * the <CODE>OutputStream</CODE>. 249 */ 250 protected void createProcessErrorPump(final InputStream is, 251 final OutputStream os) { 252 errorThread = createPump(is, os); 253 } 254 255 /** 256 * Creates a stream pumper to copy the given input stream to the given 257 * output stream. 258 * 259 * @param is the input stream to copy from 260 * @param os the output stream to copy into 261 * @return the stream pumper thread 262 */ 263 protected Thread createPump(final InputStream is, final OutputStream os) { 264 return createPump(is, os, false); 265 } 266 267 /** 268 * Creates a stream pumper to copy the given input stream to the given 269 * output stream. 270 * 271 * @param is the input stream to copy from 272 * @param os the output stream to copy into 273 * @param closeWhenExhausted close the output stream when the input stream is exhausted 274 * @return the stream pumper thread 275 */ 276 protected Thread createPump(final InputStream is, final OutputStream os, 277 final boolean closeWhenExhausted) { 278 final Thread result = new Thread(new StreamPumper(is, os, 279 closeWhenExhausted)); 280 result.setDaemon(true); 281 return result; 282 } 283 }