Запустите внешнюю программу из Java, прочитайте вывод, разрешите прерывание - PullRequest
15 голосов
/ 18 марта 2009

Я хочу запустить процесс из Java, прочитать его вывод и получить код возврата. Но пока он выполняется, я хочу иметь возможность отменить его. Я начинаю с запуска процесса:

ProcessBuilder pb = new ProcessBuilder(args);
pb.redirectErrorStream(true);
Process proc = pb.start();

Если я вызываю proc.waitFor (), я ничего не могу сделать, пока процесс не завершится. Итак, я предполагаю, что мне нужно что-то вроде этого:

while (true) {
  see if process has exited
  capture some output from the process
  decide if I want to cancel it, and if so, cancel it
  sleep for a while
}

Это правильно? Может кто-нибудь дать мне пример того, как это сделать на Java?

Ответы [ 5 ]

11 голосов
/ 18 марта 2009

Вот пример того, что я думаю, вы хотите сделать:

ProcessBuilder pb = new ProcessBuilder(args);
pb.redirectErrorStream(true);
Process proc = pb.start();

InputStream is = proc.getInputStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);

String line;
int exit = -1;

while ((line = br.readLine()) != null) {
    // Outputs your process execution
    System.out.println(line);
    try {
        exit = proc.exitValue();
        if (exit == 0)  {
            // Process finished
        }
    } catch (IllegalThreadStateException t) {
        // The process has not yet finished. 
        // Should we stop it?
        if (processMustStop())
            // processMustStop can return true 
            // after time out, for example.
            proc.destroy();
    }
}

Вы можете улучшить это :-) У меня нет реальной среды, чтобы проверить это сейчас, но вы можете найти больше информации здесь .

8 голосов
/ 25 октября 2009

Я рекомендую проверить Apache Commons Exec , чтобы избежать воссоздания колеса. У него есть несколько приятных функций, таких как выбор между синхронным и асинхронным выполнением, а также стандартное решение для запуска процесса watchdog , который может помочь в отсрочке выполнения в случае, если он застрянет.

5 голосов
/ 18 марта 2009

Вспомогательный класс, подобный этому, сработает:

public class ProcessWatcher implements Runnable {

    private Process p;
    private volatile boolean finished = false;

    public ProcessWatcher(Process p) {
        this.p = p;
        new Thread(this).start();
    }

    public boolean isFinished() {
        return finished;
    }

    public void run() {
        try {
            p.waitFor();
        } catch (Exception e) {}
        finished = true;
    }

}

Затем вы бы реализовали свой цикл в точности так, как описали:

Process p = Runtime.getRuntime().exec("whatever command");
ProcessWatcher pw = new ProcessWatcher(p);
InputStream output = p.getInputStream();
while(!pw.isFinished()) {
    processOutput(output);
    if(shouldCancel()) p.destroy();
    Thread.sleep(500);
}

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

EDIT : Макдауэлл на 100% прав в своем комментарии ниже, поэтому я сделал готовую переменную изменчивой.

2 голосов
/ 20 октября 2012

Как насчет этого (посмотрите, как это работает в jcabi-heroku-maven-plugin ):

/**
 * Wait for the process to stop, logging its output in parallel.
 * @param process The process to wait for
 * @return Stdout produced by the process
 * @throws InterruptedException If interrupted in between
 */
private String waitFor(final Process process) throws InterruptedException {
    final BufferedReader reader = new BufferedReader(
        new InputStreamReader(process.getInputStream())
    );
    final CountDownLatch done = new CountDownLatch(1);
    final StringBuffer stdout = new StringBuffer();
    new Thread(
        new VerboseRunnable(
            new Callable<Void>() {
                @Override
                public Void call() throws Exception {
                    while (true) {
                        final String line = reader.readLine();
                        if (line == null) {
                            break;
                        }
                        System.out.println(">> " + line);
                        stdout.append(line);
                    }
                    done.countDown();
                    return null;
                }
            },
            false
        )
    ).start();
    try {
        process.waitFor();
    } finally {
        done.await();
        IOUtils.closeQuietly(reader);
    }
    return stdout.toString();
}

пс. Теперь эта реализация доступна как com.jcabi.log.VerboseProcess класс из jcabi-log артефакт.

0 голосов
/ 18 марта 2009

Что заставило бы вас решить убить процесс - асинхронное событие (например, ввод от пользователя) или синхронное событие (например, процесс сделал то, что вы хотели, чтобы он делал)? Я предполагаю, что это первое - ввод от пользователя заставляет вас решить отменить подпроцесс.

Кроме того, какой объем вывода вы ожидаете для подпроцесса? Если это много, то подпроцесс может блокироваться, если вы недостаточно быстро читаете из его выходного потока.

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

Посмотрите здесь немного подробнее: http://java.sun.com/developer/JDCTechTips/2005/tt0727.html#2

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...