Вывод процесса из apache-commons exec
Я в своем уме остроумия здесь. Я уверен, что это что-то простое, и у меня, скорее всего, есть огромные пробелы в моем понимании 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? Возможно ли, что будет некоторое время, когда буфер будет пуст, выйдет из цикла и продолжит работу, пока процесс еще продолжается? Когда я запускаю его, это не так.
В любом случае, я пытаюсь получить то же самое поведение, используя обыкновенный 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());
}
3 ответа
Не пропустите ByteArrayOutputStream
к PumpStreamHandler
, используйте реализацию абстрактного класса org.apache.commons.exec.LogOutputStream
, Что-то вроде этого:
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 в список строк.
Чего я не понимаю, так это того, почему здесь не нужна команда в стиле waitfor? Возможно ли, что будет некоторое время, когда буфер будет пуст, выйдет из цикла и продолжит работу, пока процесс еще продолжается? Когда я запускаю его, это не так.
блоки readLine. То есть ваш код будет ждать, пока строка не будет прочитана.
PumpStreamHandler
из документации
Копирует стандартный вывод и ошибку подпроцессов в стандартный вывод и ошибку родительского процесса. Если выходные данные или поток ошибок установлены в нуль, любая обратная связь от этого потока будет потеряна.
Основываясь на ответе Джеймса Уилсона, я создал вспомогательный класс "Выполнить". Он оборачивает его ответ в решение, которое также предоставляет 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 вспомогательный класс:
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
* http://stackru.com/questions/7340452/process-output-from
* -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;
}
}
Это очень старый поток, но мне пришлось использовать 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