package io.enmasse.systemtest.executor;

import java.io.*;
import java.util.ArrayList;
import java.util.concurrent.*;

/**
 * Class provide execution of external command
 */
public class Executor {
    private Process process;
    private String stdOut;
    private String stdErr;

    /**
     * Getter for stdOutput
     * @return string stdOut
     */
    public String getStdOut() {
        return stdOut;
    }

    /**
     * Getter for stdErrorOutput
     * @return string stdErr
     */
    public String getStdErr() {
        return stdErr;
    }

    /**
     * Method executes external command
     * @param commands arguments for command
     * @return returns true if command exit code is 0
     * @throws IOException
     * @throws InterruptedException
     * @throws ExecutionException
     */
    public boolean execute(ArrayList<String> commands) throws IOException, InterruptedException, ExecutionException {
        return execute(commands, 0);
    }

    /**
     * Method executes external command
     * @param commands arguments for command
     * @param timeout timeout in ms for kill
     * @return returns true if command exit code is 0
     * @throws IOException
     * @throws InterruptedException
     * @throws ExecutionException
     */
    public boolean execute(ArrayList<String> commands, int timeout) throws IOException, InterruptedException, ExecutionException{
        process = Runtime.getRuntime().exec((commands.toArray(new String[0])));

        Future<String> output = readStdOutput();
        Future<String> error = readStdError();

        int retCode = 1;
        if(timeout > 0){
            if (process.waitFor(timeout, TimeUnit.MILLISECONDS)){
                retCode = process.exitValue();
            }
        }else{
            retCode = process.waitFor();
        }

        stdOut = output.get();
        stdErr = error.get();

        return retCode == 0;
    }

    /**
     * Get standard output of execution
     * @return future string output
     */
    private Future<String> readStdOutput() {
        StreamGobbler errorGobbler = new
                StreamGobbler(process.getInputStream());
        return errorGobbler.read();
    }

    /**
     * Get standard error output of execution
     * @return future string error output
     */
    private Future<String> readStdError(){
        StreamGobbler errorGobbler = new
                StreamGobbler(process.getErrorStream());
        return errorGobbler.read();
    }

    /**
     * Class represent async reader
     */
    class StreamGobbler {
        private final ExecutorService executorService = Executors.newSingleThreadExecutor();
        private InputStream is;

        /**
         * Constructor of StreamGobbler
         * @param is input stream for reading
         */
        StreamGobbler(InputStream is) {
            this.is = is;
        }

        /**
         * read method
         * @return return future string of output
         */
        public Future<String> read() {
            Future<String> future = executorService.submit(() -> {
                InputStreamReader isr = new InputStreamReader(is);
                BufferedReader br = new BufferedReader(isr);
                String line;
                StringBuilder builder = new StringBuilder();
                while ( (line = br.readLine()) != null)
                    builder.append(line).append(System.getProperty("line.separator"));
                isr.close();
                br.close();
                return builder.toString();
            });

            return future;
        }
    }
}

@@ -273,16 +273,96 @@
  stdErr;
+%0A    private StreamGobbler stdOutReader;%0A    private StreamGobbler stdErrReader;
 %0A%0A    /*
@@ -1469,16 +1469,17 @@
 xception
+ 
 %7B%0A      
@@ -1809,24 +1809,86 @@
 xitValue();%0A
+            %7Delse%7B%0A                process.destroyForcibly();%0A
             
@@ -1969,59 +1969,361 @@
    s
-tdOut = output.get();%0A        stdErr = error.get();
+hutDownReaders();%0A        try %7B%0A            stdOut = output.get(500, TimeUnit.MILLISECONDS);%0A        %7Dcatch (TimeoutException ex)%7B%0A            stdOut = stdOutReader.getData();%0A        %7D%0A%0A        try %7B%0A            stdErr = error.get(500, TimeUnit.MILLISECONDS);%0A        %7Dcatch (TimeoutException ex)%7B%0A            stdErr = stdErrReader.getData();%0A        %7D
 %0A%0A  
@@ -2486,25 +2486,24 @@
 dStdOutput()
- 
 %7B%0A        St
@@ -2504,56 +2504,26 @@
     
-StreamGobbler errorGobbler = new%0A               
+stdOutReader = new
  Str
@@ -2571,34 +2571,34 @@
      return 
-errorGobbl
+stdOutRead
 er.read();%0A 
@@ -2762,56 +2762,26 @@
     
-StreamGobbler errorGobbler = new%0A               
+stdErrReader = new
  Str
@@ -2837,25 +2837,201 @@
 urn 
-errorGobbler.read
+stdErrReader.read();%0A    %7D%0A%0A    /**%0A     * Shutdown process output readers%0A     */%0A    private void shutDownReaders()%7B%0A        stdOutReader.shutDownReader();%0A        stdErrReader.shutDownReader
 ();%0A
@@ -3239,16 +3239,74 @@
 ream is;
+%0A        private StringBuilder data = new StringBuilder();
 %0A%0A      
@@ -3478,32 +3478,375 @@
  is;%0A        %7D%0A%0A
+        /**%0A         * Shutdown executor service with reader%0A         */%0A        public void shutDownReader()%7B%0A            executorService.shutdownNow();%0A        %7D%0A%0A        /**%0A         * Return data from stream sync%0A         * @return string of data%0A         */%0A        public String getData()%7B%0A            return data.toString();%0A        %7D%0A%0A
         /**%0A    
@@ -4193,69 +4193,8 @@
 ne;%0A
-                StringBuilder builder = new StringBuilder();%0A
     
@@ -4265,23 +4265,20 @@
         
-builder
+data
 .append(
@@ -4413,15 +4413,12 @@
 urn 
-builder
+data
 .toS

Fix timeout in executor of clients