ProcessBuilder receives an executable and individual arguments. It does not need a shell to pass a file path to a known command, and its Process result needs an owner.
Java ProcessBuilder: pass command tokens without a shell
Operational contract
Never construct a shell command string from a user-supplied manifest name when the job only needs to run a fixed executable. Select the executable from trusted configuration, keep each argument separate, and constrain which inputs the child may read. The sample starts the JDK's own version command and discards output so a full pipe cannot block it. Returning a Process transfers wait, timeout, and cleanup responsibility to the caller; the companion lesson owns that lifecycle.
Failure case
A depot startup check runs the installed Java executable with a fixed '-version' argument solely to confirm that the process starts and exits. This sample discards version text; a caller that needs the numeric version must capture a bounded stream and parse it separately. The command is not a facility for arbitrary user input. If the child fails to launch, the application records the I/O exception and never reports a successful check.
Java code
import java.io.IOException;
import java.nio.file.Path;
public class JavaRuntimeProbe {
public static Process start() throws IOException {
Path executable = Path.of(System.getProperty("java.home"), "bin", "java");
return new ProcessBuilder(executable.toString(), "-version")
.redirectOutput(ProcessBuilder.Redirect.DISCARD)
.redirectError(ProcessBuilder.Redirect.DISCARD)
.start();
}
}Performance and ownership cost
Launching a native process is far more expensive than a Java method call and consumes operating-system resources until reaped. The snippet allocates no output buffer because both streams are discarded. If output is required, drain it under a size limit rather than reading it only after waiting for exit.
Common Mistakes
- Do not concatenate untrusted arguments into a shell string.
- Do not ignore the returned Process after starting it.
- Do not wait for exit while leaving bounded output pipes undrained.
