2009-08-05 10 views
2

Lassen Sie mich zuerst sagen, dass meine Erfahrung mit Threading ziemlich niedrig ist.Java-Threading-Frage - Hören von n Fehler-Streams

Ich habe eine App, die mehrere andere Java-Gläser über die Runtime.exec Methode startet. Das Problem ist, dass die gestarteten JARs gleichzeitig ausgeführt werden müssen, aber um den Fehler-Stream für die gestarteten JARs zu erhalten, muss man im Grunde eine Schleife 'sitzen und zuhören' haben, bis der Prozess abgeschlossen ist.

Das ist, was ich habe jetzt:

_processes.add(Runtime.getRuntime().exec(commandList.toArray(new String[ commandList.size() ]))); 
Thread thread = new Thread(new Runnable() { 
    private final int _processNumber = _processes.size() - 1; 
    public void run() { 
     String streamData = _processNumber + " : "; 
     streamData += "StdError [\r"; 
     BufferedReader bufferedReader = 
       new BufferedReader(new InputStreamReader(_processes.get(_processNumber).getErrorStream())); 
     String line = null; 
     try { 
      while ((line = bufferedReader.readLine()) != null) { 
       streamData += line + "\r"; 
      } 
      bufferedReader.close(); 
      streamData += "]\r"; 
      LOG.error(streamData); 
     } 
     catch (Exception exception) { 
      LOG.fatal(exception.getMessage()); 
      exception.printStackTrace(); 
     } 
    } 
}); 
thread.start(); 

Kann mir jemand erklären, wie die ‚Fehlerstrom-Listener-Threads‘ zu bekommen richtig funktioniert?

TIA

Antwort

0

Es ist ein sehr gutes Tutorial here wie Runtime.exec() verwenden. Lesen Sie alles durch, aber schauen Sie sich insbesondere Seite 4 an, wo erklärt wird, wie Sie einen 'StreamGobbler' verwenden, der in einem separaten Thread ausgeführt wird, um sowohl den std out- als auch den std err-Stream des ausgeführten Prozesses zu verwenden.

Im Grunde, was sollten Sie suchen in pseduo Code zu implementieren ist:

Runtime rt1 = Runtime.getRuntime().exec("my command") 
new StreamGobbler(rt1.getOutputStream()).start() 
new StreamGobbler(rt1.getErrorStream()).start() 
//repeat for each external process (rt2, rt3 etc) 
... 
rt1.waitFor() 
rt2.waitFor() 
rt3.waitFor() 

D.h. Sie starten jeden Prozess und beginnen sofort, die Ausgabe jedes Prozesses in einem separaten Thread zu konsumieren. Warten Sie mit dem Start der konsumierenden Threads einfach, bis jeder Prozess beendet und zurückgegeben wurde. .

+0

Ja, ich habe das schon mal gelesen. Das Problem ist, dass rt1 einen hörenden Sockel öffnet und rt2 die Schreibseite des Sockels öffnet. Wenn ich 2 Threads starte, um sich von den Streams von rt1 zu ernähren, scheint der Eltern-Thread zu blockieren und rt2 startet nie. Ich habe versucht, alle Exec-Befehle auszuführen und dann alle Listener zu starten, und wenn ich dies tue, zwingt es die exec'ed-Befehle zu blockieren. – javamonkey79

1

Statt Runtime.getRuntime der Verwendung() exec(), verwendet Process externe Prozesse zu starten .. Es wird Ihr Leben viel einfacher ..

Beispiel-Code aus einem Projekt von mir machen:

//Build command 
    List<String> commands = new ArrayList<String>(); 
    commands.add("my_application"); 
    commands.add("arg1"); 
    commands.add("arg2"); 
    log.debug("{}", commands); 

    //Run command with arguments 
    ProcessBuilder pb = new ProcessBuilder(commands); 
    pb.directory(directory); 
    pb.redirectErrorStream(true); 
    Process process = pb.start(); 

    //Read output 
    StringBuilder out = new StringBuilder(); 
    BufferedReader br = new BufferedReader(new InputStreamReader 
     (process.getInputStream())); 

    //Only log unique lines (you might not need this) 
    String line = null, previous = null; 
    while ((line = br.readLine()) != null) 
     if (!line.equals(previous)) { 
      previous = line; 
      out.append(line).append('\n'); 
      log.debug(line); 
     } 

    //Check result 
    if (process.waitFor() == 0) 
     return 0; 

    //Abnormal termination: Log command parameters and output and throw ExecutionException 
    log.error("{}", commands); 
    log.error("\n{}", out.toString()); 
    throw new ExecutionException(new IllegalStateException("MyApplication exit code 1")); 
+0

Ich denke, die Parallelität der erzeugten Threads und ihrer entsprechenden Streams ist hier das Problem. Ich sehe nicht viel in ProcessBuilder (oder in ganz Java), die Prozesse viel besser behandelt. jconfig wird in dem oben genannten Artikel erwähnt, aber anscheinend ist das Projekt ziemlich tot (zumindest für jetzt). – javamonkey79

+0

Wäre PipedOut/InputStream hier hilfreich? – Tim