ProcessLauncher.java Source code

Java tutorial

Introduction

Here is the source code for ProcessLauncher.java

Source

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Launches a process, redirecting the output of that sub-process to the output
 * of this (the parent) process.
 * 
 * @author Klaas Waslander
 */
public final class ProcessLauncher {
    /** the logger for this class */
    private final static Logger LOGGER = Logger.getLogger(ProcessLauncher.class.getName());

    private String commandLine;

    private String[] commandArray;

    private File baseDir;

    private ArrayList listeners = new ArrayList(1);

    private Process subProcess;

    private boolean finished = false;

    StringBuffer out = new StringBuffer();

    StringBuffer err = new StringBuffer();

    /**
     * Constructs new process launcher with the given command line.
     */
    public ProcessLauncher(String commandLine) {
        this(commandLine, null);
    }

    public ProcessLauncher(String commandLine, File baseDir) {
        this.commandLine = commandLine;
        this.baseDir = baseDir;
    }

    /**
     * Constructs new process launcher with the given command array.
     */
    public ProcessLauncher(String[] commandArray) {
        this(commandArray, null);
    }

    public ProcessLauncher(String[] commandArray, File baseDir) {
        this.commandArray = commandArray;
        this.baseDir = baseDir;
    }

    /**
     * Constructs new process launcher with the given command element list.
     */
    public ProcessLauncher(ArrayList commandList) {
        this(commandList, null);
    }

    public ProcessLauncher(ArrayList commandList, File baseDir) {
        this(toStringArray(commandList), baseDir);
    }

    private static String[] toStringArray(ArrayList list) {
        String[] result = new String[list.size()];
        Iterator iter = list.iterator();
        int arrayIndex = 0;
        while (iter.hasNext()) {
            result[arrayIndex++] = iter.next().toString();
        }
        return result;
    }

    /**
     * Classes implementing this interface can receive output generated by
     * processes launched using the ProcessLauncher.
     */
    public interface OutputListener {
        public void standardOutput(char[] output);

        public void errorOutput(char[] output);
    }

    /**
     * Add a listener for output from the to-be-launched process.
     */
    public void addOutputListener(OutputListener listener) {
        this.listeners.add(listener);
    }

    /** fire error output event */
    private void fireErr(char[] err) {
        if (this.listeners.isEmpty()) {
            this.err.append(out);
        }

        Iterator iter = this.listeners.iterator();
        while (iter.hasNext()) {
            ((OutputListener) iter.next()).errorOutput(err);
        }
    }

    /** fire standard output event */
    private void fireOut(char[] out) {
        if (this.listeners.isEmpty()) {
            this.out.append(out);
        }

        Iterator iter = this.listeners.iterator();
        while (iter.hasNext()) {
            ((OutputListener) iter.next()).standardOutput(out);
        }
    }

    /**
     * Get standard output, in case no listeners were registered - never returns
     * null.
     */
    public String getStandardOutput() {
        if (!this.listeners.isEmpty()) {
            throw new IllegalStateException(
                    "Cannot get standard output, because outputlisteners have been registered.");
        }
        return this.out.toString();
    }

    /**
     * Get error output, in case no listeners were registered - never returns
     * null.
     */
    public String getErrorOutput() {
        if (!this.listeners.isEmpty()) {
            throw new IllegalStateException(
                    "Cannot get error output, because outputlisteners have been registered.");
        }
        return this.err.toString();
    }

    /**
     * Get the commandline that is used to launch the process.
     */
    public String getCommandLine() {
        String usedCommand = this.commandLine;
        if (this.commandLine == null && this.commandArray != null) {
            usedCommand = "";
            for (int i = 0; i < this.commandArray.length; i++) {
                if (i > 0) {
                    usedCommand += " ";
                }
                usedCommand += this.commandArray[i];
            }
        }
        return usedCommand;
    }

    /**
     * Check whether execution has finished.
     */
    public boolean hasFinished() {
        return finished;
    }

    /**
     * Launches the process, and blocks until that process completes execution.
     * 
     * @throws CommandNotExistsException
     *           If the command could not be executed because it does not exist
     */
    public int launch() throws CommandNotExistsException {
        this.err.setLength(0);
        this.out.setLength(0);

        BackgroundPrinter stdout = null;
        BackgroundPrinter stderr = null;
        try {
            if (this.commandArray != null) {
                this.subProcess = Runtime.getRuntime().exec(this.commandArray, null, this.baseDir);
            } else {
                this.subProcess = Runtime.getRuntime().exec(this.commandLine, null, this.baseDir);
            }

            stdout = new BackgroundPrinter(subProcess.getInputStream(), false);
            stderr = new BackgroundPrinter(subProcess.getErrorStream(), true);
            stdout.start();
            stderr.start();

            // kill process and wait max 10 seconds for output to complete
            int exitValue = this.subProcess.waitFor();
            stdout.join(10000);
            stderr.join(10000);

            /*
             * if (exitValue != 0) { LOGGER.fine("WARNING: exit value " + exitValue + "
             * for command \"" + getCommandLine() + "\""); }
             */

            return exitValue;
        } catch (IOException ioe) {
            // usually caused if the command does not exist at all
            throw new CommandNotExistsException("Command probably does not exist: " + ioe);
        } catch (Exception e) {
            LOGGER.log(Level.SEVERE, "Exception while running/launching \"" + getCommandLine() + "\".", e);
        } finally {
            if (this.subProcess != null) {
                this.subProcess.destroy();
                this.subProcess = null;
            }
            if (stdout != null) {
                stdout.close();
            }
            if (stderr != null) {
                stderr.close();
            }
            this.finished = true;
        }
        return -1;
    }

    /**
     * Tries to abort the currently running process.
     */
    public void abort() {
        if (this.subProcess != null) {
            this.subProcess.destroy();
            this.subProcess = null;
        }
    }

    /**
     * Catches output from a "java.lang.Process" and writes it to either
     * System.err or System.out.
     * 
     * @author Klaas Waslander - Sun Java Center
     */
    private class BackgroundPrinter extends Thread {
        private InputStream in;

        boolean isErrorOutput;

        public BackgroundPrinter(InputStream in, boolean isErrorOutput) {
            this.in = in;
            this.isErrorOutput = isErrorOutput;
        }

        public void run() {
            try {
                BufferedReader reader = new BufferedReader(new InputStreamReader(this.in));

                // read buffer
                char[] buf = new char[1024];

                // write data to target, until no more data is left to read
                int numberOfReadBytes;
                while ((numberOfReadBytes = reader.read(buf)) != -1) {
                    char[] clearedbuf = new char[numberOfReadBytes];
                    System.arraycopy(buf, 0, clearedbuf, 0, numberOfReadBytes);

                    if (this.isErrorOutput) {
                        fireErr(clearedbuf);
                    } else {
                        fireOut(clearedbuf);
                    }
                }
                /*
                 * } catch (IOException ioe) { // ignore this: process has ended,
                 * causing IOException } catch (NullPointerException ioe) { // ignore
                 * this: there was no resulting output
                 */
            } catch (Exception e) {
                LOGGER.log(Level.FINE, "Exception while reading from stream from subprocess.", e);
            }
        }

        public void close() {
            try {
                this.in.close();
            } catch (Exception e) {
                LOGGER.log(Level.WARNING, "Closing background stream for launched process caused exception.", e);
            }
        }
    }

    /**
     * Exception that is thrown when a command could not be executed because it
     * (probably) does not exist at all.
     * 
     * @author Klaas Waslander
     */
    public static class CommandNotExistsException extends RuntimeException {
        /**
         * Construct a new exception for a command that does not exist.
         * 
         * @param msg
         *          The message for this exception.
         */
        public CommandNotExistsException(String msg) {
            super(msg);
        }
    }
}