org.buildobjects.process.ProcBuilder Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jproc Show documentation
Show all versions of jproc Show documentation
Library for launching external processes and managing input and output.
package org.buildobjects.process;
import java.io.*;
import java.util.*;
import static java.util.Arrays.asList;
import static org.buildobjects.process.Helper.asSet;
/** A builder to construct a new process. The process gets configured by the withXXX-methods and
* spawned by the run() method*/
public class ProcBuilder {
private ByteArrayOutputStream defaultStdout = new ByteArrayOutputStream();
private String command;
private List args = new ArrayList();
private Map env = new HashMap<>();
private OutputStream stdout = defaultStdout;
private InputStream stdin;
private OutputStream stderr;
private Long timoutMillis = 5000L;
private Set expectedExitStatuses = new HashSet(){{add(0);}};
private File directory;
private StreamConsumer outputConsumer;
private StreamConsumer errorConsumer;
private boolean clearEnvironment;
/** Creates a new ProcBuilder
* @param command The command to run
* @param args The command line arguments
*/
public ProcBuilder(String command, String... args) {
this.command = command;
withArgs(args);
}
/**
* Adds another argument
* @param arg to add
* @return this, for chaining
* */
public ProcBuilder withArg(String arg) {
args.add(arg);
return this;
}
/** Redirecting the standard output. If it is not redirected the output gets captured in memory and
* is available on the @see ProcResult
*
* @param stdout stream to redirect the output to. \
* @return this, for chaining
* */
public ProcBuilder withOutputStream(OutputStream stdout) {
if (outputConsumer != null) {
throw new IllegalArgumentException("`withOutputStream(OutputStream)` and `withOutputConsumer(OutputConsumer)` " +
"are mutually exclusive.");
}
this.stdout = stdout;
return this;
}
/** Redirecting the error output. If it is not redirected the output gets captured in memory and
* is available on the @see ProcResult
*
* @param stderr stream to redirect the output to. \
* @return this, for chaining
* */
public ProcBuilder withErrorStream(OutputStream stderr) {
if (errorConsumer != null) {
throw new IllegalArgumentException("`withErrorStream(OutputStream)` and `withErrorConsumer(OutputConsumer)` " +
"are mutually exclusive.");
}
this.stderr = stderr;
return this;
}
/** Specify a timeout for the operation. If not specified the default is 5 seconds.
* @param timeoutMillis time that the process gets to run
* @return this, for chaining
* */
public ProcBuilder withTimeoutMillis(long timeoutMillis) {
this.timoutMillis = timeoutMillis;
return this;
}
/** Disable timeout for the operation.
*
* @return this, for chaining
* */
public ProcBuilder withNoTimeout() {
this.timoutMillis = null;
return this;
}
/** Take the input for the program from a given InputStream
* @param stdin stream to read the input from
* @return this, for chaining
*/
public ProcBuilder withInputStream(InputStream stdin) {
this.stdin = stdin;
return this;
}
/** Supply the input as string
* @param input the actual input
* @return this, for chaining
*/
public ProcBuilder withInput(String input) {
stdin = new ByteArrayInputStream(input.getBytes());
return this;
}
/** Supply the input as byte[]
* @param input the actual input
* @return this, for chaining
*/
public ProcBuilder withInput(byte[] input) {
stdin = new ByteArrayInputStream(input);
return this;
}
/** Override the wokring directory
* @param directory the working directory for the process
* @return this, for chaining
*/
public ProcBuilder withWorkingDirectory(File directory) {
if (!directory.isDirectory()) {
throw new IllegalArgumentException("File '" + directory.getPath() + "' is not a directory.");
}
this.directory = directory;
return this;
}
/** Add multiple args
* @param args the arguments add
* @return this, for chaining
*/
public ProcBuilder withArgs(String... args) {
this.args.addAll(asList(args));
return this;
}
/** Define the valid exit status codes for the command
*
* @param exitstatuses array containing the exit codes that are valid
* @return the ProcBuilder object; permits chaining.
* @author Mark Galbraith ([email protected])
*
* @deprecated Please use the variants with a set or vargs parameters*/
public ProcBuilder withExitStatuses(int[] exitstatuses) {
this.expectedExitStatuses = asSet(exitstatuses);
return this;
}
/** Define the valid exit status codes for the command
*
* @param expectedExitStatuses array containing the exit codes that are valid
* @return the ProcBuilder object; permits chaining.
* @author Mark Galbraith ([email protected])
*/
public ProcBuilder withExpectedExitStatuses(Set expectedExitStatuses) {
this.expectedExitStatuses = expectedExitStatuses;
return this;
}
/** Define the valid exit status codes for the command
* Convenience method taking varargs.
*
* @param expectedExitStatuses varargs parameter containing the exit codes that are valid
* @return the ProcBuilder object; permits chaining.
* @author Mark Galbraith ([email protected])
*/
public ProcBuilder withExpectedExitStatuses(int... expectedExitStatuses) {
this.expectedExitStatuses = asSet(expectedExitStatuses);
return this;
}
/** Ignore the error status returned from this command
*
* @return the ProcBuilder object; permits chaining.
*/
public ProcBuilder ignoreExitStatus() {
this.expectedExitStatuses = Collections.emptySet();
return this;
}
/** Spawn the actual execution.
* This will block until the process terminates.
* @return the result of the successful execution
*
* @throws StartupException if the process can't be started
* @throws TimeoutException if the timeout kicked in
* @throws ExternalProcessFailureException if the external process returned a non-null exit value*/
public ProcResult run() throws StartupException, TimeoutException, ExternalProcessFailureException {
if (stdout != defaultStdout && outputConsumer != null) {
throw new IllegalArgumentException("`withOutputStream(OutputStream)` and `withOutputConsumer(OutputConsumer)` " +
"are mutually exclusive.");
}
if (stderr != null && errorConsumer != null) {
throw new IllegalArgumentException("`withErrorStream(OutputStream)` and `withErrorConsumer(OutputConsumer)` " +
"are mutually exclusive.");
}
try {
Proc proc = new Proc(command, args, env, clearEnvironment, stdin, outputConsumer != null ? outputConsumer : stdout , directory, timoutMillis, errorConsumer != null ? errorConsumer : stderr);
final ByteArrayOutputStream output = defaultStdout == stdout && outputConsumer == null ? defaultStdout : null;
if (expectedExitStatuses.size() > 0 && !expectedExitStatuses.contains(proc.getExitValue())) {
throw new ExternalProcessFailureException(command, proc.toString(), proc.getExitValue(), proc.getErrorString(), output, proc.getExecutionTime());
}
return new ProcResult(proc.toString(), output, proc.getExitValue(), proc.getExecutionTime(), proc.getErrorBytes());
} finally {
stdout = defaultStdout = new ByteArrayOutputStream();
stdin = null;
}
}
/** Static helper to run a process
* @param cmd the command
* @param args the arguments
* @return the standard output
* @throws StartupException if the process can't be started
* @throws TimeoutException if the timeout kicked in
* @throws ExternalProcessFailureException if the external process returned a non-null exit value
* */
public static String run(String cmd, String... args) {
ProcBuilder builder= new ProcBuilder(cmd)
.withArgs(args);
return builder.run().getOutputString();
}
/** Static helper to filter a string through a process
* @param input the input to be fed into the process
* @param cmd the command
* @param args the arguments
* @return the standard output
* @throws StartupException if the process can't be started
* @throws TimeoutException if the timeout kicked in
* @throws ExternalProcessFailureException if the external process returned a non-null exit value
* */
public static String filter(String input, String cmd, String... args) {
ProcBuilder builder= new ProcBuilder(cmd)
.withArgs(args)
.withInput(input);
return builder.run().getOutputString();
}
/** Clears the environment before setting new variables. */
public ProcBuilder clearEnvironment() {
this.clearEnvironment = true;
return this;
}
/**
* Add a variable to the process's environment
*
* @param var variable name
* @param value the value to be passed in
* @return this, for chaining
* */
public ProcBuilder withVar(String var, String value) {
env.put(var, value);
return this;
}
/**
* Add multiple variables to the process's environment
*
* @param vars Map of variables to their respective values
* @return this, for chaining
*/
public ProcBuilder withVars(Map vars) {
env.putAll(vars);
return this;
}
/**
* Process the standard output with the given consumer object
*
* @param outputConsumer an object that defines how to process the standard output stream
* @return this, for chaining
*/
public ProcBuilder withOutputConsumer(StreamConsumer outputConsumer) {
if (stdout != defaultStdout) {
throw new IllegalArgumentException("`withOutputStream(OutputStream)` and `withOutputConsumer(OutputConsumer)` " +
"are mutually exclusive.");
}
this.outputConsumer = outputConsumer;
return this;
}
/**
* Process the error output with given consumer object
* @param errorConsumer an object that defines how to process the error output stream
* @return this, for chaining
*/
public ProcBuilder withErrorConsumer(StreamConsumer errorConsumer) {
if (stderr != null) {
throw new IllegalArgumentException("`withErrorStream(OutputStream)` and `withErrorConsumer(OutputConsumer)` " +
"are mutually exclusive.");
}
this.errorConsumer = errorConsumer;
return this;
}
/** @return a string representation of the process invocation.
*
* This approximates the representation of this invocation
* in a shell. Note that the escaping of arguments is incomplete,
* it works only for whitespace. Fancy control characters are
* not replaced.
*
* Also, this returns a representation of the current state of
* the builder. If more arguments are added the process this
* representation will not represent the process that gets launched.
*
* @deprecated Use getCommandLine instead.
*/
@Deprecated
public String getProcString() {
return Proc.formatCommandLine(command, args);
}
/** @return a string representation of the process invocation.
*
* This approximates the representation of this invocation
* in a shell. Note that the escaping of arguments is incomplete,
* it works only for whitespace. Fancy control characters are
* not replaced.
*
* Also, this returns a representation of the current state of
* the builder. If more arguments are added the process this
* representation will not represent the process that gets launched.
*/
public String getCommandLine() {
return Proc.formatCommandLine(command, args);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy