/*******************************************************************************
* Copyright (c) 2012 Pivotal Software, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Pivotal Software, Inc. - initial API and implementation
*******************************************************************************/
package org.springsource.ide.eclipse.commons.frameworks.test.util;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import org.junit.Assert;
/**
* Convenient wrapper around a {@link Process}. Simplifies the synchronous execution of external
* commands by handling reading from out and error streams and either buffering the result output
* for later retrieval, or forwarding the to output to designated streams.
*
* @author Kris De Volder
*/
public class ExternalProcess {
/**
* A thread that keeps reading input from a Stream until the end is reached
* or there's some error reading the Stream.
*/
public static class StreamGobler extends Thread {
private final OutputStream echo;
private InputStream toRead; //Stream to read. This is nulled after all input has been consumed.
/**
* Creates a StreamGobler that reads input from an input stream
* and buffers up all input it has read for later retrieval via
* the getOut() method.
*/
public StreamGobler(InputStream toRead) {
this(toRead, new ByteArrayOutputStream());
}
/**
* Creates a StreamGobler that reads input from an input stream
* and writes it out to an outputstream.
*/
public StreamGobler(InputStream toRead, OutputStream forwardTo) {
this.toRead = toRead;
this.echo = forwardTo;
start();
}
@Override
public void run() {
byte[] buf = new byte[256];
while (toRead!=null) {
try {
int i = toRead.read(buf);
if (i==-1) {
//EOF
toRead = null; //Done!
} else {
append(buf, i);
}
} catch (IOException e) {
toRead = null;
ByteArrayOutputStream errMsg = new ByteArrayOutputStream();
e.printStackTrace(new PrintStream(errMsg));
append(errMsg.toByteArray());
}
}
}
private void append(byte[] buf) {
append(buf, buf.length);
}
private void append(byte[] buf, int len) {
if (echo!=null) {
try {
echo.write(buf, 0, len);
} catch (IOException e) {
}
}
}
public String getContents() throws InterruptedException {
try {
this.join();
if (echo instanceof ByteArrayOutputStream) {
return ((ByteArrayOutputStream)echo).toString();
} else {
return null;
}
} finally {
toRead = null;
}
}
}
private Process process;
private StreamGobler err; // Standard error is to be read from here
private StreamGobler out; // Standard out is to be read from here
private int exitValue = -9999;
private ExternalCommand cmd;
/**
* Creates an external process and waits for it to terminate. The output and error streams
* will be read and forwarded to System.out and System.err
*/
public ExternalProcess(File workingDir, ExternalCommand cmd) throws IOException, InterruptedException {
this(workingDir, cmd, false);
}
private void init(File workingDir, ExternalCommand cmd,
OutputStream outStream, OutputStream errStream) throws IOException,
InterruptedException {
this.cmd = cmd;
ProcessBuilder processBuilder = new ProcessBuilder(cmd.getProgramAndArgs());
processBuilder.directory(workingDir);
cmd.configure(processBuilder);
process = processBuilder.start();
err = new StreamGobler(process.getErrorStream(), errStream);
out = new StreamGobler(process.getInputStream(), outStream);
exitValue = process.waitFor();
}
public ExternalProcess(File workingDir, ExternalCommand cmd, boolean captureStreams) throws IOException, InterruptedException {
if (captureStreams) {
init(workingDir, cmd, new ByteArrayOutputStream(), new ByteArrayOutputStream());
} else {
init(workingDir, cmd, System.out, System.err);
}
}
public String getOut() throws InterruptedException {
return out.getContents();
}
public String getErr() throws InterruptedException {
return err.getContents();
}
@Override
public String toString() {
StringBuffer result = new StringBuffer();
try {
process.exitValue();
result.append(">>>> ExternalProcess: ");
result.append(cmd+"\n");
result.append("exitValue = "+exitValue+"\n");
String strOut = getOut();
if (strOut!=null) {
result.append("\n------- System.out -------\n");
result.append(strOut);
}
String strErr = getErr();
if (strErr!=null) {
result.append("\n------- System.err -------\n");
result.append(getOut());
}
result.append("<<<< ExternalProcess");
return result.toString();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return result.toString();
} catch (IllegalThreadStateException e) {
return "ExternalProcess(RUNNING, "+cmd+")";
}
}
public int getExitValue() {
return exitValue;
}
}