package jdk.jpackage.internal;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
final public class Executor {
Executor() {
}
Executor setOutputConsumer(Consumer<Stream<String>> v) {
outputConsumer = v;
return this;
}
Executor saveOutput(boolean v) {
saveOutput = v;
return this;
}
Executor setWriteOutputToFile(boolean v) {
writeOutputToFile = v;
return this;
}
Executor setTimeout(long v) {
timeout = v;
if (timeout != INFINITE_TIMEOUT) {
setWriteOutputToFile(true);
}
return this;
}
Executor setProcessBuilder(ProcessBuilder v) {
pb = v;
return this;
}
Executor setCommandLine(String... cmdline) {
return setProcessBuilder(new ProcessBuilder(cmdline));
}
Executor setQuiet(boolean v) {
quietCommand = v;
return this;
}
List<String> getOutput() {
return output;
}
Executor executeExpectSuccess() throws IOException {
int ret = execute();
if (0 != ret) {
throw new IOException(
String.format("Command %s exited with %d code",
createLogMessage(pb, false), ret));
}
return this;
}
int execute() throws IOException {
output = null;
boolean needProcessOutput = outputConsumer != null || Log.isVerbose() || saveOutput;
Path outputFile = null;
if (needProcessOutput) {
pb.redirectErrorStream(true);
if (writeOutputToFile) {
outputFile = Files.createTempFile("jpackageOutputTempFile", ".tmp");
pb.redirectOutput(outputFile.toFile());
}
} else {
pb.redirectError(ProcessBuilder.Redirect.DISCARD);
pb.redirectOutput(ProcessBuilder.Redirect.DISCARD);
}
Log.verbose(String.format("Running %s", createLogMessage(pb, true)));
Process p = pb.start();
int code = 0;
if (writeOutputToFile) {
try {
code = waitForProcess(p);
} catch (InterruptedException ex) {
Log.verbose(ex);
throw new RuntimeException(ex);
}
}
if (needProcessOutput) {
final List<String> savedOutput;
Supplier<Stream<String>> outputStream;
if (writeOutputToFile) {
output = savedOutput = Files.readAllLines(outputFile);
Files.delete(outputFile);
outputStream = () -> {
if (savedOutput != null) {
return savedOutput.stream();
}
return null;
};
if (outputConsumer != null) {
outputConsumer.accept(outputStream.get());
}
} else {
try (var br = new BufferedReader(new InputStreamReader(
p.getInputStream()))) {
if ((outputConsumer != null || Log.isVerbose())
|| saveOutput) {
savedOutput = br.lines().collect(Collectors.toList());
} else {
savedOutput = null;
}
output = savedOutput;
outputStream = () -> {
if (savedOutput != null) {
return savedOutput.stream();
}
return br.lines();
};
if (outputConsumer != null) {
outputConsumer.accept(outputStream.get());
}
if (savedOutput == null) {
br.lines().forEach(x -> {});
}
}
}
}
try {
if (!writeOutputToFile) {
code = p.waitFor();
}
if (!quietCommand) {
Log.verbose(pb.command(), getOutput(), code);
}
return code;
} catch (InterruptedException ex) {
Log.verbose(ex);
throw new RuntimeException(ex);
}
}
private int waitForProcess(Process p) throws InterruptedException {
if (timeout == INFINITE_TIMEOUT) {
return p.waitFor();
} else {
if (p.waitFor(timeout, TimeUnit.SECONDS)) {
return p.exitValue();
} else {
Log.verbose(String.format("Command %s timeout after %d seconds",
createLogMessage(pb, false), timeout));
p.destroy();
return -1;
}
}
}
static Executor of(String... cmdline) {
return new Executor().setCommandLine(cmdline);
}
static Executor of(ProcessBuilder pb) {
return new Executor().setProcessBuilder(pb);
}
private static String createLogMessage(ProcessBuilder pb, boolean quiet) {
StringBuilder sb = new StringBuilder();
sb.append((quiet) ? pb.command().get(0) : pb.command());
if (pb.directory() != null) {
sb.append(String.format(" in %s", pb.directory().getAbsolutePath()));
}
return sb.toString();
}
public final static int INFINITE_TIMEOUT = -1;
private ProcessBuilder pb;
private boolean saveOutput;
private boolean writeOutputToFile;
private boolean quietCommand;
private long timeout = INFINITE_TIMEOUT;
private List<String> output;
private Consumer<Stream<String>> outputConsumer;
}