// Path: src/main/java/com/danielflower/apprunner/io/LineConsumer.java
// public interface LineConsumer {
//     void consumeLine(String line);
// }
// 
// Path: src/main/java/com/danielflower/apprunner/io/WriterToOutputBridge.java
// public class WriterToOutputBridge extends Writer {
//     private final LineConsumer consoleLogHandler;
// 
//     public WriterToOutputBridge(LineConsumer consoleLogHandler) {
//         this.consoleLogHandler = consoleLogHandler;
//     }
// 
//     public void write(char[] cbuf, int off, int len) throws IOException {
//         String s = new String(cbuf, off, len);
//         consoleLogHandler.consumeLine(s);
//     }
// 
//     public void flush() throws IOException {
//     }
// 
//     public void close() throws IOException {
//     }
// }
// 
// Path: src/main/java/com/danielflower/apprunner/problems/ProjectCannotStartException.java
// public class ProjectCannotStartException extends AppRunnerException {
//     public ProjectCannotStartException(String message) {
//         super(message);
//     }
// 
//     public ProjectCannotStartException(String message, Throwable cause) {
//         super(message, cause);
//     }
// 
//     public ProjectCannotStartException(Throwable cause) {
//         super(cause);
//     }
// }
// 
// Path: src/main/java/com/danielflower/apprunner/FileSandbox.java
// public static String fullPath(File file) {
//     try {
//         return file.getCanonicalPath();
//     } catch (IOException e) {
//         return file.getAbsolutePath();
//     }
// }


import com.danielflower.apprunner.io.LineConsumer;
import com.danielflower.apprunner.io.WriterToOutputBridge;
import com.danielflower.apprunner.problems.ProjectCannotStartException;
import org.apache.commons.exec.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.eclipse.jetty.io.WriterOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import static com.danielflower.apprunner.FileSandbox.fullPath;
            throw pcse;
        } catch (Exception e) {
            String message = "Built successfully, but error on start for " + fullPath(projectRoot);
            buildLogHandler.consumeLine(message);
            buildLogHandler.consumeLine(e.toString());
            throw new ProjectCannotStartException(message, e);
        } finally {
            if (!started) {
                watchDog.destroyProcess();
            }
        }
        logEndTime(command, startTime);
        return watchDog;
    }

    public static void run(LineConsumer outputHandler, Map<String, String> envVarsForApp, CommandLine command, File projectRoot, long timeout) throws ProjectCannotStartException {
        long startTime = logStartInfo(command, projectRoot);
        ExecuteWatchdog watchDog = new ExecuteWatchdog(timeout);
        Executor executor = createExecutor(outputHandler, command, projectRoot, watchDog);
        try {
            int exitValue = executor.execute(command, envVarsForApp);
            if (executor.isFailure(exitValue)) {
                String message = watchDog.killedProcess()
                    ? "Timed out waiting for " + command
                    : "Exit code " + exitValue + " returned from " + command;
                throw new ProjectCannotStartException(message);
            }
        } catch (Exception e) {
            String message = "Error running: " + fullPath(projectRoot) + "> " + StringUtils.join(command.toStrings(), " ");
            outputHandler.consumeLine(message);
// Path: src/main/java/com/danielflower/apprunner/io/LineConsumer.java
// public interface LineConsumer {
//     void consumeLine(String line);
// }
// 
// Path: src/main/java/com/danielflower/apprunner/io/WriterToOutputBridge.java
// public class WriterToOutputBridge extends Writer {
//     private final LineConsumer consoleLogHandler;
// 
//     public WriterToOutputBridge(LineConsumer consoleLogHandler) {
//         this.consoleLogHandler = consoleLogHandler;
//     }
// 
//     public void write(char[] cbuf, int off, int len) throws IOException {
//         String s = new String(cbuf, off, len);
//         consoleLogHandler.consumeLine(s);
//     }
// 
//     public void flush() throws IOException {
//     }
// 
//     public void close() throws IOException {
//     }
// }
// 
// Path: src/main/java/com/danielflower/apprunner/problems/ProjectCannotStartException.java
// public class ProjectCannotStartException extends AppRunnerException {
//     public ProjectCannotStartException(String message) {
//         super(message);
//     }
// 
//     public ProjectCannotStartException(String message, Throwable cause) {
//         super(message, cause);
//     }
// 
//     public ProjectCannotStartException(Throwable cause) {
//         super(cause);
//     }
// }
// 
// Path: src/main/java/com/danielflower/apprunner/FileSandbox.java
// public static String fullPath(File file) {
//     try {
//         return file.getCanonicalPath();
//     } catch (IOException e) {
//         return file.getAbsolutePath();
//     }
// }


// Path: src/main/java/com/danielflower/apprunner/runners/ProcessStarter.java
import com.danielflower.apprunner.io.LineConsumer;
import com.danielflower.apprunner.io.WriterToOutputBridge;
import com.danielflower.apprunner.problems.ProjectCannotStartException;
import org.apache.commons.exec.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.eclipse.jetty.io.WriterOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import static com.danielflower.apprunner.FileSandbox.fullPath;

            throw pcse;
        } catch (Exception e) {
            String message = "Built successfully, but error on start for " + fullPath(projectRoot);
            buildLogHandler.consumeLine(message);
            buildLogHandler.consumeLine(e.toString());
            throw new ProjectCannotStartException(message, e);
        } finally {
            if (!started) {
                watchDog.destroyProcess();
            }
        }
        logEndTime(command, startTime);
        return watchDog;
    }

    public static void run(LineConsumer outputHandler, Map<String, String> envVarsForApp, CommandLine command, File projectRoot, long timeout) throws ProjectCannotStartException {
        long startTime = logStartInfo(command, projectRoot);
        ExecuteWatchdog watchDog = new ExecuteWatchdog(timeout);
        Executor executor = createExecutor(outputHandler, command, projectRoot, watchDog);
        try {
            int exitValue = executor.execute(command, envVarsForApp);
            if (executor.isFailure(exitValue)) {
                String message = watchDog.killedProcess()
                    ? "Timed out waiting for " + command
                    : "Exit code " + exitValue + " returned from " + command;
                throw new ProjectCannotStartException(message);
            }
        } catch (Exception e) {
            String message = "Error running: " + fullPath(projectRoot) + "> " + StringUtils.join(command.toStrings(), " ");
            outputHandler.consumeLine(message);
            outputHandler.consumeLine(e.toString());