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 public PumpStreamHandler() { 052 this(System.out, System.err); 053 } 054 055 /** 056 * Construct a new <CODE>PumpStreamHandler</CODE>. 057 * 058 * @param outAndErr 059 * the output/error <CODE>OutputStream</CODE>. 060 */ 061 public PumpStreamHandler(final OutputStream outAndErr) { 062 this(outAndErr, outAndErr); 063 } 064 065 /** 066 * Construct a new <CODE>PumpStreamHandler</CODE>. 067 * 068 * @param out 069 * the output <CODE>OutputStream</CODE>. 070 * @param err 071 * the error <CODE>OutputStream</CODE>. 072 */ 073 public PumpStreamHandler(final OutputStream out, final OutputStream err) { 074 this(out, err, null); 075 } 076 077 /** 078 * Construct a new <CODE>PumpStreamHandler</CODE>. 079 * 080 * @param out 081 * the output <CODE>OutputStream</CODE>. 082 * @param err 083 * the error <CODE>OutputStream</CODE>. 084 * @param input 085 * the input <CODE>InputStream</CODE>. 086 */ 087 public PumpStreamHandler(final OutputStream out, final OutputStream err, 088 final InputStream input) { 089 090 this.out = out; 091 this.err = err; 092 this.input = input; 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 } } 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 (inputStreamPumper != null) { 166 inputStreamPumper.stopProcessing(); 167 } 168 169 if (outputThread != null) { 170 try { 171 outputThread.join(); 172 outputThread = null; 173 } catch (InterruptedException e) { 174 // ignore 175 } 176 } 177 178 if (errorThread != null) { 179 try { 180 errorThread.join(); 181 errorThread = null; 182 } catch (InterruptedException e) { 183 // ignore 184 } 185 } 186 187 if (inputThread != null) { 188 try { 189 inputThread.join(); 190 inputThread = null; 191 } catch (InterruptedException e) { 192 // ignore 193 } 194 } 195 196 if (err != null && err != out) { 197 try { 198 err.flush(); 199 } catch (IOException e) { 200 String msg = "Got exception while flushing the error stream : " + e.getMessage(); 201 DebugUtils.handleException(msg ,e); 202 } 203 } 204 205 if (out != null) { 206 try { 207 out.flush(); 208 } catch (IOException e) { 209 String msg = "Got exception while flushing the output stream"; 210 DebugUtils.handleException(msg ,e); 211 } 212 } 213 } 214 215 /** 216 * Get the error stream. 217 * 218 * @return <CODE>OutputStream</CODE>. 219 */ 220 protected OutputStream getErr() { 221 return err; 222 } 223 224 /** 225 * Get the output stream. 226 * 227 * @return <CODE>OutputStream</CODE>. 228 */ 229 protected OutputStream getOut() { 230 return out; 231 } 232 233 /** 234 * Create the pump to handle process output. 235 * 236 * @param is 237 * the <CODE>InputStream</CODE>. 238 * @param os 239 * the <CODE>OutputStream</CODE>. 240 */ 241 protected void createProcessOutputPump(final InputStream is, 242 final OutputStream os) { 243 outputThread = createPump(is, os); 244 } 245 246 /** 247 * Create the pump to handle error output. 248 * 249 * @param is 250 * the <CODE>InputStream</CODE>. 251 * @param os 252 * the <CODE>OutputStream</CODE>. 253 */ 254 protected void createProcessErrorPump(final InputStream is, 255 final OutputStream os) { 256 errorThread = createPump(is, os); 257 } 258 259 /** 260 * Creates a stream pumper to copy the given input stream to the given 261 * output stream. 262 * 263 * @param is the input stream to copy from 264 * @param os the output stream to copy into 265 * @return the stream pumper thread 266 */ 267 protected Thread createPump(final InputStream is, final OutputStream os) { 268 return createPump(is, os, false); 269 } 270 271 /** 272 * Creates a stream pumper to copy the given input stream to the given 273 * output stream. 274 * 275 * @param is the input stream to copy from 276 * @param os the output stream to copy into 277 * @param closeWhenExhausted close the output stream when the input stream is exhausted 278 * @return the stream pumper thread 279 */ 280 protected Thread createPump(final InputStream is, final OutputStream os, 281 final boolean closeWhenExhausted) { 282 final Thread result = new Thread(new StreamPumper(is, os, 283 closeWhenExhausted)); 284 result.setDaemon(true); 285 return result; 286 } 287 288 289 /** 290 * Creates a stream pumper to copy the given input stream to the given 291 * output stream. 292 * 293 * @param is the System.in input stream to copy from 294 * @param os the output stream to copy into 295 * @return the stream pumper thread 296 */ 297 private Thread createSystemInPump(InputStream is, OutputStream os) { 298 inputStreamPumper = new InputStreamPumper(is, os); 299 final Thread result = new Thread(inputStreamPumper); 300 result.setDaemon(true); 301 return result; 302 } 303 304 }