public void determineExecutionPlan() {
    List<TaskInfo> nodeQueue =
        CollectionUtils.collect(
            new ArrayList<Task>(entryTasks),
            new Transformer<TaskInfo, Task>() {
              public TaskInfo transform(Task original) {
                return graph.getNode(original);
              }
            });

    Set<TaskInfo> visitingNodes = new HashSet<TaskInfo>();
    while (!nodeQueue.isEmpty()) {
      TaskInfo taskNode = nodeQueue.get(0);
      boolean filtered = !filter.isSatisfiedBy(taskNode.getTask());
      if (filtered) {
        taskNode.setRequired(false);
      }

      if (!taskNode.getRequired() || executionPlan.containsKey(taskNode.getTask())) {
        nodeQueue.remove(0);
        continue;
      }

      if (visitingNodes.add(taskNode)) {
        // Have not seen this task before - add its dependencies to the head of the queue and leave
        // this
        // task in the queue
        ArrayList<TaskInfo> dependsOnTasks = new ArrayList<TaskInfo>();
        addAllReversed(dependsOnTasks, taskNode.getHardSuccessors());
        addAllReversed(dependsOnTasks, taskNode.getSoftSuccessors());
        for (TaskInfo dependsOnTask : dependsOnTasks) {
          if (visitingNodes.contains(dependsOnTask)) {
            throw new CircularReferenceException(
                String.format(
                    "Circular dependency between tasks. Cycle includes [%s, %s].",
                    taskNode.getTask(), dependsOnTask.getTask()));
          }
          nodeQueue.add(0, dependsOnTask);
        }
      } else {
        // Have visited this task's dependencies - add it to the end of the plan
        nodeQueue.remove(0);
        visitingNodes.remove(taskNode);
        executionPlan.put(taskNode.getTask(), taskNode);
      }
    }
  }
 public TaskInfo getTaskToExecute() {
   lock.lock();
   try {
     while (true) {
       TaskInfo nextMatching = null;
       boolean allTasksComplete = true;
       for (TaskInfo taskInfo : executionPlan.values()) {
         allTasksComplete = allTasksComplete && taskInfo.isComplete();
         if (taskInfo.isReady()
             && taskInfo.allDependenciesComplete()
             && !runningProjects.contains(taskInfo.getTask().getProject().getPath())) {
           nextMatching = taskInfo;
           break;
         }
       }
       if (allTasksComplete) {
         return null;
       }
       if (nextMatching == null) {
         try {
           condition.await();
         } catch (InterruptedException e) {
           throw new RuntimeException(e);
         }
       } else {
         if (nextMatching.allDependenciesSuccessful()) {
           nextMatching.startExecution();
           runningProjects.add(nextMatching.getTask().getProject().getPath());
           return nextMatching;
         } else {
           nextMatching.skipExecution();
           condition.signalAll();
         }
       }
     }
   } finally {
     lock.unlock();
   }
 }
  public void taskComplete(TaskInfo taskInfo) {
    lock.lock();
    try {
      if (taskInfo.isFailed()) {
        handleFailure(taskInfo);
      }

      taskInfo.finishExecution();
      runningProjects.remove(taskInfo.getTask().getProject().getPath());
      condition.signalAll();
    } finally {
      lock.unlock();
    }
  }
  private void handleFailure(TaskInfo taskInfo) {
    Throwable executionFailure = taskInfo.getExecutionFailure();
    if (executionFailure != null) {
      // Always abort execution for an execution failure (as opposed to a task failure)
      abortExecution();
      this.failures.add(executionFailure);
      return;
    }

    // Task failure
    try {
      failureHandler.onTaskFailure(taskInfo.getTask());
      this.failures.add(taskInfo.getTaskFailure());
    } catch (Exception e) {
      // If the failure handler rethrows exception, then execution of other tasks is aborted.
      // (--continue will collect failures)
      abortExecution();
      this.failures.add(e);
    }
  }