public void addToTaskGraph(Collection<? extends Task> tasks) {
    List<Task> queue = new ArrayList<Task>(tasks);
    Collections.sort(queue);
    entryTasks.addAll(queue);
    Set<Task> visiting = new HashSet<Task>();
    CachingTaskDependencyResolveContext context = new CachingTaskDependencyResolveContext();

    while (!queue.isEmpty()) {
      Task task = queue.get(0);

      if (graph.hasTask(task) && graph.getNode(task).getRequired()) {
        queue.remove(0);
        continue;
      }

      if (visiting.add(task)) {
        // Have not seen this task before - add its dependencies to the head of the queue and leave
        // this
        // task in the queue
        Set<Task> dependsOnTasks = new TreeSet<Task>(Collections.reverseOrder());
        dependsOnTasks.addAll(context.getDependencies(task));
        for (Task dependsOnTask : dependsOnTasks) {
          if (visiting.contains(dependsOnTask)) {
            throw new CircularReferenceException(
                String.format(
                    "Circular dependency between tasks. Cycle includes [%s, %s].",
                    task, dependsOnTask));
          }
          queue.add(0, dependsOnTask);
        }
      } else {
        // Have visited this task's dependencies - add it to the graph
        queue.remove(0);
        visiting.remove(task);
        TaskInfo node = graph.addNode(task);
        Set<? extends Task> dependencies = context.getDependencies(task);
        for (Task dependency : dependencies) {
          graph.addHardEdge(node, dependency);
        }
        for (Task mustRunAfter : task.getMustRunAfter().getDependencies(task)) {
          graph.addSoftEdge(node, mustRunAfter);
        }
      }
    }
  }
 public void clear() {
   lock.lock();
   try {
     graph.clear();
     entryTasks.clear();
     executionPlan.clear();
     failures.clear();
     runningProjects.clear();
   } finally {
     lock.unlock();
   }
 }