ProcessRunner Class — spring-boot Architecture
Architecture documentation for the ProcessRunner class in ProcessRunner.java from the spring-boot codebase.
Entity Profile
Source Code
core/spring-boot-docker-compose/src/main/java/org/springframework/boot/docker/compose/core/ProcessRunner.java lines 43–184
class ProcessRunner {
private static final String USR_LOCAL_BIN = "/usr/local/bin";
private static final boolean MAC_OS = System.getProperty("os.name").toLowerCase(Locale.ROOT).contains("mac");
private static final Log logger = LogFactory.getLog(ProcessRunner.class);
private final @Nullable File workingDirectory;
/**
* Create a new {@link ProcessRunner} instance.
*/
ProcessRunner() {
this(null);
}
/**
* Create a new {@link ProcessRunner} instance.
* @param workingDirectory the working directory for the process
*/
ProcessRunner(@Nullable File workingDirectory) {
this.workingDirectory = workingDirectory;
}
/**
* Runs the given {@code command}. If the process exits with an error code other than
* zero, an {@link ProcessExitException} will be thrown.
* @param command the command to run
* @return the output of the command
* @throws ProcessExitException if execution failed
*/
String run(String... command) {
return run(null, command);
}
/**
* Runs the given {@code command}. If the process exits with an error code other than
* zero, an {@link ProcessExitException} will be thrown.
* @param outputConsumer consumer used to accept output one line at a time
* @param command the command to run
* @return the output of the command
* @throws ProcessExitException if execution failed
*/
String run(@Nullable Consumer<String> outputConsumer, String... command) {
logger.trace(LogMessage.of(() -> "Running '%s'".formatted(String.join(" ", command))));
Process process = startProcess(command);
ReaderThread stdOutReader = new ReaderThread(process.getInputStream(), "stdout", outputConsumer);
ReaderThread stdErrReader = new ReaderThread(process.getErrorStream(), "stderr", outputConsumer);
logger.trace("Waiting for process exit");
int exitCode = waitForProcess(process);
logger.trace(LogMessage.format("Process exited with exit code %d", exitCode));
String stdOut = stdOutReader.toString();
String stdErr = stdErrReader.toString();
if (exitCode != 0) {
throw new ProcessExitException(exitCode, command, stdOut, stdErr);
}
return stdOut;
}
private Process startProcess(String[] command) {
ProcessBuilder processBuilder = new ProcessBuilder(command);
processBuilder.directory(this.workingDirectory);
try {
return processBuilder.start();
}
catch (IOException ex) {
String path = processBuilder.environment().get("PATH");
if (MAC_OS && path != null && !path.contains(USR_LOCAL_BIN)
&& !command[0].startsWith(USR_LOCAL_BIN + "/")) {
String[] localCommand = command.clone();
localCommand[0] = USR_LOCAL_BIN + "/" + localCommand[0];
return startProcess(localCommand);
}
throw new ProcessStartException(command, ex);
}
}
private int waitForProcess(Process process) {
try {
return process.waitFor();
}
catch (InterruptedException ex) {
throw new IllegalStateException("Interrupted waiting for %s".formatted(process));
}
}
/**
* Thread used to read stream input from the process.
*/
private static class ReaderThread extends Thread {
private final InputStream source;
private final @Nullable Consumer<String> outputConsumer;
private final StringBuilder output = new StringBuilder();
private final CountDownLatch latch = new CountDownLatch(1);
ReaderThread(InputStream source, String name, @Nullable Consumer<String> outputConsumer) {
this.source = source;
this.outputConsumer = outputConsumer;
setName("OutputReader-" + name);
setDaemon(true);
start();
}
@Override
public void run() {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(this.source, StandardCharsets.UTF_8))) {
String line = reader.readLine();
while (line != null) {
this.output.append(line);
this.output.append("\n");
if (this.outputConsumer != null) {
this.outputConsumer.accept(line);
}
line = reader.readLine();
}
this.latch.countDown();
}
catch (IOException ex) {
throw new UncheckedIOException("Failed to read process stream", ex);
}
}
@Override
public String toString() {
try {
this.latch.await();
return this.output.toString();
}
catch (InterruptedException ex) {
return "";
}
}
}
}
Source
Analyze Your Own Codebase
Get architecture documentation, dependency graphs, and domain analysis for your codebase in minutes.
Try Supermodel Free