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.