/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package se.kth.karamel.backend.dag; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.apache.log4j.Logger; import se.kth.karamel.common.exception.DagConstructionException; import se.kth.karamel.common.exception.KaramelException; /** * Unit of execution in the DAG that knows about its predecessors and successors. * * @author kamal */ public class DagNode implements DagTaskCallback { public static enum Status { WAITING, READY, EXIST, ONGOING, DONE, FAILED, SKIPPED, TERMINATED; } private static final Logger logger = Logger.getLogger(DagNode.class); private final String id; private final Set<DagNode> predecessors = new HashSet<>(); private final Set<DagNode> successors = new HashSet<>(); private final Set<DagNode> signaledPredecessor = new HashSet<>(); private final Set<String> probs = new HashSet<>(); private DagTask task; private Status status = Status.WAITING; private int indention = 1; private String label; public DagNode(String id) { this.id = id; } public DagNode(String id, DagTask task) { this.id = id; this.task = task; } public String getId() { return id; } public Status getStatus() { return status; } public void setLabel(String label) { this.label = label; } public void setTask(DagTask task) throws DagConstructionException { if (this.task == null) { this.task = task; } else { throw new DagConstructionException(String.format("An attempt to override the task in the DAG '%s'", task)); } } public DagTask getTask() { return task; } public Set<DagNode> getSuccessors() { return successors; } public boolean addSuccessor(DagNode successor) { if (!successors.contains(successor)) { successors.add(successor); successor.addPredecessor(this); return true; } else { return false; } } public Set<DagNode> getPredecessors() { return predecessors; } public void addPredecessor(DagNode predecessor) { if (!predecessors.contains(predecessor)) { predecessors.add(predecessor); } } public void prepareToStart(String prob) throws DagConstructionException { if (probs.contains(prob)) { return; } probs.add(prob); task.prepareToStart(); for (DagNode succ : successors) { succ.prepareToStart(prob); } } public void detectCylcles(String prob, List<DagNode> ancestors) throws DagConstructionException { if (ancestors.contains(this)) { String message = "ERROR in YAML Definition: a cycle was detected (cyclic dependency): " + ancestors.toString() + " " + id; logger.error(String.format("Prob: %s %s", prob, message)); throw new DagConstructionException(); } if (probs.contains(prob)) { // logger.debug(String.format("Prob: %s has already visited %s", prob, id)); return; } else { // logger.debug(String.format("Prob: %s is visiting %s", prob, id)); } probs.add(prob); for (DagNode succ : successors) { List<DagNode> newList = new ArrayList<>(); newList.addAll(ancestors); newList.add(this); succ.detectCylcles(prob, newList); } } public void findMaxIndentionLevel(int indention) { if (this.indention < indention) { this.indention = indention; } for (DagNode newTask : successors) { newTask.findMaxIndentionLevel(indention + 1); } } public String printBfs(String prob, String pref, int indention) { String printStat = (label != null) ? label : status.toString(); if (this.indention > indention || probs.contains(prob)) { // logger.debug(String.format("Prob: %s has already visited %s", prob, id)); return pref + "$" + id + "(" + printStat + ")"; } else { // logger.debug(String.format("Prob: %s is visiting %s", prob, id)); } probs.add(prob); StringBuilder builder = new StringBuilder(); builder.append(pref).append("").append(id).append("(").append(printStat).append(")"); for (DagNode newTask : successors) { builder.append("\n").append(newTask.printBfs(prob, pref + " " + indention + "|", indention + 1)); } builder.append("\n").append(pref); return builder.toString(); } @Override public int hashCode() { return id.hashCode(); } @Override public boolean equals(Object o) { return (o instanceof DagNode && id.hashCode() == o.hashCode()); } @Override public String toString() { return id; } @Override public void queued() { status = Status.READY; } @Override public void exists() { logger.debug(String.format("Skipped '%s' because idempotent and exists in the machine.", id)); status = Status.EXIST; signalChildren(); } @Override public void started() { status = Status.ONGOING; } public void start() { task.submit(this); } @Override public void succeed() { logger.debug(String.format("Done '%s'", id)); status = Status.DONE; signalChildren(); } @Override public void skipped() { logger.debug(String.format("Skip '%s'", id)); status = Status.SKIPPED; signalChildren(); } public void terminate() { if (status != Status.TERMINATED) { for (DagNode succ : successors) { succ.terminate(); } task.terminate(); } } @Override public void terminated() { status = Status.TERMINATED; } private void signalChildren() { for (DagNode succ : successors) { try { succ.signal(this); } catch (KaramelException ex) { logger.error("", ex); } } } public synchronized void signal(DagNode pred) throws KaramelException { signaledPredecessor.add(pred); if (predecessors.size() == signaledPredecessor.size()) { logger.debug(String.format("Submitting '%s'", id)); if (task == null) { String message = String.format("Node is ready to go in the DAG but it doesn't have any task '%s'", id); logger.error(message); throw new KaramelException(message); } start(); } } @Override public void failed(String reason) { logger.error(String.format("Failed '%s' because '%s', DAG is stuck here :(", id, reason)); status = Status.FAILED; } public String toJson() { StringBuilder builder = new StringBuilder(); builder.append("["); int i = 0; for (DagNode dagNode : predecessors) { i++; builder.append("\"").append(dagNode.getId()).append("\""); if (i != predecessors.size()) { builder.append(","); } } builder.append("]"); return String.format("{\"id\": \"%s\", %s, \"status\": \"%s\", \"preds\": %s}", id, task.asJson(), status.toString(), builder.toString()); } }