package uk.co.datumedge.redislauncher;

import static uk.co.datumedge.redislauncher.Configuration.defaultConfiguration;

import java.io.IOException;
import java.io.OutputStream;

import org.apache.commons.exec.DefaultExecuteResultHandler;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.Executor;
import org.apache.commons.exec.ProcessDestroyer;
import org.apache.commons.exec.PumpStreamHandler;

/**
 * An execution of a redis server.  Used by a {@link LocalRedisServer} to start and stop a redis server process.
 */
public final class Execution {
	private ExecutionProcessDestroyer executionProcessDestroyer;
	private final OutputStream outputStream;
	private final OutputStream errorStream;
	final Configuration configuration;

	public static Builder anExecution() {
		return new Builder();
	}

	private Execution(Configuration configuration, OutputStream outputStream, OutputStream errorStream) {
		this.configuration = configuration;
		this.outputStream = outputStream;
		this.errorStream = errorStream;
	}

	DefaultExecuteResultHandler start(ProcessDestroyer lifecyleProcessDestroyer) throws IOException {
		Executor executor = new DefaultExecutor();
		DefaultExecuteResultHandler handler = new DefaultExecuteResultHandler();
		this.executionProcessDestroyer = new ExecutionProcessDestroyer();
		executor.setProcessDestroyer(new CompositeProcessDestroyer(
				this.executionProcessDestroyer, lifecyleProcessDestroyer));
		executor.setStreamHandler(new PumpStreamHandler(
				outputStream,
				errorStream,
				configuration.inputStream()));
		executor.execute(configuration.commandLine(), handler);
		return handler;
	}

	void destroy() {
		if (executionProcessDestroyer != null) {
			executionProcessDestroyer.destroy();
		}
	}
	
	/**
	 * A builder of {@code Execution} instances.
	 */
	public static final class Builder {
		private Configuration configuration;
		private OutputStream outputStream;
		private OutputStream errorStream;

		private Builder() { }

		public Builder withConfiguration(Configuration configuration) {
			this.configuration = configuration;
			return this;
		}
		
		public Builder withOutputStream(OutputStream outputStream) {
			this.outputStream = outputStream;
			return this;
		}
		
		public Builder withErrorStream(OutputStream errorStream) {
			this.errorStream = errorStream;
			return this;
		}

		/**
		 * @return an {@code Execution} instance
		 * @throws NullPointerException
		 */
		public Execution build() {
			if (configuration == null) configuration = defaultConfiguration();
			return new Execution(configuration, outputStream, errorStream);
		}
	}
}

@@ -755,16 +755,122 @@
 ation;%0A%0A
+%09/**%0A%09 * Creates an %7B@code Execution%7D builder.%0A%09 *%0A%09 * @return an %7B@code Execution%7D builder instance%0A%09 */%0A
 %09public 
@@ -1865,17 +1865,16 @@
 %0A%09%09%7D%0A%09%7D%0A
-%09
 %0A%09/**%0A%09 
@@ -2097,16 +2097,123 @@
 () %7B %7D%0A%0A
+%09%09/**%0A%09%09 * Sets the %7B@code Configuration%7D used by the builder.%0A%09%09 *%0A%09%09 * @return the updated builder%0A%09%09 */%0A
 %09%09public
@@ -2321,34 +2321,150 @@
 eturn this;%0A%09%09%7D%0A
+%0A
 %09%09
+/**%0A%09%09 * Sets the %7B@code OutputStream%7D to which %7B@code stdout%7D is sent.%0A%09%09 *%0A%09%09 * @return the updated builder%0A%09%09 */
 %0A%09%09public Builde
@@ -2568,18 +2568,134 @@
 is;%0A%09%09%7D%0A
+%0A
 %09%09
+/**%0A%09%09 * Sets the %7B@code OutputStream%7D to which %7B@code stderr%7D is sent.%0A%09%09 *%0A%09%09 * @return the updated builder%0A%09%09 */
 %0A%09%09publi
@@ -2819,79 +2819,566 @@
 %09 * 
-@return an %7B@code Execution%7D instance%0A%09%09 * @throws NullPointerException
+Creates an %7B@code Execution%7D instance from the current builder. If no %7B@code Configuration%7D was specified,%0A%09%09 * %7B@linkplain Configuration.Builder#build() default configuration%7D is used. If no %7B@code outputStream%7D was%0A%09%09 * specified, %7B@code stdout%7D is swallowed. If no %7B@code errorStream%7D was specified, %7B@code stderr%7D is swallowed.%0A%09%09 *%0A%09%09 * @return an %7B@code Execution%7D instance%0A%09%09 * @throws NullPointerException%0A%09%09 *             if no %7B@code Configuration%7D was specified and the %7B@code redislauncher.command%7D system%0A%09%09 *             property does not exist
 %0A%09%09 

add javadoc to Execution and its builder