Skip to content

Instantly share code, notes, and snippets.

@Exerosis
Created February 17, 2018 23:47
Show Gist options
  • Save Exerosis/4cf77ddf428c26b29baff5a31fff2faf to your computer and use it in GitHub Desktop.
Save Exerosis/4cf77ddf428c26b29baff5a31fff2faf to your computer and use it in GitHub Desktop.
public class CommandLine implements Closeable {
private static final String RETURN = "\n";
private static final byte[] RETURN_DATA = RETURN.getBytes();
private final ExecutorService executor;
private final int buffer;
private final Process process;
private Closeable out, err;
interface Builder {
interface Buffer<Return> extends Output<Return> {
Return __build(OutputStream err,
OutputStream out,
boolean redirect,
Number buffer) throws IOException;
@Override
default Return __build(OutputStream err,
OutputStream out,
boolean redirect) throws IOException {
return __build(err, out, redirect, 1024);
}
default Output<Return> buffer(Number buffer) {
return (err, out, redirect) -> __build(err, out, redirect, buffer);
}
}
interface Output<Return> extends Errors<Return> {
Return __build(OutputStream err,
OutputStream out,
boolean redirect) throws IOException;
@Override
default Return __build(OutputStream err, boolean redirect) throws IOException {
return __build(err, null, redirect);
}
default Errors.Redirect<Return> pipeOutput(OutputStream out) {
return (err, redirect) -> __build(err, out, redirect);
}
}
interface Errors<Return> {
Return __build(OutputStream err,
boolean redirect) throws IOException;
default Return pipeErrors(OutputStream err) throws IOException {
return __build(err, false);
}
default Return ignoreErrors() throws IOException {
return __build(null, true);
}
interface Redirect<Return> extends Errors<Return> {
default Return redirectErrors() throws IOException {
return __build(null, true);
}
@Override
default Return ignoreErrors() throws IOException {
return __build(null, false);
}
}
}
}
public static Builder.Buffer<CommandLine> create(String command) {
return (err, out, redirect, buffer) ->
new CommandLine(command, err, out, redirect, buffer.intValue());
}
private CommandLine(String command,
OutputStream err,
OutputStream out,
boolean redirect,
int buffer) throws IOException {
this.buffer = buffer;
executor = newFixedThreadPool(redirect ? 1 : 2);
process = new ProcessBuilder(command)
.redirectErrorStream(redirect)
.start();
if (out != null)
this.out = pipe(process.getInputStream(), out);
if (err != null)
this.err = pipe(process.getErrorStream(), err);
}
public ExecutorService getExecutor() {
return executor;
}
public Process getProcess() {
return process;
}
public CommandLine execute(String... commands) {
return execute(() -> new Iterator<String>() {
int index = 0;
@Override
public boolean hasNext() {
return index < commands.length;
}
@Override
public String next() {
return commands[index++];
}
});
}
public CommandLine execute(Iterable<String> commands) {
try {
for (String line : commands) {
process.getOutputStream().write(line.getBytes());
if (!line.endsWith(RETURN))
process.getOutputStream().write(RETURN_DATA);
process.getOutputStream().flush();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
return this;
}
private void drain(InputStream in) throws IOException {
for (int $ = 0; $ != -1; $ = in.read()) ;
}
private Closeable pipe(InputStream in, OutputStream out) {
final Future<?> task = executor.submit(() -> {
final byte[] buffer = new byte[this.buffer];
for (int length = 0; ; length = in.read(buffer)) {
out.write(buffer, 0, length);
out.flush();
}
});
return () -> task.cancel(true);
}
@Override
public void close() throws IOException {
process.destroy();
if (out != null)
out.close();
if (err != null)
err.close();
executor.shutdown();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment