package hudson.plugins.ec2.win.winrm; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PipedInputStream; import java.io.PipedOutputStream; import java.util.logging.Level; import java.util.logging.Logger; import com.google.common.io.Closeables; public class WindowsProcess { private static final Logger log = Logger.getLogger(WindowsProcess.class.getName()); private final static int INPUT_BUFFER = 16 * 1024; private final WinRMClient client; private final PipedInputStream toCallersStdin; private final PipedOutputStream callersStdin; private final PipedInputStream callersStdout; private final PipedOutputStream toCallersStdout; private final PipedInputStream callersStderr; private final PipedOutputStream toCallersStderr; private boolean terminated; private final String command; private Thread outputThread; private Thread inputThread; WindowsProcess(WinRMClient client, String command) throws IOException { this.client = client; this.command = command; toCallersStdin = new PipedInputStream(INPUT_BUFFER); callersStdin = new PipedOutputStream(toCallersStdin); callersStdout = new PipedInputStream(INPUT_BUFFER); toCallersStdout = new PipedOutputStream(callersStdout); callersStderr = new PipedInputStream(INPUT_BUFFER); toCallersStderr = new PipedOutputStream(callersStderr); startStdoutCopyThread(); try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } startStdinCopyThread(); } public InputStream getStdout() { return callersStdout; } public OutputStream getStdin() { return callersStdin; } public InputStream getStderr() { return callersStderr; } public synchronized int waitFor() { if (terminated) { return client.exitCode(); } try { try { outputThread.join(); } finally { client.deleteShell(); terminated = true; } return client.exitCode(); } catch (InterruptedException exc) { Thread.currentThread().interrupt(); throw new RuntimeException("Exception while executing command", exc); } } public synchronized void destroy() { if (terminated) { return; } client.signal(); client.deleteShell(); terminated = true; } private void startStdoutCopyThread() { outputThread = new Thread("output copy: " + command) { @Override public void run() { try { for (;;) { if (!client.slurpOutput(toCallersStdout, toCallersStderr)) { log.log(Level.FINE, "no more output for " + command); break; } } } catch (Exception exc) { log.log(Level.WARNING, "ouch, stdout exception for " + command, exc); exc.printStackTrace(); } finally { Closeables.closeQuietly(toCallersStdout); Closeables.closeQuietly(toCallersStderr); } } }; outputThread.setDaemon(true); outputThread.start(); } private void startStdinCopyThread() { inputThread = new Thread("input copy: " + command) { @Override public void run() { try { byte[] buf = new byte[INPUT_BUFFER]; for (;;) { int n = 0; try { n = toCallersStdin.read(buf); } catch (IOException ioe) { // it's safe to ignore IO Exception coming from // Jenkins // This can happen with PipedInputStream if the // writing Thread // is killed but the input stream is handed to // another thread // in this case, we can still read from the pipe. continue; } if (n == -1) break; if (n == 0) continue; byte[] bufToSend = new byte[n]; System.arraycopy(buf, 0, bufToSend, 0, n); log.log(Level.FINE, "piping " + bufToSend.length + " to input of " + command); client.sendInput(bufToSend); } } catch (Exception exc) { log.log(Level.WARNING, "ouch, STDIN exception for " + command, exc); } finally { Closeables.closeQuietly(callersStdin); } } }; inputThread.setDaemon(true); inputThread.start(); } }