Lesen des streams (sdtout) eines externen Prozesses, der eine Exception wirft

DarthShader

Erfahrenes Mitglied
Hallo zusammen,

ich möchte den standard output stream (sdtout) eines anderen (konsolen-) Programmes auslesen. Der folgende Code zeigt, wie ich das bisher (erfolgreich) mache:

Java:
// Start the process
ProcessBuilder builder = new ProcessBuilder( "foo.exe", "-list", "/home/user/" );
builder.directory( new File( workingDir ) );
Process process = builder.start();
			
// Get the process' input stream and create buffered reader
InputStream in = process.getInputStream(); 
BufferedReader br = new BufferedReader( new InputStreamReader( in ) );
			
// Read from the stream
String line;
StringBuilder sb = new StringBuilder();
while ( ( line = br.readLine() ) != null )
  sb.append( line );

Es funktioniert, solange das andere Programm ("foo.exe") keine Exception wirft. Ist dem so, so stoppt mein Java-Programm beim Versuch, von dem Stream zu lesen.

Meine Vermutung ist, dass das externe Programm den sdtout Stream nicht korrekt schließt, und deshalb "wartet" die Anweisung "line = br.readLine()" ewig auf Daten.

Hat jemand eine Idee, wie man das korrekt macht bzw. wie ich es umsetzen könnte, dass das Lesen vom Stream nicht hängt, wenn das externe Programm abstürzt/eine Exception wirft?


Über eine Antwort würde ich mich sehr freuen


Vielen Dank für Eure Hilfe!
 
Moin,

ist das andere Programm auch von Dir?
Dann solltest Du dort die Exception sauber abfangen und behandeln!

Andernfalls wird es vermutlich schwierig,weil Du ja nicht darauf reagieren kannst, wenn das andere Programm hängt, außer, dass Du nur eine gewisse Zeit auf den Input wartest und dann den Leseversuch beendest!

Gruß
Klaus
 
Hallo Klaus,

vielen Dank für Deine Antwort. Da gibt es wohl wirklich keine schöne Lösung, wenn die Gegenseite den Stream nicht schließt.

Mein Workaround sieht momentan so aus, dass ich zunächst den stderr Stream abfrage und lese - kommt da nix, dann lese ich vom stdin des externen Programmes. Wirft das externe Programm eine Exception, so kommt auch was auf dem stderr und ich brauche das Lesen vom stdin gar nicht erst anfangen.

Mal ganz abgesehen von der Problematik, hast du natürlich recht, dass die Exception vernünftig behandelt werden muss.
 
Hi.

Wenn das aufgerufene Programm beendet wird (ob nun durch eine Exception oder was auch immer), wird der stdout und stderr des Programms automatisch geschlossen, denn das Programm gibt es ja nicht mehr.

Ist also die Frage was dort wirklich passiert wenn dein Programm eine Exception wirft. Es könnte damit zusammenhängen, das es nicht beendet werden kann da der Ausgabepuffer für den
stderr Stream voll ist und das Programm noch dabei ist den Stacktrace dortrein zu schreiben (was auch erklären würde warum deine Methode zuerst den stderr Stream zu lesen erfolgreich ist).

Du müßtest also z.B. in einem eigenen Thread den ErrorStream auslesen um zu verhindern dass es zu einer solchen Situation kommt.

Gruß
 
Hm, ich muss etwas zurückrudern - das mit dem stderr zuerst funktioniert auch nicht, merkwürdig warum ich das beim ersten mal nicht bemerkt habe. Kommt auf dem stderr nichts, also das Programm wirft keine Exception, so bleibt er auch beim Versuch stehen, vom stderr zu lessen - ich habe also nichts gewonnen.

Ich frage mich deshalb, was da wirklich passiert - Deine Vermutung, deepthroat, klingt ja schonmal nicht schlecht.

Also ich stehe gerade auf dem Schlauch. Wie muss man denn vorgehen, um den stdout sowie auch den stderr von einem externen Prozess zu bekommen? Muss ich hier wirklich mit Threads und Timeouts arbeiten? Das würde ich gerne vermeiden, wenn möglich.
 
Hm, ich muss etwas zurückrudern - das mit dem stderr zuerst funktioniert auch nicht, merkwürdig warum ich das beim ersten mal nicht bemerkt habe. Kommt auf dem stderr nichts, also das Programm wirft keine Exception, so bleibt er auch beim Versuch stehen, vom stderr zu lessen - ich habe also nichts gewonnen.
Das Problem ist natürlich das du nicht wissen kannst welcher Puffer zuerst voll sein wird.

Probier's mal so:
Java:
		final BufferedReader berr = 
	new BufferedReader(new InputStreamReader(process.getErrorStream()));

new Thread() {
	public void run() {
		try {
			while (berr.readLine() != null) ;
		} catch (java.io.IOException ex) {
		}
	}
}.start();
Danach kannst du ganz normal die Standardausagebe vom Prozess lesen und zum Schluß den exitValue prüfen.

Gruß
 
Ich bin mir noch nicht ganz sicher, wie das zu laufen hat.

Soll ich für beide Streams (normal und err) jeweils einen Thread aufmachen? Und wo genau ist da dann der "Timeout"? Muss, wenn der eine Thread was liest, der den anderen dann abbrechen? Aber theoretisch könnte doch auf beiden Streams etwas kommen.

Ich hab keine Ahnung, wie ich das sauber implementieren soll :-/
 
Ich bin mir noch nicht ganz sicher, wie das zu laufen hat.

Soll ich für beide Streams (normal und err) jeweils einen Thread aufmachen?
Nein. Wozu? Du hast doch bereits einen Thread.
Und wo genau ist da dann der "Timeout"?
Welcher Timeout? Wozu?
Muss, wenn der eine Thread was liest, der den anderen dann abbrechen?
Wozu? Die 2 Streams sind getrennte Resourcen. Da muss man nichts synchronisieren.

Gruß
 
Nein. Wozu? Du hast doch bereits einen Thread.

Ich dachte, ich müsste für stdin sowie stderr zwei deparate Threads machen - wahrscheinlich habe ich Deinen Lösungsansatz gar nicht richtig verstanden.

Welcher Timeout? Wozu?

Der Timeout, der den Thread beendet, wenn das Lesen ewig wartet. Wenn also bei einem Stream vom externen Programm nix kommt, so wartet "readLine()" doch - und wann soll dann der Thread beendet werden? Ich dachte gerade deshalb braucht man einen Timeout, damit, wenn nach einer bestimmten Zeit tatsächlich nix mehr kommt, man den Thread beendet bzw. das Programm fortführt.

Wozu? Die 2 Streams sind getrennte Resourcen. Da muss man nichts synchronisieren.

Auch hier dachte ich an das "ewige warten" dass auf dem Stream was kommt. Beispiel: Thread 1 für den normalen Stream wartet, Thread 2 mit dem error stream liest Daten. Jetzt weiß der Thread 2 ja, dass auf dem normalen Thread des ersten Threads nix mehr kommen wird, und kann dem ersten Thread sagen, dass er beendet werden kann.
 
Der Timeout, der den Thread beendet, wenn das Lesen ewig wartet. Wenn also bei einem Stream vom externen Programm nix kommt, so wartet "readLine()" doch - und wann soll dann der Thread beendet werden? Ich dachte gerade deshalb braucht man einen Timeout, damit, wenn nach einer bestimmten Zeit tatsächlich nix mehr kommt, man den Thread beendet bzw. das Programm fortführt.
Wenn du erwartest das dein aufgerufenes Programm nie beendet wird, und auch nichts auf stderr bzw. stdout schreibt, dann brauchst du deinen Timeout.

Gruß
 
Zurück