TimeoutProcessBuilder.java

/*
 * TimeoutProcessBuilder
 *
 * $Id$
 * $URL$
 */
package gov.usgs.util;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

/**
 * The TimeoutProcessBuilder wraps a ProcessBuilder, adding support for a
 * command time out.
 *
 * This class does not support a full command String complete with arguments.
 * You can use the StringUtils.split method to get around this.
 *
 * @see java.lang.ProcessBuilder
 * @see TimeoutProcess
 */
public class TimeoutProcessBuilder {

	/** The wrapped process builder. */
	private ProcessBuilder builder = null;
	/** The timeout for this process. */
	private long timeout = -1;

	/**
	 * Create a new TimeoutProcessBuilder with a timeout and an array of
	 * strings.
	 *
	 * @param timeout
	 *            timeout in milliseconds for process, or <= 0 for no timeout.
	 * @param command
	 *            array of strings that represent command. The first element
	 *            must be the full path to the executable, without arguments.
	 */
	public TimeoutProcessBuilder(long timeout, String... command) {
		builder = new ProcessBuilder(command);
		this.timeout = timeout;
	}

	/**
	 * Create a new TimeoutProcessBuilder with a timeout and an array of
	 * strings.
	 *
	 * @param timeout
	 *            timeout in milliseconds for process, or <= 0 for no timeout.
	 * @param command
	 *            list of strings that represent command.
	 */
	public TimeoutProcessBuilder(long timeout, List<String> command) {
		builder = new ProcessBuilder(command);
		this.timeout = timeout;

	}

	/**
	 * This signature is preserved, but calls the alternate constructor with
	 * argument order swapped.
	 * @param command
	 *            list of strings that represent command.
	 * @param timeout
	 *            timeout in milliseconds for process, or &lt;= 0 for no timeout.
	 */
	@Deprecated
	public TimeoutProcessBuilder(List<String> command, long timeout) {
		this(timeout, command);
	}

	/** @return list of builder commands */
	public List<String> command() {
		return builder.command();
	}

	/**
	 * @param command give builder a list of commands
	 * @return TimeoutProcessBuilder
	 */
	public TimeoutProcessBuilder command(List<String> command) {
		builder.command(command);
		return this;
	}

	/**
	 * @param command give builder a single command
	 * @return TimeoutProcessBuilder
	 */
	public TimeoutProcessBuilder command(String command) {
		builder.command(command);
		return this;
	}

	/** @return builder directory */
	public File directory() {
		return builder.directory();
	}

	/**
	 * @param directory to set
	 * @return TimeoutProcessBuilder
	 */
	public TimeoutProcessBuilder directory(File directory) {
		builder.directory(directory);
		return this;
	}

	/** @return builder environment */
	public Map<String, String> environment() {
		return builder.environment();
	}

	/** @return boolean redirectErrorStream */
	public boolean redirectErrorStream() {
		return builder.redirectErrorStream();
	}

	/**
	 * @param redirectErrorStream to set
	 * @return TimeoutProcessBuilder
	 */
	public TimeoutProcessBuilder redirectErrorStream(boolean redirectErrorStream) {
		builder.redirectErrorStream(redirectErrorStream);
		return this;
	}

	/**
	 * @return a TimeoutProcess
	 * @throws IOException if IO error occurs
	 */
	public TimeoutProcess start() throws IOException {
		final TimeoutProcess process = new TimeoutProcess(builder.start());

		if (timeout > 0) {
			// set up the timeout for this process
			final Timer timer = new Timer();
			timer.schedule(new TimerTask() {
				@Override
				public void run() {
					process.setTimeoutElapsed(true);
					process.destroy();
				}
			}, timeout);
			process.setTimer(timer);
		}

		return process;
	}

	/** @return timeout */
	public long getTimeout() {
		return this.timeout;
	}

	/** @param timeout to set */
	public void setTimeout(final long timeout) {
		this.timeout = timeout;
	}

}