2012-10-22 27 views
10

Sto eseguendo un comando che mi restituisce il numero di revisione di un file; 'nome del file'. Ma se c'è qualche problema nell'esecuzione del comando, l'applicazione si blocca. Cosa posso fare per evitare quella condizione? Si prega di trovare sotto il mio codice.Runtime.getRuntime(). Exec (cmd) sospeso

+2

Dai un'occhiata a [ProcessBuilder] (http://docs.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html). Questa è un'API più semplice per fare questo genere di cose – RNJ

+0

Se non c'è output, 'readLine' bloccherà per sempre. – assylias

+0

@assylias: come faccio a controllare se non c'è uscita? – user1688404

risposta

24

Immagino che il problema sia che si sta solo leggendo InputStream e non si sta leggendo ErrorStream. Devi anche fare attenzione che entrambi i flussi siano letti in parallelo. Può accadere che attualmente i dati inviati dal flusso di output riempiano il buffer del sistema operativo, il comando exec verrà automaticamente sospeso per dare al lettore la possibilità di svuotare il buffer. Ma il programma sarà ancora in attesa dell'elaborazione dell'output. Quindi, si verifica il blocco.

È possibile creare una classe separata per gestire sia l'ingresso e di errore flusso come segue,

public class ReadStream implements Runnable { 
    String name; 
    InputStream is; 
    Thread thread;  
    public ReadStream(String name, InputStream is) { 
     this.name = name; 
     this.is = is; 
    }  
    public void start() { 
     thread = new Thread (this); 
     thread.start(); 
    }  
    public void run() { 
     try { 
      InputStreamReader isr = new InputStreamReader (is); 
      BufferedReader br = new BufferedReader (isr); 
      while (true) { 
       String s = br.readLine(); 
       if (s == null) break; 
       System.out.println ("[" + name + "] " + s); 
      } 
      is.close();  
     } catch (Exception ex) { 
      System.out.println ("Problem reading stream " + name + "... :" + ex); 
      ex.printStackTrace(); 
     } 
    } 
} 

Il modo in cui lo si utilizza è la seguente,

String cmd= "cmd /C si viewhistory --fields=revision --project="+fileName; 
Process p = Runtime.getRuntime().exec(cmd) ; 
s1 = new ReadStream("stdin", p.getInputStream()); 
s2 = new ReadStream("stderr", p.getErrorStream()); 
s1.start(); 
s2.start(); 
p.waitFor();   
} catch (Exception e) { 
e.printStackTrace(); 
} finally { 
    if(p != null) 
     p.destroy(); 
} 
+0

questo è perfetto almeno per me! grazie –

+1

Sì, quel blocco di codice mi ha anche salvato dall'essere ignorante hehe :) – Akyo

+0

Estremamente utile grazie! – welterw8

3

Questo codice è basato sul stessa idea la risposta di Arham, ma è implementata usando un flusso parallelo java 8, che lo rende un po 'più conciso.

public static String getOutputFromProgram(String program) throws IOException { 
    Process proc = Runtime.getRuntime().exec(program); 
    return Stream.of(proc.getErrorStream(), proc.getInputStream()).parallel().map((InputStream isForOutput) -> { 
     StringBuilder output = new StringBuilder(); 
     try (BufferedReader br = new BufferedReader(new InputStreamReader(isForOutput))) { 
      String line; 
      while ((line = br.readLine()) != null) { 
       output.append(line); 
       output.append("\n"); 
      } 
     } catch (IOException e) { 
      throw new RuntimeException(e); 
     } 
     return output; 
    }).collect(Collectors.joining()); 
} 

è possibile chiamare il metodo come questo

getOutputFromProgram("cmd /C si viewhistory --fields=revision --project="+fileName); 

Si noti che questo metodo si bloccherà se il programma che si sta chiamando si blocca, che accadrà se si richiede l'input.

+0

Funziona alla grande con 'mvn verify' e non lo sto avvolgendo con' cmd/C' - sai qual è il vantaggio del wrapping di una seconda shell? –

+1

Ho copiato esattamente la stringa di comando dell'OP e ha iniziato con "cmd/C". Non penso che ci sia alcun beneficio nel confezionarlo in questo caso. – mikeyreilly