package alien4cloud.paas.wf;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.elasticsearch.common.collect.Lists;
import org.elasticsearch.common.collect.Maps;
import alien4cloud.exception.AlreadyExistException;
import org.alien4cloud.tosca.model.definitions.Operation;
import org.alien4cloud.tosca.model.templates.NodeTemplate;
import org.alien4cloud.tosca.model.templates.RelationshipTemplate;
import alien4cloud.paas.plan.ToscaNodeLifecycleConstants;
import alien4cloud.paas.wf.WorkflowsBuilderService.TopologyContext;
import alien4cloud.paas.wf.exception.BadWorkflowOperationException;
import alien4cloud.paas.wf.exception.InconsistentWorkflowException;
import alien4cloud.paas.wf.util.WorkflowUtils;
public abstract class AbstractWorkflowBuilder {
public abstract void addNode(Workflow wf, String nodeId, TopologyContext toscaTypeFinder, boolean isCompute);
public abstract void addRelationship(Workflow wf, String nodeId, NodeTemplate nodeTemplate, RelationshipTemplate RelationshipTemplate,
TopologyContext toscaTypeFinder);
public void removeEdge(Workflow wf, String from, String to) {
AbstractStep fromStep = wf.getSteps().get(from);
if (fromStep == null) {
throw new InconsistentWorkflowException(String.format(
"Inconsistent workflow: a step nammed '%s' can not be found while it's referenced else where ...", from));
}
AbstractStep toStep = wf.getSteps().get(to);
if (toStep == null) {
throw new InconsistentWorkflowException(String.format(
"Inconsistent workflow: a step nammed '%s' can not be found while it's referenced else where ...", to));
}
fromStep.getFollowingSteps().remove(to);
toStep.getPrecedingSteps().remove(from);
}
public void connectStepFrom(Workflow wf, String stepId, String[] stepNames) {
AbstractStep to = wf.getSteps().get(stepId);
if (to == null) {
throw new InconsistentWorkflowException(String.format(
"Inconsistent workflow: a step nammed '%s' can not be found while it's referenced else where ...", stepId));
}
for (String preceding : stepNames) {
AbstractStep precedingStep = wf.getSteps().get(preceding);
if (precedingStep == null) {
throw new InconsistentWorkflowException(String.format(
"Inconsistent workflow: a step nammed '%s' can not be found while it's referenced else where ...", preceding));
}
WorkflowUtils.linkSteps(precedingStep, to);
}
}
public void connectStepTo(Workflow wf, String stepId, String[] stepNames) {
AbstractStep from = wf.getSteps().get(stepId);
if (from == null) {
throw new InconsistentWorkflowException(String.format(
"Inconsistent workflow: a step nammed '%s' can not be found while it's referenced else where ...", stepId));
}
for (String following : stepNames) {
AbstractStep followingStep = wf.getSteps().get(following);
if (followingStep == null) {
// TODO throw ex
}
WorkflowUtils.linkSteps(from, followingStep);
}
}
// private Set<String> getAllChildrenHierarchy(PaaSNodeTemplate paaSNodeTemplate) {
// Set<String> nodeIds = new HashSet<String>();
// recursivelyPopulateChildrenHierarchy(paaSNodeTemplate, nodeIds);
// return nodeIds;
// }
//
// private void recursivelyPopulateChildrenHierarchy(PaaSNodeTemplate paaSNodeTemplate, Set<String> nodeIds) {
// nodeIds.add(paaSNodeTemplate.getId());
// List<PaaSNodeTemplate> children = paaSNodeTemplate.getChildren();
// if (children != null) {
// for (PaaSNodeTemplate child : children) {
// recursivelyPopulateChildrenHierarchy(child, nodeIds);
// }
// }
// }
protected AbstractStep eventuallyAddStdOperationStep(Workflow wf, AbstractStep lastStep, String nodeId, String operationName,
TopologyContext topologyContext, boolean forceOperation) {
NodeTemplate nodeTemplate = topologyContext.getTopology().getNodeTemplates().get(nodeId);
// FIXME: should we browse hierarchy ?
Operation operation = WorkflowUtils.getOperation(nodeTemplate.getType(), ToscaNodeLifecycleConstants.STANDARD, operationName, topologyContext);
// for compute all std operations are added, for others, only those having artifacts
if ((operation != null && operation.getImplementationArtifact() != null) || forceOperation) {
lastStep = appendOperationStep(wf, lastStep, nodeId, ToscaNodeLifecycleConstants.STANDARD, operationName);
}
return lastStep;
}
protected NodeActivityStep appendStateStep(Workflow wf, AbstractStep lastStep, String nodeId, String stateName) {
NodeActivityStep step = WorkflowUtils.addStateStep(wf, nodeId, stateName);
WorkflowUtils.linkSteps(lastStep, step);
return step;
}
protected NodeActivityStep insertStateStep(Workflow wf, AbstractStep lastStep, String nodeId, String stateName) {
NodeActivityStep step = WorkflowUtils.addStateStep(wf, nodeId, stateName);
WorkflowUtils.linkSteps(step, lastStep);
return step;
}
protected NodeActivityStep addActivityStep(Workflow wf, String nodeId, AbstractActivity activity) {
NodeActivityStep step = new NodeActivityStep();
step.setNodeId(nodeId);
step.setActivity(activity);
step.setName(WorkflowUtils.buildStepName(wf, step, 0));
wf.addStep(step);
return step;
}
protected NodeActivityStep appendOperationStep(Workflow wf, AbstractStep lastStep, String nodeId, String interfaceName, String operationName) {
NodeActivityStep step = WorkflowUtils.addOperationStep(wf, nodeId, interfaceName, operationName);
WorkflowUtils.linkSteps(lastStep, step);
return step;
}
protected NodeActivityStep insertOperationStep(Workflow wf, AbstractStep previousStep, String nodeId, String interfaceName,
String operationName) {
NodeActivityStep step = WorkflowUtils.addOperationStep(wf, nodeId, interfaceName, operationName);
WorkflowUtils.linkSteps(step, previousStep);
return step;
}
protected void unlinkSteps(AbstractStep from, AbstractStep to) {
from.removeFollowing(to.getName());
to.removePreceding(from.getName());
}
protected boolean isOperationStep(NodeActivityStep defaultStep, String interfaceName, String operationName) {
if (defaultStep.getActivity() instanceof OperationCallActivity) {
OperationCallActivity oet = (OperationCallActivity) defaultStep.getActivity();
if (oet.getInterfaceName().equals(interfaceName) && oet.getOperationName().equals(operationName)) {
return true;
}
}
return false;
}
/**
* @param wf
* @param relatedStepId if specified, the step will be added near this one (maybe before)
* @param before if true, the step will be added before the relatedStepId
* @param activity
*/
public void addActivity(Workflow wf, String relatedStepId, boolean before, AbstractActivity activity, TopologyContext topologyContext) {
if (WorkflowUtils.isNativeOrSubstitutionNode(activity.getNodeId(), topologyContext)) {
throw new BadWorkflowOperationException("Activity can not be added for abstract nodes");
}
if (relatedStepId != null) {
if (before) {
// insert
insertActivityStep(wf, relatedStepId, activity);
} else {
// append
appendActivityStep(wf, relatedStepId, activity);
}
} else {
addActivityStep(wf, activity.getNodeId(), activity);
}
}
public void insertActivityStep(Workflow wf, String stepId, AbstractActivity activity) {
AbstractStep lastStep = wf.getSteps().get(stepId);
String stepBeforeId = null;
if (lastStep.getPrecedingSteps() != null && lastStep.getPrecedingSteps().size() == 1) {
stepBeforeId = lastStep.getPrecedingSteps().iterator().next();
}
NodeActivityStep insertedStep = addActivityStep(wf, activity.getNodeId(), activity);
WorkflowUtils.linkSteps(insertedStep, lastStep);
if (stepBeforeId != null) {
AbstractStep stepBefore = wf.getSteps().get(stepBeforeId);
unlinkSteps(stepBefore, lastStep);
WorkflowUtils.linkSteps(stepBefore, insertedStep);
}
}
public void appendActivityStep(Workflow wf, String stepId, AbstractActivity activity) {
AbstractStep lastStep = wf.getSteps().get(stepId);
String stepAfterId = null;
if (lastStep.getFollowingSteps() != null && lastStep.getFollowingSteps().size() == 1) {
stepAfterId = lastStep.getFollowingSteps().iterator().next();
}
NodeActivityStep insertedStep = addActivityStep(wf, activity.getNodeId(), activity);
WorkflowUtils.linkSteps(lastStep, insertedStep);
if (stepAfterId != null) {
AbstractStep stepAfter = wf.getSteps().get(stepAfterId);
unlinkSteps(lastStep, stepAfter);
WorkflowUtils.linkSteps(insertedStep, stepAfter);
}
}
public void removeStep(Workflow wf, String stepId, boolean force) {
AbstractStep step = wf.getSteps().remove(stepId);
if (step == null) {
throw new InconsistentWorkflowException(String.format(
"Inconsistent workflow: a step nammed '%s' can not be found while it's referenced else where ...", stepId));
}
if (!force && step instanceof NodeActivityStep && ((NodeActivityStep) step).getActivity() instanceof DelegateWorkflowActivity) {
throw new BadWorkflowOperationException("Native steps can not be removed from workflow");
}
if (step.getPrecedingSteps() != null) {
if (step.getFollowingSteps() != null) {
// connect all preceding to all following
for (String precedingId : step.getPrecedingSteps()) {
AbstractStep preceding = wf.getSteps().get(precedingId);
for (String followingId : step.getFollowingSteps()) {
AbstractStep following = wf.getSteps().get(followingId);
WorkflowUtils.linkSteps(preceding, following);
}
}
}
for (Object precedingId : step.getPrecedingSteps().toArray()) {
AbstractStep preceding = wf.getSteps().get(precedingId);
unlinkSteps(preceding, step);
}
}
if (step.getFollowingSteps() != null) {
for (Object followingId : step.getFollowingSteps().toArray()) {
AbstractStep following = wf.getSteps().get(followingId);
unlinkSteps(step, following);
}
}
}
public void renameStep(Workflow wf, String stepId, String newStepName) {
if (wf.getSteps().containsKey(newStepName)) {
throw new AlreadyExistException(String.format("A step named ''{0}'' already exists in workflow '%s'", newStepName, wf.getName()));
}
AbstractStep step = wf.getSteps().remove(stepId);
step.setName(newStepName);
wf.getSteps().put(newStepName, step);
// now explore the links
if (step.getPrecedingSteps() != null) {
for (String precedingId : step.getPrecedingSteps()) {
AbstractStep precedingStep = wf.getSteps().get(precedingId);
precedingStep.getFollowingSteps().remove(stepId);
precedingStep.getFollowingSteps().add(newStepName);
}
}
if (step.getFollowingSteps() != null) {
for (String followingId : step.getFollowingSteps()) {
AbstractStep followingStep = wf.getSteps().get(followingId);
followingStep.getPrecedingSteps().remove(stepId);
followingStep.getPrecedingSteps().add(newStepName);
}
}
}
public void removeNode(Workflow wf, String nodeName) {
AbstractStep[] steps = new AbstractStep[wf.getSteps().size()];
steps = wf.getSteps().values().toArray(steps);
for (AbstractStep step : steps) {
if (step instanceof NodeActivityStep && ((NodeActivityStep) step).getNodeId().equals(nodeName)) {
removeStep(wf, step.getName(), true);
}
}
}
/**
* When a relationship is removed, we remove all links between src and target.
* <p>
* TODO : a better implem should be to just remove the links that rely to this relationship. But to do this, we have to associate the link with the
* relationship (when the link is created consecutively to a relationship add).
*
* @param wf
* @param relationhipTarget
*/
public void removeRelationship(Workflow wf, String nodeId, String relationhipTarget) {
Iterator<AbstractStep> steps = wf.getSteps().values().iterator();
while(steps.hasNext()) {
AbstractStep step = steps.next();
if (step instanceof NodeActivityStep && ((NodeActivityStep) step).getNodeId().equals(nodeId)) {
if (step.getFollowingSteps() != null) {
Object[] followingStepIds = step.getFollowingSteps().toArray();
for (Object followingId : followingStepIds) {
AbstractStep followingStep = wf.getSteps().get(followingId);
if (followingStep instanceof NodeActivityStep && ((NodeActivityStep) followingStep).getNodeId().equals(relationhipTarget)) {
unlinkSteps(step, followingStep);
}
}
}
if (step.getPrecedingSteps() != null) {
Object precedings[] = step.getPrecedingSteps().toArray();
for (Object precedingId : precedings) {
AbstractStep precedingStep = wf.getSteps().get(precedingId);
if (precedingStep instanceof NodeActivityStep && ((NodeActivityStep) precedingStep).getNodeId().equals(relationhipTarget)) {
unlinkSteps(precedingStep, step);
}
}
}
}
}
}
/**
* Swap steps means:
* <ul>
* <li>The connection between step and target is inverted.
* <li>All step's predecessors become predecessors of target & vice versa
* <li>All step's followers become followers of target & vice versa
* </ul>
* That's all folks !
*/
public void swapSteps(Workflow wf, String stepId, String targetId) {
AbstractStep step = wf.getSteps().get(stepId);
AbstractStep target = wf.getSteps().get(targetId);
unlinkSteps(step, target);
List<AbstractStep> stepPredecessors = removePredecessors(wf, step);
List<AbstractStep> stepFollowers = removeFollowers(wf, step);
List<AbstractStep> targetPredecessors = removePredecessors(wf, target);
List<AbstractStep> targetFollowers = removeFollowers(wf, target);
associateFollowers(step, targetFollowers);
associateFollowers(target, stepFollowers);
associatePredecessors(step, targetPredecessors);
associatePredecessors(target, stepPredecessors);
WorkflowUtils.linkSteps(target, step);
}
private void associatePredecessors(AbstractStep step, List<AbstractStep> stepPredecessors) {
for (AbstractStep predecessor : stepPredecessors) {
WorkflowUtils.linkSteps(predecessor, step);
}
}
private void associateFollowers(AbstractStep step, List<AbstractStep> stepFollowers) {
for (AbstractStep follower : stepFollowers) {
WorkflowUtils.linkSteps(step, follower);
}
}
private List<AbstractStep> removePredecessors(Workflow wf, AbstractStep step) {
List<AbstractStep> result = Lists.newArrayList();
if (step.getPrecedingSteps() == null || step.getPrecedingSteps().size() == 0) {
return result;
}
Object precedings[] = step.getPrecedingSteps().toArray();
for (Object precedingId : precedings) {
AbstractStep precedingStep = wf.getSteps().get(precedingId);
unlinkSteps(precedingStep, step);
result.add(precedingStep);
}
return result;
}
private List<AbstractStep> removeFollowers(Workflow wf, AbstractStep step) {
List<AbstractStep> result = Lists.newArrayList();
if (step.getFollowingSteps() == null || step.getFollowingSteps().size() == 0) {
return result;
}
Object followings[] = step.getFollowingSteps().toArray();
for (Object followingId : followings) {
AbstractStep followingStep = wf.getSteps().get(followingId);
unlinkSteps(step, followingStep);
result.add(followingStep);
}
return result;
}
public void renameNode(Workflow wf, String oldName, String newName) {
if (wf.getSteps() != null) {
for (AbstractStep step : wf.getSteps().values()) {
if (step instanceof NodeActivityStep && ((NodeActivityStep) step).getNodeId().equals(oldName)) {
((NodeActivityStep) step).setNodeId(newName);
((NodeActivityStep) step).getActivity().setNodeId(newName);
}
}
}
}
public Workflow reinit(Workflow wf, TopologyContext toscaTypeFinder) {
Map<String, AbstractStep> steps = Maps.newHashMap();
wf.setSteps(steps);
if (toscaTypeFinder.getTopology().getNodeTemplates() != null) {
// first stage : add the nodes
for (Entry<String, NodeTemplate> entry : toscaTypeFinder.getTopology().getNodeTemplates().entrySet()) {
String nodeId = entry.getKey();
boolean forceOperation = WorkflowUtils.isComputeOrVolume(nodeId, toscaTypeFinder);
addNode(wf, nodeId, toscaTypeFinder, forceOperation);
}
// second stage : add the relationships
for (Entry<String, NodeTemplate> entry : toscaTypeFinder.getTopology().getNodeTemplates().entrySet()) {
String nodeId = entry.getKey();
if (entry.getValue().getRelationships() != null) {
for (RelationshipTemplate relationshipTemplate : entry.getValue().getRelationships().values()) {
addRelationship(wf, nodeId, entry.getValue(), relationshipTemplate, toscaTypeFinder);
}
}
}
}
return wf;
}
}