Runtime.exec(). WaitFor() на самом деле не ждет

У меня есть код, который использует Runtime.exec() для запуска внешнего .jar(созданного как установщик IzPack).

Если я запустил этот файл external.jar из командной строки следующим образом:

java -jar external.jar

Затем командная строка не возвращает управление, пока приложение не будет завершено. Однако, если я запускаю external.jar из некоторого java-класса, используя:

Process p = Runtime.getRuntime().exec("java -jar external.jar");
int exitCode = p.waitFor();
System.out.println("Process p returned: " + exitCode);

Затем p возвращается почти мгновенно с кодом успеха 0, несмотря на то, что external.jar еще не завершил выполнение (я также пробовал это с помощью трассировки внешнего файла ProcessBuilder).

Почему он ждет возврата из командной строки, но не при выполнении из другой Java-программы?

Я также установил 3 баночки, A, B и C, где A вызывает B, который вызывает C (используя Runtime.exec()), где C Thread.sleep в течение 10 секунд, как простой тест, и, как ожидалось, A не возвращается до 10 секунд после его запуска.

Я полагаю, что это, вероятно, какая-то проблема с threading с внешним .jar, где исполнение передается от одной вещи к другой, но учитывая, что она работает непосредственно из командной строки, я ожидаю увидеть такое же поведение ( возможно наивно) при вызове из другой Java-программы.

Я тестировал это на Windows и Ubuntu с Java 6.

Спасибо!

Ответ 1

Другим возможным способом достижения этого может быть захват вывода процесса и ожидание его завершения.

Например:

Process tr = Runtime.getRuntime().exec( new String[]{"wkhtmltopdf",mainPage,mainPagePDF});
BufferedReader stdOut=new BufferedReader(new InputStreamReader(tr.getInputStream()));
String s;
while((s=stdOut.readLine())!=null){
       //nothing or print
}

Обычно выходной поток представляет собой tr.getInputStream(), но в зависимости от программы, которую вы выполняете, поток потока процесса:

  • tr.getInputStream()
  • tr.getErrorStream()
  • tr.getOutputStream()

Выполняя этот цикл while, вы заставляете свою программу ждать завершения процесса.

Ответ 2

Вы можете использовать Process Builder....

ProcessBuilder pb = new ProcessBuilder("java", "-jar", "/fielname.jar");
Process p = pb.start();
p.waitFor();

Ответ 3

Вы создаете новый поток для обработки нереста процесса? Если так, то исходная программа будет продолжать работать независимо от порожденного процесса, и поэтому waitFor() будет работать только с новым процессом, а не с родителем.

Ответ 4

Process.waitFor() бесполезен для какой-либо собственной системной команды.

Вам нужно получить выход процесса, чтобы определить, возвращается ли он.

Я написал образец кода для вас

/**
 * 
 * @param cmdarray      command and parameter of System call
 * @param dir           the directory execute system call
 * @param returnImmediately   true indicate return after system call immediately; 
     *                          false otherwise.
 *  if set true, the returned call result does not have reference value
 * @return the return code of system call , default is -1
 */
public static int systemCall(String[] cmdarray,File dir,boolean returnImmediately)
{
  int result = -1;
  try {
   Process p = Runtime.getRuntime().exec(cmdarray,null,dir);
  if(!returnImmediately)
  {
   java.io.InputStream stdin = p.getInputStream();
   java.io.InputStreamReader isr = new java.io.InputStreamReader(stdin);
   java.io.BufferedReader br = new java.io.BufferedReader(isr);
   String line = null;
   while ( (line = br.readLine()) != null)
      System.out.println(line);
      }
      try{result =  p.exitValue();}
        catch(Exception ie){;}
      } catch (IOException e) {
        e.printStackTrace();}

  return result;
}

public static void main(String[] argc){             
  String[] cmdarray = {"jar","cvf","s2.jar","*"};
  File dir = new File("D:\\src\\struts-2.3.1");
  int k = systemCall(cmdarray,dir,true);
  System.out.println("k="+k);
 }

Ответ 5

У меня была такая же проблема с использованием процессов, чтобы выполнить какое-то программное обеспечение с помощью консоли, и я просто решил его с помощью process.waitFor()

Для меня это сработало отлично.

    try{
        Process tr = Runtime.getRuntime().exec( new String[]{ "wkhtmltopdf",frontPage,frontPagePDF});
        tr.waitFor();
    } catch (Exception ex) {
        EverLogger.logEntry("Error al pasar a PDF la portada", "error", "activity");
        return;
    } 
some more code here.