Process output from apache-commons exec

后端 未结 4 1574
予麋鹿
予麋鹿 2020-12-10 10:11

I am at my wits end here. I\'m sure this is something simple and I most likely have huge holes in my understanding of java and streams. I think there are so many classes tha

相关标签:
4条回答
  • 2020-12-10 10:58

    What I don't get is why there is no need for a 'waitfor' style command here? Isn't it possible that there will be some time in which the buffer will be empty, exit the loop, and continue on while the process is still going? When I run it, this doesn't seem to be the case.

    readLine blocks. That is, your code will wait until a line has been read.

    PumpStreamHandler

    from Documentation

    Copies standard output and error of subprocesses to standard output and error of the parent process. If output or error stream are set to null, any feedback from that stream will be lost.

    0 讨论(0)
  • 2020-12-10 10:59

    Its a very old thread but I had to use Apache Commons Exec and had to solve the same problem. I trust with last version of Apache Commons Exec published in 2014, below solution works well both with and without watchdog;

    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 {
    }
    }
    

    Above class can be set as StreamHandler for the executor as below;

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

    Complete code is available here; https://github.com/raohammad/externalprocessfromjava

    0 讨论(0)
  • 2020-12-10 11:12

    Don't pass a ByteArrayOutputStream to the PumpStreamHandler, use an implementation of the abstract class org.apache.commons.exec.LogOutputStream. From the javadoc:

    The implementation parses the incoming data to construct a line and passes the complete line to an user-defined implementation.

    Thus the LogOutputStram is preprocessing the output to give you the control of handling individual lines instead of the raw bytes. Something like this:

    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;
        }
    }
    

    Then after the blocking call to exec.execute your getLines() will have the standard out and standard error you are looking for. The ExecutionResultHandler is optional from the perspective of just executing the process, and collecting all the stdOut/stdErr into a list of lines.

    0 讨论(0)
  • 2020-12-10 11:13

    Based on James A Wilson's answer I created the helper class "Execute". It wraps his answer into a solution that also supplies the exitValue for convenience.

    A single line is necessary to execute a command this way:

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

    The following Junit Testcase tests and shows how to use it:

    Junit4 test case:

    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);
         }
       }
    }
    

    Execute Java helper Class:

    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://stackoverflow.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;
        }
    
    }
    
    0 讨论(0)
提交回复
热议问题