Commits

Conor MacNeill committed 1adef99

Extract process utils to a separate library - remove scmutils dependency

Comments (0)

Files changed (18)

processutils/pom.xml

+<project>
+    <parent>
+        <groupId>com.atlassian.pom</groupId>
+        <artifactId>atlassian-public-pom</artifactId>
+        <version>23</version>
+    </parent>
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>com.atlassian.utils</groupId>
+    <artifactId>atlassian-processutils</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <dependencies>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>1.4</version>
+        </dependency>
+        <dependency>
+            <groupId>log4j</groupId>
+            <artifactId>log4j</artifactId>
+            <version>1.2.15</version>
+            <exclusions>
+                <exclusion>
+                    <groupId>com.sun.jdmk</groupId>
+                    <artifactId>jmxtools</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>com.sun.jmx</groupId>
+                    <artifactId>jmxri</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>javax.jms</groupId>
+                    <artifactId>jms</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+    </dependencies>
+</project>

processutils/src/main/java/com/atlassian/utils/process/BaseInputHandler.java

+package com.atlassian.utils.process;
+
+import com.atlassian.utils.process.InputHandler;
+import com.atlassian.utils.process.Watchdog;
+
+/**
+ */
+public abstract class BaseInputHandler implements InputHandler {
+    private Watchdog watchdog;
+
+    public void complete() {
+        // do nothing
+    }
+
+    public void setWatchdog(Watchdog watchdog) {
+        this.watchdog = watchdog;
+    }
+
+    public void cancelProcess() {
+        watchdog.cancel();
+    }
+
+    protected void resetWatchdog() {
+        watchdog.resetWatchdog();
+    }
+}

processutils/src/main/java/com/atlassian/utils/process/BaseOutputHandler.java

+package com.atlassian.utils.process;
+
+/**
+ */
+public abstract class BaseOutputHandler implements OutputHandler {
+    private Watchdog watchdog;
+
+    public void setWatchdog(Watchdog watchdog) {
+        this.watchdog = watchdog;
+    }
+
+    protected void resetWatchdog() {
+        if (watchdog != null) {
+            watchdog.resetWatchdog();
+        }
+    }
+
+    public void cancelProcess() {
+        watchdog.cancel();
+    }
+
+    public void complete() {
+    }
+}

processutils/src/main/java/com/atlassian/utils/process/BaseProcessHandler.java

+package com.atlassian.utils.process;
+
+/**
+ */
+public abstract class BaseProcessHandler implements ProcessHandler {
+    private Watchdog watchdog;
+
+    public void setWatchdog(Watchdog watchdog) {
+        this.watchdog = watchdog;
+    }
+
+    public Watchdog getWatchdog() {
+        return watchdog;
+    }
+}

processutils/src/main/java/com/atlassian/utils/process/CopyOutputHandler.java

+package com.atlassian.utils.process;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.InterruptedIOException;
+
+/**
+ * An Output Handler which copies the process output to a give output stream
+ */
+public class CopyOutputHandler extends BaseOutputHandler {
+    private final OutputStream dest;
+
+    /**
+     * Create a CopyOutputHandler to redirect output from the process to the given stream
+     *
+     * @param dest the stream to which output is to be written
+     */
+    public CopyOutputHandler(OutputStream dest) {
+        this.dest = dest;
+    }
+
+    public void process(InputStream output) throws ProcessException {
+        try {
+            byte buffer[] = new byte[1024];
+            int num;
+            while ((num = output.read(buffer)) != -1) {
+                resetWatchdog();
+                dest.write(buffer, 0, num);
+            }
+        } catch (InterruptedIOException e) {
+            // This means the process was asked to stop which can be normal so we just finish
+        } catch (IOException e) {
+            throw new ProcessException(e);
+        }
+    }
+}

processutils/src/main/java/com/atlassian/utils/process/ExternalProcess.java

+package com.atlassian.utils.process;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * This class manages the execution of an external process, using separate threads to process
+ * the process' IO requirements.
+ */
+public class ExternalProcess implements Watchdog {
+    private String[] cmdArray;
+    private File workingDir;
+    private String[] environment;
+    private ProcessHandler handler;
+    private Process process;
+
+    private ProcessException processException;
+
+    private LatchedRunnable outputPump;
+    private LatchedRunnable errorPump;
+    private LatchedRunnable inputPump;
+
+    private static final ExecutorService pumpThreadPool;
+
+    private long lastWatchdogReset;
+    private long timeout = 60000L;
+    private boolean cancelled;
+
+    public void resetWatchdog() {
+        lastWatchdogReset = System.currentTimeMillis();
+    }
+
+    public long getTimeoutTime() {
+        return lastWatchdogReset + timeout;
+    }
+
+    public boolean isTimedOut() {
+        return getTimeoutTime() < System.currentTimeMillis();
+    }
+
+    static {
+        ThreadFactory threadFactory = new ThreadFactory() {
+            public Thread newThread(Runnable r) {
+                return new Thread(r, "ExtProcess IO Pump");
+            }
+        };
+        pumpThreadPool = new ThreadPoolExecutor(6, Integer.MAX_VALUE, 120, TimeUnit.SECONDS,
+                                                new SynchronousQueue<Runnable>(), threadFactory);
+    }
+
+    /**
+     * Process an external command.
+     * @param cmdArray the command and its arguments as separate elements
+     * @param handler The handler for this execution. The handler supports the required IO
+     *                operations
+     */
+    public ExternalProcess(String[] cmdArray, ProcessHandler handler) {
+        setCommand(cmdArray);
+        setHandler(handler);
+    }
+
+    /**
+     * Process an external command (the command and arguments are given as a list)
+     * @param command A list containing the command and its arguments
+     * @param handler The process handler to manage the execution of this process.
+     */
+    public ExternalProcess(List<String> command, ProcessHandler handler) {
+        setCommand(command.toArray(new String[command.size()]));
+        setHandler(handler);
+    }
+
+    /**
+     * Process an external command. The command is given as a single command line and parsed into
+     * the command and its arguments. Spaces are used as argument delimiters so if any command arguments
+     * need to contain spaces, the array or list based constructors should be used.
+     *
+     * @param commandLine the command and its arguments in a single line. If any arguments
+     *                    need to contain spaces, the array or list based constructors should be used.
+     * @param handler The handler for this execution. The handler supports the required IO
+     *                operations
+     */
+    public ExternalProcess(String commandLine, ProcessHandler handler) {
+        String[] cmdArray = ProcessUtils.tokenizeCommand(commandLine);
+        setCommand(cmdArray);
+        setHandler(handler);
+    }
+
+    private void setHandler(ProcessHandler handler) {
+        this.handler = handler;
+    }
+
+    private void setCommand(String[] cmdArray) {
+        this.cmdArray = cmdArray;
+    }
+
+    public void setWorkingDir(File workingDir) {
+        this.workingDir = workingDir;
+    }
+
+    public void setEnvironment(String[] environment) {
+        this.environment = environment;
+    }
+
+    private boolean arePumpsRunning() {
+        return outputPump.isRunning() || errorPump.isRunning()
+                || (inputPump != null && inputPump.isRunning());
+    }
+
+    /**
+     * Get the process handler for this process execution
+     *
+     * @return the ProcessHandler instance associated with this process execution.
+     */
+    public ProcessHandler getHandler() {
+        return handler;
+    }
+
+    /**
+     * Start the external process and setup the IO pump threads needed to
+     * manage the process IO. If you call this method you must eventually call the
+     * finish() method. Using this method you may execute additional code between process
+     * start and finish.
+     */
+    public void start() {
+        try {
+            this.process = Runtime.getRuntime().exec(cmdArray, environment, workingDir);
+            setupIOPumps();
+        } catch (IOException e) {
+            processException = new ProcessException(e);
+        }
+    }
+
+    private void setupIOPumps() {
+        // set up threads to feed data to and extract data from the process
+        if (handler.hasInput()) {
+            inputPump = new LatchedRunnable() {
+                protected void doTask() {
+                    handler.provideInput(process.getOutputStream());
+                }
+            };
+        }
+
+        errorPump = new LatchedRunnable() {
+            protected void doTask() {
+                try {
+                    handler.processError(process.getErrorStream());
+                } catch (Throwable e) {
+                    if (!isCancelled()) {
+                        processException = new ProcessException(e);
+                    }
+                }
+            }
+        };
+
+        outputPump = new LatchedRunnable() {
+            protected void doTask() {
+                try {
+                    handler.processOutput(process.getInputStream());
+                } catch (Throwable e) {
+                    if (!isCancelled()) {
+                        processException = new ProcessException(e);
+                    }
+                }
+            }
+        };
+
+        // tickle the dog initially
+        resetWatchdog();
+        handler.setWatchdog(this);
+
+        pumpThreadPool.execute(errorPump);
+        pumpThreadPool.execute(outputPump);
+        if (inputPump != null) {
+            pumpThreadPool.execute(inputPump);
+        }
+    }
+
+    /**
+     * Finish process execution. This method should be called after you have called the
+     * start() method.
+     */
+    public void finish() {
+        if (process != null) {
+            try {
+                do {
+                    long checkTime = getTimeoutTime();
+                    awaitPump(outputPump, checkTime);
+                    awaitPump(inputPump, checkTime);
+                    awaitPump(errorPump, checkTime);
+                } while (!isTimedOut() && arePumpsRunning());
+            } finally {
+                int exitCode = 0;
+                if (!cancelled) {
+                    exitCode = wrapUpProcess();
+                }
+                handler.complete(exitCode, processException);
+            }
+        } else {
+            handler.complete(-1, processException);
+        }
+    }
+
+    /**
+     * Execute the external command. When this method returns, the process handler
+     * provided at construction time should be consulted to collect exit code, exceptions,
+     * process output, etc.
+     */
+    public void execute() {
+        start();
+        finish();
+    }
+
+    /**
+     * Executes the external command. While it is running, the given runnable is executed.
+     * The external command is not checked until the runnable completes
+     *
+     * @param runnable A task to perform while the external command is running.
+     */
+    public void executeWhile(Runnable runnable) {
+        start();
+        if (runnable != null) {
+            runnable.run();
+        }
+        finish();
+    }
+
+    public String getCommandLine() {
+        StringBuilder sb = new StringBuilder();
+        for (String s : cmdArray) {
+            sb.append(s);
+            sb.append(" ");
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Wait a given time for the process to finish
+     *
+     * @param maxWait the maximum amount of time in milliseconds to wait for the process to finish
+     *
+     * @return true if the process has finished.
+     */
+    public boolean finish(int maxWait) {
+        if (process != null) {
+            boolean finished = false;
+            try {
+                long endTime = System.currentTimeMillis() + maxWait;
+                awaitPump(outputPump, endTime);
+                awaitPump(inputPump, endTime);
+                awaitPump(errorPump, endTime);
+            } finally {
+                if (!arePumpsRunning()) {
+                    // process finished
+                    finished = true;
+                    int exitCode = wrapUpProcess();
+                    handler.complete(exitCode, processException);
+                }
+            }
+            return finished;
+        } else {
+            handler.complete(-1, processException);
+            return true;
+        }
+    }
+
+    private int wrapUpProcess() {
+        int exitCode = -1;
+        boolean processIncomplete = true;
+        try {
+            exitCode = process.exitValue();
+            processIncomplete = false;
+        } catch (IllegalThreadStateException e) {
+            // process still running - could be a race to have the process finish so wait a little to be sure
+            while (processIncomplete && System.currentTimeMillis() - getTimeoutTime() < 10) {
+                // we are currently before the end of the period (within 10ms slack), so process probably not ready yet
+                try {
+                    Thread.sleep(100);
+                    exitCode = process.exitValue();
+                    processIncomplete = false;
+                } catch (InterruptedException e1) {
+                    // just ignore
+                } catch (IllegalThreadStateException e2) {
+                    // ignore and try in the next loop
+                }
+            }
+        } finally {
+            process.destroy();
+        }
+
+        // make sure pumps are done
+        if (arePumpsRunning()) {
+            cancel();
+        }
+
+        if (processIncomplete) {
+            processException = new ProcessException("Command failed to complete");
+        }
+        return exitCode;
+    }
+
+    private void awaitPump(LatchedRunnable runnable, long latestTime) {
+        if (runnable != null) {
+            long timeout = latestTime - System.currentTimeMillis();
+            if (timeout < 1) {
+                timeout = 1;
+            }
+            runnable.await(timeout);
+        }
+    }
+
+    /**
+     * Cancel should be called if you wish to interrupt process execution.
+     */
+    public void cancel() {
+        this.cancelled = true;
+        if (outputPump != null) {
+            outputPump.cancel();
+        }
+
+        if (inputPump != null) {
+            inputPump.cancel();
+        }
+
+        if (errorPump != null) {
+            errorPump.cancel();
+        }
+        process.destroy();
+    }
+
+    public void setTimeout(long timeout) {
+        this.timeout = timeout;
+    }
+}

processutils/src/main/java/com/atlassian/utils/process/InputHandler.java

+package com.atlassian.utils.process;
+
+import java.io.OutputStream;
+
+/**
+ * A Handler for process input
+ *
+ * The InputHandler interface is designed to allow different input provision strategies to be plugged into
+ * process handlers
+ */
+public interface InputHandler {
+    void process(OutputStream input);
+    void complete();
+
+    void setWatchdog(Watchdog watchdog);
+}

processutils/src/main/java/com/atlassian/utils/process/LatchedRunnable.java

+package com.atlassian.utils.process;
+
+import org.apache.log4j.Logger;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Date: 16/07/2008
+ * Time: 14:54:03
+ */
+public abstract class LatchedRunnable implements Runnable {
+    private static Logger log = Logger.getLogger(LatchedRunnable.class);
+    private CountDownLatch latch = new CountDownLatch(1);
+    private Thread runner;
+    private boolean cancelled;
+
+    public final void run() {
+        try {
+            runner = Thread.currentThread();
+            doTask();
+        } finally {
+            latch.countDown();
+        }
+    }
+
+    public boolean await(long millis) {
+        try {
+            return latch.await(millis, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            log.warn("Interrupted waiting for ExternalProcess pump to complete");
+            throw new RuntimeException("Interrupted waiting for Process pump", e);
+        }
+    }
+
+    public boolean isRunning() {
+        return latch.getCount() > 0;
+    }
+
+    protected abstract void doTask();
+
+
+    public void interrupt() {
+        if (runner != null) {
+            runner.interrupt();
+        }
+    }
+
+    public void cancel() {
+        this.cancelled = true;
+    }
+
+    public boolean isCancelled() {
+        return cancelled;
+    }
+}

processutils/src/main/java/com/atlassian/utils/process/LineOutputHandler.java

+package com.atlassian.utils.process;
+
+import org.apache.commons.io.IOUtils;
+
+import java.io.InputStream;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+
+/**
+ * An Output Handler which breaks the output from the process into
+ * discrete lines and processes each line in turn. Subclasses provide the
+ * appropriate implementation of the per-line processing method.
+ */
+public abstract class LineOutputHandler extends BaseOutputHandler {
+    private final String encoding;
+
+    protected LineOutputHandler() {
+        this("UTF-8");
+    }
+
+    protected LineOutputHandler(String encoding) {
+        this.encoding = encoding;
+    }
+
+    public void process(InputStream output) throws ProcessException {
+        BufferedReader reader = null;
+        int counter = 0;
+        try {
+            if (encoding == null) {
+                reader = new BufferedReader(new InputStreamReader(output));
+            } else {
+                reader = new BufferedReader(new InputStreamReader(output, encoding));
+            }
+            String line;
+            while ((line = reader.readLine()) != null) {
+                resetWatchdog();
+                processLine(counter++, line);
+            }
+            processInputEnd(counter);
+        } catch (InterruptedIOException e) {
+            // This means the process was asked to stop which can be normal so we just finish
+            processEndByException(counter);
+        } catch (IOException e) {
+            processEndByException(counter);
+            throw new ProcessException(e);
+        } finally {
+            IOUtils.closeQuietly(reader);
+        }
+    }
+
+    protected void processEndByException(int counter) {
+        // do nothing by default
+    }
+
+    /**
+     * Input has finished
+     *
+     * @param lineCount total number of lines processed
+     */
+    protected void processInputEnd(int lineCount) throws ProcessException {
+        // do nothing by default
+    }
+
+    /**
+     * Process the given line
+     *
+     * @param lineNum The line number of the line being processed
+     * @param line the content of the line
+     */
+    protected abstract void processLine(int lineNum, String line);
+
+}

processutils/src/main/java/com/atlassian/utils/process/OutputHandler.java

+package com.atlassian.utils.process;
+
+import java.io.InputStream;
+
+/**
+ * A Handler for process output
+ *
+ * The OutputHandler interface is designed to allow different output handling strategies to be plugged into
+ * process handlers
+ */
+public interface OutputHandler {
+    /**
+     * Process an output stream generated by the external process (either stdout or stderr)
+     *
+     * @param output the external process' output stream (available as an input to this class)
+     * @throws ProcessException if there is a problem processing the output
+     */
+    void process(InputStream output) throws ProcessException;
+
+    /**
+     * Called when the process completes. This call allows the output handler to close any
+     * open resources and finalize any processing.
+     */
+    void complete();
+
+    /**
+     * Set the watchdog that this handler should be resetting to prevent the process from being terminated.
+     * The watchdog should be called periodically based on output generated by the process.
+     *
+     * @param watchdog process watchdog instance.
+     */
+    void setWatchdog(Watchdog watchdog);
+}

processutils/src/main/java/com/atlassian/utils/process/PluggableProcessHandler.java

+package com.atlassian.utils.process;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * An implementation of a ProcessHandler where the processors for the process' IO streams can
+ * be plugged in.
+ *
+ */
+public class PluggableProcessHandler extends BaseProcessHandler {
+    private int exitCode;
+    private ProcessException exception;
+
+    private OutputHandler outputHandler;
+    private OutputHandler errorHandler;
+    private InputHandler inputHandler;
+
+    private boolean complete;
+
+    public PluggableProcessHandler() {
+    }
+
+    public void processOutput(InputStream processOutput) throws ProcessException {
+        if (outputHandler == null) {
+            throw new IllegalStateException("Process output received with no handler");
+        }
+        outputHandler.setWatchdog(getWatchdog());
+        outputHandler.process(processOutput);
+    }
+
+    public void processError(InputStream processError) throws ProcessException {
+        if (errorHandler == null) {
+            throw new IllegalStateException("Process error output received with no handler");
+        }
+        errorHandler.setWatchdog(getWatchdog());
+        errorHandler.process(processError);
+    }
+
+    public boolean hasInput() {
+        return inputHandler != null;
+    }
+
+    public void provideInput(OutputStream processInput) {
+        if (!hasInput()) {
+            throw new IllegalStateException("Attempt to read input without an input handler");
+        }
+        inputHandler.setWatchdog(getWatchdog());
+        inputHandler.process(processInput);
+    }
+
+    public void complete(int exitCode, ProcessException exception) {
+        this.exitCode = exitCode;
+        this.exception = exception;
+        if (outputHandler != null) {
+            outputHandler.complete();
+        }
+        if (errorHandler != null) {
+            errorHandler.complete();
+        }
+        if (inputHandler != null) {
+            inputHandler.complete();
+        }
+
+        if (exception == null && exitCode != 0) {
+            this.exception = new ProcessException("Non-zero exit code: " + exitCode, exitCode);
+        }
+        complete = true;
+    }
+
+    public int getExitCode() {
+        return exitCode;
+    }
+
+    public ProcessException getException() {
+        return exception;
+    }
+
+    public String getError() {
+        return null;
+    }
+
+    public void setOutputHandler(OutputHandler outputHandler) {
+        this.outputHandler = outputHandler;
+    }
+
+    public void setErrorHandler(OutputHandler errorHandler) {
+        this.errorHandler = errorHandler;
+    }
+
+    public void setInputHandler(InputHandler inputHandler) {
+        this.inputHandler = inputHandler;
+    }
+
+    public OutputHandler getOutputHandler() {
+        return outputHandler;
+    }
+
+    public OutputHandler getErrorHandler() {
+        return errorHandler;
+    }
+
+    public InputHandler getInputHandler() {
+        return inputHandler;
+    }
+
+    public boolean succeeded() {
+        return exception == null;
+    }
+
+    public void reset() {
+        exitCode = 0;
+        exception = null;
+        complete = false;
+    }
+
+    public boolean isComplete() {
+        return complete;
+    }
+}

processutils/src/main/java/com/atlassian/utils/process/ProcessException.java

+package com.atlassian.utils.process;
+
+/**
+ * Exceptions thrown while running external processes
+ */
+public class ProcessException extends Exception {
+    int exitCode;
+
+    public ProcessException(String message, Throwable cause) {
+        super(message, cause);
+        setExitCode(cause);
+    }
+
+    private void setExitCode(Throwable cause) {
+        // Trawl the whole chain looking for a process exception - copy it's exit code.
+        while (cause != null) {
+            if (cause instanceof ProcessException) {
+                exitCode = ((ProcessException) cause).getExitCode();
+                return;
+            }
+            cause = cause.getCause();
+        }
+    }
+
+    public ProcessException(String message) {
+        super(message);
+    }
+
+    public ProcessException(Throwable e) {
+        super(e);
+        setExitCode(e);
+    }
+
+    public ProcessException(String message, int exitCode) {
+        super(message);
+        this.exitCode = exitCode;
+    }
+
+    public int getExitCode() {
+        return exitCode;
+    }
+}

processutils/src/main/java/com/atlassian/utils/process/ProcessHandler.java

+package com.atlassian.utils.process;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Interface used to manage the IO and termination needs of an external processes
+ * managed by ExternalProcess.  The methods of this interface will be called by
+ * different threads, so implementors should take care to ensure thread safety
+ *
+ * @see com.atlassian.utils.process.ExternalProcess
+ */
+public interface ProcessHandler {
+    /**
+     * Process the process stdout stream
+     *
+     * @param output the external process' output stream (available as an input to this class)
+     * @throws ProcessException if there is a problem processing the output
+     */
+    void processOutput(InputStream output) throws ProcessException;
+
+    /**
+     * Process the process stderr stream
+     *
+     * @param error the external process' standard error stream (available as an input to this class)
+     * @throws ProcessException if there is a problem processing the output
+     */
+    void processError(InputStream error) throws ProcessException;
+
+    /**
+     * Indicate if this handler has input to provide to the process
+     *
+     * @return true if input is available
+     */
+    boolean hasInput();
+
+    /**
+     * Provide input to the external process. Input is provided by writing the content to the
+     * given output stream. This method will only be called if hasInput() returns true
+     *
+     * @param input the output stream representing standard input to the external process
+     * @throws IllegalStateException if no input has been configured.
+     */
+    void provideInput(OutputStream input);
+
+    /**
+     * Called when the external process has completed
+     *
+     * @param exitCode the exit code of the external process
+     * @param exception any process exceptions that were thrown within the VM when handling the
+     *        external process
+     */
+    void complete(int exitCode, ProcessException exception);
+
+    /**
+     * Indicate if the process has completed
+     *
+     * @return true if complete has been called.
+     */
+    boolean isComplete();
+
+    /**
+     * Set the watchdog associated with this handler. The watchdog should be called at regular intervals
+     * to prevent the external process being terminated. Typically this is done in the IO handling methods
+     *
+     * @return the handler's watchdog instance
+     */
+    void setWatchdog(Watchdog watchdog);
+
+    /**
+     * Indicate if the process execution has been considered successful.
+     *
+     * @return true if the process execution completed without error
+     */
+    boolean succeeded();
+
+    /**
+     * Called if the process is to be re-executed.
+     */
+    void reset();
+
+    /**
+     * Get any processing exception associated with this handler
+     * @return a processing exception instance or null if no exception occurred.
+     */
+    ProcessException getException();
+
+    /**
+     * Get the process exit code
+     * @return process exit code
+     */
+    int getExitCode();
+}
+

processutils/src/main/java/com/atlassian/utils/process/ProcessUtils.java

+package com.atlassian.utils.process;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ */
+public class ProcessUtils {
+    private ProcessUtils() {
+    }
+
+    public static String[] tokenizeCommand(String commandLine) {
+        return commandLine.split("[ \\t\\n\\r\\f]+");
+    }
+
+    public static List<String> tokenizeAsList(String commandLine) {
+        return new ArrayList<String>(Arrays.asList(tokenizeCommand(commandLine)));
+    }
+}

processutils/src/main/java/com/atlassian/utils/process/StringInputHandler.java

+package com.atlassian.utils.process;
+
+import org.apache.commons.io.IOUtils;
+
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.IOException;
+
+/**
+ * An input handler which provides the input from the given string.
+ */
+public class StringInputHandler implements InputHandler {
+    private String encoding;
+    private String content;
+
+    public StringInputHandler(String content) {
+        this(null, content);
+    }
+
+    public StringInputHandler(String encoding, String content) {
+        this.encoding = encoding;
+        this.content = content;
+    }
+
+    public void process(OutputStream input) {
+        OutputStreamWriter writer = null;
+        try {
+            if (encoding == null) {
+                writer = new OutputStreamWriter(input);
+            } else {
+                writer = new OutputStreamWriter(input, encoding);
+            }
+            writer.write(content);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        } finally {
+            IOUtils.closeQuietly(writer);
+        }
+    }
+
+    public void complete() {
+        // nothing to do here
+    }
+
+    public void setWatchdog(Watchdog watchdog) {
+    }
+}

processutils/src/main/java/com/atlassian/utils/process/StringOutputHandler.java

+package com.atlassian.utils.process;
+
+import org.apache.commons.io.IOUtils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.InterruptedIOException;
+import java.io.StringWriter;
+
+/**
+ * An Output Handler which captures the output stream to a string
+ */
+public class StringOutputHandler extends BaseOutputHandler {
+    private final String encoding;
+    private final StringWriter sw = new StringWriter();
+
+    public StringOutputHandler() {
+        this(null);
+    }
+
+    public StringOutputHandler(String encoding) {
+        this.encoding = encoding;
+    }
+
+    public void process(InputStream output) throws ProcessException {
+        InputStreamReader reader;
+        try {
+            if (encoding == null) {
+                reader = new InputStreamReader(output);
+            } else {
+                reader = new InputStreamReader(output, encoding);
+            }
+
+            char buffer[] = new char[1024];
+            int num;
+            while ((num = reader.read(buffer)) != -1) {
+                resetWatchdog();
+                sw.write(buffer, 0, num);
+            }
+        } catch (InterruptedIOException e) {
+            // This means the process was asked to stop which can be normal so we just finish
+        } catch (IOException e) {
+            throw new ProcessException(e);
+        }
+    }
+
+    public String getOutput() {
+        return sw.toString();
+    }
+
+    public void complete() {
+        IOUtils.closeQuietly(sw);
+    }
+}

processutils/src/main/java/com/atlassian/utils/process/StringProcessHandler.java

+package com.atlassian.utils.process;
+
+/**
+ * A ProcessHandler implementation which collects process output into strings and, optionally,
+ * provides input from a string.
+ */
+public class StringProcessHandler extends PluggableProcessHandler {
+    private static final String DEFAULT_ENCODING = "UTF-8";
+
+    private StringOutputHandler outputHandler;
+    private StringOutputHandler errorHandler;
+
+    public StringProcessHandler() {
+        this(null, DEFAULT_ENCODING);
+    }
+
+    public StringProcessHandler(String input) {
+        this(input, DEFAULT_ENCODING);
+    }
+
+    public StringProcessHandler(String input, String encoding) {
+        if (input != null) {
+            setInputHandler(new StringInputHandler(encoding, input));
+        }
+
+        outputHandler = new StringOutputHandler(encoding);
+        setOutputHandler(outputHandler);
+
+        errorHandler = new StringOutputHandler(encoding);
+        setErrorHandler(errorHandler);
+    }
+
+    public String getOutput() {
+        return outputHandler.getOutput();
+    }
+
+    public String getError() {
+        return errorHandler.getOutput();
+    }
+}
+

processutils/src/main/java/com/atlassian/utils/process/Watchdog.java

+package com.atlassian.utils.process;
+
+/**
+ */
+public interface Watchdog {
+    void resetWatchdog();
+
+    void cancel();
+}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.