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