Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/mike10004/subprocess-java
Fluent Java library for launching processes
https://github.com/mike10004/subprocess-java
Last synced: 10 days ago
JSON representation
Fluent Java library for launching processes
- Host: GitHub
- URL: https://github.com/mike10004/subprocess-java
- Owner: mike10004
- License: mit
- Created: 2018-11-09T15:34:19.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2021-04-26T18:39:28.000Z (over 3 years ago)
- Last Synced: 2024-10-29T14:13:19.779Z (2 months ago)
- Language: Java
- Size: 189 KB
- Stars: 2
- Watchers: 2
- Forks: 0
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# subprocess-java
Fluent Java library for launching processes by running executable binaries
outside the JVM.## Design goals
* use asynchronous patterns for process launch and termination
* support customization but provide sensible defaults
* cleanly separate of value classes and service classes/interfaces
* avoid dependencies to allow library users to do their own thing
* support Windows and Linux (and MacOS for the most part, but without testing)## Quick Start
Include the dependency with
com.github.mike10004
subprocess
0.4
and use
import io.github.mike10004.subprocess.*;
to import the classes. (Note that the `groupId` is `com.github.mike10004` but
the package starts with `io.github.mike10004`. I recognize that this is an
unfortunate inconsistency.)### Launch process and capture output
// parameters refer to type of captured stdout and stderr data
ProcessResult result;
try (ScopedProcessTracker processTracker = new ScopedProcessTracker()) {
result = Subprocess.running("echo")
.arg("hello, world")
.build()
.launcher(processTracker)
.outputStrings(Charset.defaultCharset())
.launch()
.await();
}
System.out.println(result.content().stdout()); // prints "hello, world"### Launch process and write output to file
ProcessResult result;
try (ScopedProcessTracker processTracker = new ScopedProcessTracker()) {
ProcessMonitor monitor = Subprocess.running("echo")
.arg("0123456789")
.build()
.launcher(processTracker)
.outputTempFiles(new File(System.getProperty("java.io.tmpdir")).toPath())
.launch();
result = monitor.await();
}
File stdoutFile = result.content().stdout();
System.out.format("%d bytes written to %s%n", stdoutFile.length(), stdoutFile);
stdoutFile.delete();
result.content().stderr().delete();### Feed standard input to process
StreamInput input = StreamInput.fromFile(new File("/etc/passwd"));
ProcessResult result;
try (ScopedProcessTracker processTracker = new ScopedProcessTracker()) {
ProcessMonitor monitor = Subprocess.running("grep")
.arg("root")
.build()
.launcher(processTracker)
.outputStrings(Charset.defaultCharset(), input)
.launch();
result = monitor.await();
}
System.out.println("grepped " + result.content().stdout()); // prints 'root' line from /etc/passwd### Terminate a process
try (ScopedProcessTracker processTracker = new ScopedProcessTracker()) {
ProcessMonitor monitor = Subprocess.running("cat")
.arg("-")
.build()
.launcher(processTracker)
.outputStrings(Charset.defaultCharset())
.launch();
System.out.println("process alive? " + monitor.process().isAlive());
SigtermAttempt attempt = monitor.destructor().sendTermSignal()
.await(3, TimeUnit.SECONDS);
System.out.println("process alive? " + monitor.process().isAlive());
if (monitor.process().isAlive()) {
attempt.kill().awaitOrThrow(3, TimeUnit.SECONDS);
}
}### Launch process and tail output
Consumer filter = line -> {
if (Integer.parseInt(line) % 2 == 0) {
System.out.print(line + " ");
}
};
ExecutorService executorService = Executors.newFixedThreadPool(2);
try (ScopedProcessTracker processTracker = new ScopedProcessTracker()) {
StreamTailer tailer = StreamTailer.builder(Charset.defaultCharset())
.stdoutConsumer(filter)
.build();
// launch a process that prints a number every second
ProcessMonitor monitor = Subprocess.running("bash")
.arg("-c")
.arg("set -e; for N in $(seq 5) ; do sleep 0.5 ; echo $N ; done")
.build()
.launcher(processTracker)
.tailing(executorService, tailer)
.launch();
monitor.await();
}
// prints: 2 4
executorService.shutdown();## Motivations
The other libraries I've used for process manipulation either do not offer
fine enough control over process execution or require too much boilerplate,
duplicative code to exercise fine control. For example, Apache Ant offers a
robust execution framework but one that doesn't support process termination.
The base Java `ProcessBuilder` API provides control over everything, but it
requires a lot of code to make it work how you want.Furthermore, I wanted an API that reflects the asynchronous nature of process
execution and termination. Execution is asynchronous by definition, as we're
launching a new thread in a separate process. Termination is also asynchronous
because you're sending a signal to a process and not getting a direct response.