package core.aws.workflow; import core.aws.env.Context; import core.aws.env.Param; import core.aws.resource.Resource; import core.aws.util.Asserts; import core.aws.util.ToStringHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.HashSet; import java.util.Set; import java.util.concurrent.Callable; /** * @author neo */ public abstract class Task<T extends Resource> implements Callable<Void> { public final Set<Task> dependencies = new HashSet<>(); public final Set<Task> backwardDependencies = new HashSet<>(); public final T resource; private final Logger logger = LoggerFactory.getLogger(Task.class); private final Logger messageLogger = LoggerFactory.getLogger("message"); private Context context; private Workflow workflow; private volatile TaskState state = TaskState.NEW; public Task(T resource) { this.resource = resource; } public void dependsOn(Task<?> dependencyTask) { dependencies.add(dependencyTask); dependencyTask.backwardDependencies.add(this); } public void unlink(Task dependencyTask) { Asserts.isTrue(dependencies.remove(dependencyTask), "can not find task in dependencies, this={}, dependencyTask={}", this, dependencyTask); Asserts.isTrue(dependencyTask.backwardDependencies.remove(this), "can not find task in backward dependencies, this={}, dependencyTask={}", this, dependencyTask); } private boolean readyToRun() { if (state != TaskState.NEW) return false; for (Task dependency : dependencies) { if (dependency.state != TaskState.DONE) return false; } return true; } boolean done() { return state == TaskState.DONE; } void runIfReady() { synchronized (this) { if (readyToRun()) { state = TaskState.SCHEDULED; try { workflow.submit(this); } catch (InterruptedException e) { throw new IllegalStateException(e); } } } } public void injectDependencies(Workflow workflow, Context context) { //TODO: find better way to fix findbugs issue synchronized (this) { this.workflow = workflow; } this.context = context; } @Override public final Void call() throws Exception { try { Thread thread = Thread.currentThread(); thread.setName(action() + ":" + resource.id + ":" + thread.getId()); if ("true".equals(context.param(Param.DRY_RUN))) { messageLogger.info("dry-run: {}\n", this); } else { logger.info("execute {}", this); execute(context); } state = TaskState.DONE; backwardDependencies.forEach(core.aws.workflow.Task::runIfReady); } catch (Throwable e) { logger.error("failed to execute task, error={}", e.getMessage(), e); state = TaskState.FAILED; } return null; } private String action() { Action action = getClass().getDeclaredAnnotation(Action.class); if (action == null) return getClass().getSimpleName(); return action.value(); } @Override public String toString() { return new ToStringHelper(this) .add(resource) .toString(); } public abstract void execute(Context context) throws Exception; }