Die Integration externer Programme in Java-Anwendungen ist eine häufige Notwendigkeit, sei es für die Automatisierung von Aufgaben, die Interaktion mit Systemressourcen oder die Kommunikation mit anderen Anwendungen. Die ProcessBuilder
-Klasse in Java bietet eine flexible Möglichkeit, externe Prozesse zu starten und mit ihnen zu interagieren. In diesem Artikel werden wir den Umgang mit ProcessBuilder
sowie verwandten Konzepten wie Umgebungsvariablen, Kommandozeilenparametern, stdin/stdout und möglichen Herausforderungen beim Lesen von Prozessausgaben besprechen.
1. Prozesse starten mit ProcessBuilder
Die ProcessBuilder
-Klasse in Java ermöglicht es, externe Prozesse zu starten und mit ihnen zu interagieren. Um einen einfachen Prozess zu starten, können Sie ProcessBuilder
wie folgt verwenden:
import java.io.IOException;
public class ExternalProcessExample {
public static void main(String[] args) {
try {
ProcessBuilder processBuilder = new ProcessBuilder("command", "arg1", "arg2");
Process process = processBuilder.start();
// Warten, bis der Prozess beendet ist
int exitCode = process.waitFor();
System.out.println("Prozess beendet mit Exit-Code: " + exitCode);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
Code-Sprache: JavaScript (javascript)
Hier wird ein externer Prozess mit einem Befehl („command“) und optionalen Argumenten („arg1“, „arg2“) gestartet. Der waitFor
-Aufruf blockiert, bis der gestartete Prozess beendet ist, und gibt den Exit-Code des Prozesses zurück.
2. Umgebungsvariablen und Kommandozeilenparameter setzen
Oftmals ist es erforderlich, Umgebungsvariablen oder Kommandozeilenparameter für den gestarteten Prozess festzulegen. Dies kann über die environment()
-Methode von ProcessBuilder
erfolgen:
import java.io.IOException;
public class ProcessWithEnvironment {
public static void main(String[] args) {
try {
ProcessBuilder processBuilder = new ProcessBuilder("command", "arg1", "arg2");
// Umgebungsvariablen setzen
processBuilder.environment().put("KEY", "VALUE");
Process process = processBuilder.start();
int exitCode = process.waitFor();
System.out.println("Prozess beendet mit Exit-Code: " + exitCode);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
Code-Sprache: JavaScript (javascript)
3. stdin und stdout steuern
Die ProcessBuilder
-Klasse ermöglicht auch die Steuerung von stdin und das Abfangen von stdout des gestarteten Prozesses. Dazu können Sie getOutputStream()
für die Eingabe und getInputStream()
für die Ausgabe verwenden:
import java.io.*;
public class ProcessWithInputOutput {
public static void main(String[] args) {
try {
ProcessBuilder processBuilder = new ProcessBuilder("command");
// stdin des Prozesses abrufen
OutputStream stdin = processBuilder.start().getOutputStream();
// Daten in stdin schreiben
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdin));
writer.write("Input-Daten");
writer.close();
// stdout des Prozesses abrufen
InputStream stdout = processBuilder.start().getInputStream();
// Daten von stdout lesen
BufferedReader reader = new BufferedReader(new InputStreamReader(stdout));
String output = reader.readLine();
System.out.println("Prozessausgabe: " + output);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Code-Sprache: JavaScript (javascript)
4. Verwendung der StreamGobbler-Hilfsklasse
Um stdout und stderr des gestarteten Prozesses effizienter zu verarbeiten, ist es ratsam, eine Hilfsklasse wie StreamGobbler
zu verwenden. Diese Klasse liest die Ausgaben des Prozesses in einem separaten Thread und verhindert, dass die Puffer des Betriebssystems überlaufen.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class StreamGobbler extends Thread {
private InputStream inputStream;
private Consumer<String> consumer;
public StreamGobbler(InputStream inputStream, Consumer<String> consumer) {
this.inputStream = inputStream;
this.consumer = consumer;
}
@Override
public void run() {
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
reader.lines().forEach(consumer);
} catch (IOException e) {
e.printStackTrace();
}
}
}
Code-Sprache: JavaScript (javascript)
Die StreamGobbler
-Klasse erfasst den InputStream des Prozesses und leitet die Ausgaben an den angegebenen Consumer weiter. Dadurch können Sie die Ausgaben des Prozesses nach Bedarf verarbeiten, z.B. sie in einer Datei speichern oder analysieren.
5. Warnung vor Blockaden bei stdout
Beim Lesen von stdout ist es wichtig, die Ausgaben des Prozesses kontinuierlich zu verarbeiten, um zu verhindern, dass die Puffer des Betriebssystems überlaufen und der Prozess blockiert. Dies kann erreicht werden, indem Sie den StreamGobbler
oder einen ähnlichen Mechanismus verwenden, um die Ausgaben parallel zum Hauptprogramm zu verarbeiten – oder komplett zu ignorieren (denn die Hauptsache ist, dass der Stream ausgelesen wird)!
Insgesamt bietet die ProcessBuilder
-Klasse in Java eine leistungsstarke Möglichkeit, mit externen Prozessen zu interagieren. Mit den oben genannten Konzepten können Sie nicht nur Prozesse starten, sondern auch Umgebungsvariablen setzen, Kommandozeilenparameter übergeben und die stdin/stdout-Streams effizient steuern. Die Verwendung von Hilfsklassen wie StreamGobbler
erleichtert zudem die Handhabung von Prozessausgaben und minimiert das Risiko von Blockaden durch volle Puffer.