Процесс вывода из Apache-Commons Exec - PullRequest
26 голосов
/ 08 сентября 2011

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

Я только что узнал о существовании библиотеки Apache Commons (самообучающийся Java-сбой), и в настоящее время пытаюсь преобразовать часть моего Runtime.getRuntime (). Exec для использования commons - exec. Уже исправлено несколько раз в 6 месяцев, когда эта проблема возникает, а затем исчезает проблема стиля с exec.

Код выполняет сценарий perl и отображает стандартный вывод сценария в графическом интерфейсе во время его работы.

Код вызова находится внутри работника качания.

Я заблудился, как использовать pumpStreamHandler ... в любом случае вот старый код:

String pl_cmd = "perl script.pl"
Process p_pl = Runtime.getRuntime().exec( pl_cmd );

BufferedReader br_pl = new BufferedReader( new InputStreamReader( p_pl.getInputStream() ) );

stdout = br_pl.readLine();
while ( stdout != null )
{
    output.displayln( stdout );
    stdout = br_pl.readLine();
}

Полагаю, это то, что я получаю за копирование кода, который я не совсем давно понял. Выше я предполагаю, что выполняется процесс, затем захватывает выходной поток (через «getInputStream»?), Помещает его в буферизованный считыватель, а затем просто зацикливается там, пока буфер не опустеет.

Чего я не понимаю, так это почему здесь не нужна команда в стиле waitfor? Возможно ли, что будет некоторое время, когда буфер будет пуст, выйдет из цикла и продолжит работу, пока процесс еще продолжается? Когда я запускаю его, похоже, это не так.

В любом случае, я пытаюсь получить то же поведение с помощью commons exec, по сути, опять же из кода найденного Google:

DefaultExecuteResultHandler rh = new DefaultExecuteResultHandler();
ExecuteWatchdog wd  = new ExecuteWatchdog( ExecuteWatchdog.INFINITE_TIMEOUT );
Executor exec = new DefaultExecutor();

ByteArrayOutputStream out = new ByteArrayOutputStream();
PumpStreamHandler psh = new PumpStreamHandler( out );

exec.setStreamHandler( psh );
exec.setWatchdog( wd );

exec.execute(cmd, rh );
rh.waitFor();

Я пытаюсь выяснить, что делает pumpstreamhandler. Я предполагаю, что это возьмет выходные данные из объекта exec и заполнит OutputStream. Я предоставляю ему байты из stdout / err сценария perl?

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

------------ Будущее Редактировать ---------------------

Нашел это через гугл и тоже хорошо работает:

public static void main(String a[]) throws Exception
{
    ByteArrayOutputStream stdout = new ByteArrayOutputStream();
    PumpStreamHandler psh = new PumpStreamHandler(stdout);
    CommandLine cl = CommandLine.parse("ls -al");
    DefaultExecutor exec = new DefaultExecutor();
    exec.setStreamHandler(psh);
    exec.execute(cl);
    System.out.println(stdout.toString());
}

Ответы [ 4 ]

29 голосов
/ 06 сентября 2012

Не передавайте ByteArrayOutputStream в PumpStreamHandler, используйте реализацию абстрактного класса org.apache.commons.exec.LogOutputStream.Начиная с javadoc :

Реализация анализирует входящие данные для построения строки и передает полную строку пользовательской реализации.

Таким образом, LogOutputStram предварительно обрабатывает вывод, чтобы дать вам возможность управлять отдельными строками вместо необработанных байтов.Примерно так:

import java.util.LinkedList;
import java.util.List;
import org.apache.commons.exec.LogOutputStream;

public class CollectingLogOutputStream extends LogOutputStream {
    private final List<String> lines = new LinkedList<String>();
    @Override protected void processLine(String line, int level) {
        lines.add(line);
    }   
    public List<String> getLines() {
        return lines;
    }
}

Тогда после вызова блокировки на exec.execute ваш getLines() будет иметь стандартный выход и стандартную ошибку, которую вы ищете.ExecutionResultHandler является необязательным с точки зрения просто выполнения процесса и сбора всего stdOut / stdErr в список строк.

3 голосов
/ 08 сентября 2011

Чего я не понимаю, так это почему не нужна команда в стиле waitfor?Возможно ли, что будет некоторое время, когда буфер будет пуст, выйдет из цикла и продолжит работу, пока процесс еще продолжается?Когда я его запускаю, похоже, это не так.

блоки readLine.То есть ваш код будет ждать, пока строка не будет прочитана.

PumpStreamHandler

из Документация

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

2 голосов
/ 31 июля 2014

Основываясь на ответе Джеймса А. Уилсона, я создал вспомогательный класс «Выполнить». Это оборачивает его ответ в решение, которое также предоставляет exitValue для удобства.

Для выполнения команды необходимо выполнить одну строку:

ExecResult result=Execute.execCmd(cmd,expectedExitCode);

Следующие тесты Junit Testcase и показывает, как его использовать:

Тестовый пример Junit4:

package com.bitplan.newsletter;

import static org.junit.Assert.*;

import java.util.List;

import org.junit.Test;

import com.bitplan.cmd.Execute;
import com.bitplan.cmd.Execute.ExecResult;

/**
 * test case for the execute class
 * @author wf
 *
 */
public class TestExecute {
     @Test
   public void testExecute() throws Exception {
     String cmd="/bin/ls";
     ExecResult result = Execute.execCmd(cmd,0);
     assertEquals(0,result.getExitCode());
     List<String> lines = result.getLines();
     assertTrue(lines.size()>0);
     for (String line:lines) {
         System.out.println(line);
     }
   }
}

Класс Java для выполнения Java:

package com.bitplan.cmd;

import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;

import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.LogOutputStream;
import org.apache.commons.exec.PumpStreamHandler;

/**
 * Execute helper using apache commons exed
 *
 *  add this dependency to your pom.xml:
   <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-exec</artifactId>
            <version>1.2</version>
        </dependency>

 * @author wf
 *
 */
public class Execute {

    protected static java.util.logging.Logger LOGGER = java.util.logging.Logger
            .getLogger("com.bitplan.cmd");

    protected final static boolean debug=true;

    /**
     * LogOutputStream
     * /5825496/protsess-vyvoda-iz-apache-commons-exec
     * -apache-commons-exec
     * 
     * @author wf
     * 
     */
    public static class ExecResult extends LogOutputStream {
        private int exitCode;
        /**
         * @return the exitCode
         */
        public int getExitCode() {
            return exitCode;
        }

        /**
         * @param exitCode the exitCode to set
         */
        public void setExitCode(int exitCode) {
            this.exitCode = exitCode;
        }

        private final List<String> lines = new LinkedList<String>();

        @Override
        protected void processLine(String line, int level) {
            lines.add(line);
        }

        public List<String> getLines() {
            return lines;
        }
    }

    /**
     * execute the given command
     * @param cmd - the command 
     * @param exitValue - the expected exit Value
     * @return the output as lines and exit Code
     * @throws Exception
     */
    public static ExecResult execCmd(String cmd, int exitValue) throws Exception {
        if (debug)
            LOGGER.log(Level.INFO,"running "+cmd);
        CommandLine commandLine = CommandLine.parse(cmd);
        DefaultExecutor executor = new DefaultExecutor();
        executor.setExitValue(exitValue);
        ExecResult result =new ExecResult();
        executor.setStreamHandler(new PumpStreamHandler(result));
        result.setExitCode(executor.execute(commandLine));
        return result;
    }

}
0 голосов
/ 16 мая 2019

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

class CollectingLogOutputStream implements ExecuteStreamHandler {
private final List<String> lines = new LinkedList<String>();
public void setProcessInputStream(OutputStream outputStream) throws IOException 
{
}
//important - read all output line by line to track errors
public void setProcessErrorStream(InputStream inputStream) throws IOException {
    InputStreamReader isr = new InputStreamReader(inputStream);
    BufferedReader br = new BufferedReader(isr);
    String line="";
    while( (line = br.readLine()) != null){
        //use lines whereever you want - for now just print on console
        System.out.println("error:"+line);
    }
}
//important - read all output line by line to track process output
public void setProcessOutputStream(InputStream inputStream) throws IOException 
{
    InputStreamReader isr = new InputStreamReader(inputStream);
    BufferedReader br = new BufferedReader(isr);
    String line="";
    while( (line = br.readLine()) != null){
        //use lines whereever you want - for now just print on console
        System.out.println("output:"+line);
    }
  }

public void start() throws IOException {
}

public void stop() throws IOException {
}
}

Указанный выше класс может быть установлен как StreamHandler для исполнителя, как показано ниже;

//set newly created class stream handler for the executor
executor.setStreamHandler(new CollectingLogOutputStream());

Полный код доступен здесь; https://github.com/raohammad/externalprocessfromjava

...