private CompletableFuture<Void> waitForEnvironmentInitialization(
      BuildExecutionSession buildExecutionSession, StartedEnvironment startedEnvironment) {
    CompletableFuture<Void> waitToCompleteFuture = new CompletableFuture<>();
    try {
      Consumer<RunningEnvironment> onComplete =
          (runningEnvironment) -> {
            buildExecutionSession.setRunningEnvironment(runningEnvironment);
            buildExecutionSession.setStatus(BuildExecutionStatus.BUILD_ENV_SETUP_COMPLETE_SUCCESS);
            waitToCompleteFuture.complete(null);
          };
      Consumer<Exception> onError =
          (e) -> {
            buildExecutionSession.setStatus(
                BuildExecutionStatus.BUILD_ENV_SETUP_COMPLETE_WITH_ERROR);
            waitToCompleteFuture.completeExceptionally(
                new BuildProcessException(e, startedEnvironment));
          };
      buildExecutionSession.setStatus(BuildExecutionStatus.BUILD_ENV_WAITING);

      startedEnvironment.monitorInitialization(onComplete, onError);
    } catch (Throwable e) {
      waitToCompleteFuture.completeExceptionally(new BuildProcessException(e, startedEnvironment));
    }
    return waitToCompleteFuture;
  }
  @Override
  public BuildExecutionSession startBuilding(
      BuildExecutionConfiguration buildExecutionConfiguration,
      Consumer<BuildExecutionStatusChangedEvent> onBuildExecutionStatusChangedEvent)
      throws ExecutorException {

    BuildExecutionSession buildExecutionSession =
        new DefaultBuildExecutionSession(
            buildExecutionConfiguration, onBuildExecutionStatusChangedEvent);
    buildExecutionSession.setStatus(BuildExecutionStatus.NEW);

    runningExecutions.put(buildExecutionConfiguration.getId(), buildExecutionSession);

    CompletableFuture.supplyAsync(() -> configureRepository(buildExecutionSession), executor)
        .thenApplyAsync(
            repositoryConfiguration ->
                setUpEnvironment(buildExecutionSession, repositoryConfiguration),
            executor)
        .thenComposeAsync(
            startedEnvironment ->
                waitForEnvironmentInitialization(buildExecutionSession, startedEnvironment),
            executor)
        .thenApplyAsync(nul -> buildSetUp(buildExecutionSession), executor)
        .thenComposeAsync(
            runningBuild -> waitBuildToComplete(buildExecutionSession, runningBuild), executor)
        .thenApplyAsync(
            completedBuild -> retrieveBuildDriverResults(buildExecutionSession, completedBuild),
            executor)
        .thenApplyAsync(nul -> retrieveRepositoryManagerResults(buildExecutionSession), executor)
        .thenApplyAsync(nul -> destroyEnvironment(buildExecutionSession), executor)
        .handleAsync((nul, e) -> completeExecution(buildExecutionSession, e), executor);

    // TODO re-connect running instances in case of crash
    return buildExecutionSession;
  }
 private Void destroyEnvironment(BuildExecutionSession buildExecutionSession) {
   try {
     buildExecutionSession.setStatus(BuildExecutionStatus.BUILD_ENV_DESTROYING);
     buildExecutionSession.getRunningEnvironment().destroyEnvironment();
     buildExecutionSession.setStatus(BuildExecutionStatus.BUILD_ENV_DESTROYED);
   } catch (Throwable e) {
     throw new BuildProcessException(e);
   }
   return null;
 }
 private RepositorySession configureRepository(BuildExecutionSession buildExecutionSession) {
   buildExecutionSession.setStatus(BuildExecutionStatus.REPO_SETTING_UP);
   try {
     RepositoryManager repositoryManager =
         repositoryManagerFactory.getRepositoryManager(RepositoryType.MAVEN);
     BuildExecution buildExecution = buildExecutionSession.getBuildExecutionConfiguration();
     return repositoryManager.createBuildRepository(buildExecution);
   } catch (Throwable e) {
     throw new BuildProcessException(e);
   }
 }
  private Void retrieveRepositoryManagerResults(BuildExecutionSession buildExecutionSession) {
    try {
      buildExecutionSession.setStatus(
          BuildExecutionStatus.COLLECTING_RESULTS_FROM_REPOSITORY_NAMAGER);
      RunningEnvironment runningEnvironment = buildExecutionSession.getRunningEnvironment();
      buildExecutionSession.setRunningEnvironment(runningEnvironment);

      RepositorySession repositorySession = runningEnvironment.getRepositorySession();
      RepositoryManagerResult repositoryManagerResult = repositorySession.extractBuildArtifacts();
      buildExecutionSession.setRepositoryManagerResult(repositoryManagerResult);
    } catch (Throwable e) {
      throw new BuildProcessException(e, buildExecutionSession.getRunningEnvironment());
    }
    return null;
  }
 private StartedEnvironment setUpEnvironment(
     BuildExecutionSession buildExecutionSession, RepositorySession repositorySession) {
   buildExecutionSession.setStatus(BuildExecutionStatus.BUILD_ENV_SETTING_UP);
   BuildExecutionConfiguration buildExecutionConfiguration =
       buildExecutionSession.getBuildExecutionConfiguration();
   try {
     EnvironmentDriver envDriver =
         environmentDriverFactory.getDriver(buildExecutionConfiguration.getBuildType());
     StartedEnvironment startedEnv =
         envDriver.buildEnvironment(buildExecutionConfiguration.getBuildType(), repositorySession);
     return startedEnv;
   } catch (Throwable e) {
     throw new BuildProcessException(e);
   }
 }
 private RunningBuild buildSetUp(BuildExecutionSession buildExecutionSession) {
   buildExecutionSession.setStatus(BuildExecutionStatus.BUILD_SETTING_UP);
   RunningEnvironment runningEnvironment = buildExecutionSession.getRunningEnvironment();
   try {
     String liveLogWebSocketUrl = runningEnvironment.getBuildAgentUrl();
     log.debug("Setting live log websocket url: {}", liveLogWebSocketUrl);
     buildExecutionSession.setLiveLogsUri(Optional.of(new URI(liveLogWebSocketUrl)));
     buildExecutionSession.setStartTime(new Date());
     BuildDriver buildDriver =
         buildDriverFactory.getBuildDriver(
             buildExecutionSession.getBuildExecutionConfiguration().getBuildType());
     return buildDriver.startProjectBuild(buildExecutionSession, runningEnvironment);
   } catch (Throwable e) {
     throw new BuildProcessException(e, runningEnvironment);
   }
 }
 private Void retrieveBuildDriverResults(
     BuildExecutionSession buildExecutionSession, CompletedBuild completedBuild) {
   buildExecutionSession.setStatus(BuildExecutionStatus.COLLECTING_RESULTS_FROM_BUILD_DRIVER);
   try {
     BuildDriverResult buildResult = completedBuild.getBuildResult();
     BuildDriverStatus buildDriverStatus = buildResult.getBuildDriverStatus();
     buildExecutionSession.setBuildDriverResult(buildResult);
     if (buildDriverStatus.completedSuccessfully()) {
       buildExecutionSession.setStatus(BuildExecutionStatus.BUILD_COMPLETED_SUCCESS);
     } else {
       buildExecutionSession.setStatus(BuildExecutionStatus.BUILD_COMPLETED_WITH_ERROR);
     }
     return null;
   } catch (Throwable e) {
     throw new BuildProcessException(e, completedBuild.getRunningEnvironment());
   }
 }
  private CompletableFuture<CompletedBuild> waitBuildToComplete(
      BuildExecutionSession buildExecutionSession, RunningBuild runningBuild) {
    CompletableFuture<CompletedBuild> waitToCompleteFuture = new CompletableFuture<>();
    try {
      Consumer<CompletedBuild> onComplete =
          (completedBuild) -> {
            waitToCompleteFuture.complete(completedBuild);
          };
      Consumer<Throwable> onError =
          (e) -> {
            waitToCompleteFuture.completeExceptionally(
                new BuildProcessException(e, runningBuild.getRunningEnvironment()));
          };

      buildExecutionSession.setStatus(BuildExecutionStatus.BUILD_WAITING);

      runningBuild.monitor(onComplete, onError);
    } catch (Throwable exception) {
      waitToCompleteFuture.completeExceptionally(
          new BuildProcessException(exception, runningBuild.getRunningEnvironment()));
    }
    return waitToCompleteFuture;
  }
  private Void completeExecution(BuildExecutionSession buildExecutionSession, Throwable e) {
    buildExecutionSession.setStatus(BuildExecutionStatus.FINALIZING_EXECUTION);

    if (buildExecutionSession.getStartTime() == null) {
      buildExecutionSession.setException(new ExecutorException("Missing start time."));
    }
    if (e != null) {
      stopRunningEnvironment(e);
    }

    if (e != null) {
      buildExecutionSession.setException(new ExecutorException(e));
    }

    if (buildExecutionSession.getEndTime() != null) {
      buildExecutionSession.setException(new ExecutorException("End time already set."));
    } else {
      buildExecutionSession.setEndTime(new Date());
    }

    // check if any of previous statuses indicated "failed" state
    if (buildExecutionSession.hasFailed()) { // TODO differentiate build and system error
      buildExecutionSession.setStatus(BuildExecutionStatus.DONE_WITH_ERRORS);
    } else {
      buildExecutionSession.setStatus(BuildExecutionStatus.DONE);
    }

    log.debug(
        "Removing buildExecutionTask ["
            + buildExecutionSession.getId()
            + "] form list of running tasks.");
    runningExecutions.remove(buildExecutionSession.getId());

    return null;
  }