Created
February 17, 2018 23:47
-
-
Save Exerosis/4cf77ddf428c26b29baff5a31fff2faf to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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