package eu.dnetlib.manager.wf.workflows.graph; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; import com.google.common.collect.Sets; import eu.dnetlib.errors.WorkflowManagerException; import eu.dnetlib.manager.wf.model.GraphArc; import eu.dnetlib.manager.wf.model.GraphParameter; import eu.dnetlib.manager.wf.model.GraphParameterValue; import eu.dnetlib.manager.wf.workflows.util.NodeHelper; @Service public class GraphLoader { private static final Log log = LogFactory.getLog(GraphLoader.class); private final String regExRef = "\\$\\{(\\w*)\\}"; final Pattern pattern = Pattern.compile(regExRef, Pattern.MULTILINE); @Autowired private NodeHelper nodeHelper; @Autowired private Environment env; public Graph loadGraph(final List workflowGraph, final Map globalParams) throws WorkflowManagerException { final Graph graph = new Graph(); for (final eu.dnetlib.manager.wf.model.GraphNode node : workflowGraph) { final String nodeName = node.getName(); final String nodeType = node.getType(); final boolean isStart = node.isStart(); final boolean isJoin = node.isJoin(); final Map params = calculateParamsForNode(node, globalParams); if (isStart) { graph.addNode(GraphNode.newStartNode(nodeName, nodeType, params)); } else if (isJoin) { graph.addNode(GraphNode.newJoinNode(nodeName, nodeType, params)); } else { graph.addNode(GraphNode.newNode(nodeName, nodeType, params)); } if (graph.getArcs() != null) { for (final GraphArc a : node.getArcs()) { final String arcName = a.getName(); final String to = a.getTo(); graph.addArc(new Arc(StringUtils.isNotBlank(arcName) ? arcName : Arc.DEFAULT_ARC, nodeName, to)); } } graph.addNode(GraphNode.newSuccessNode()); } checkValidity(graph); return graph; } public Map calculateParamsForNode(final eu.dnetlib.manager.wf.model.GraphNode node, final Map globalParams) { final Map params = new HashMap<>(); if (node.getParams() != null) { for (final GraphParameter p : node.getParams()) { final String pName = p.getName(); final GraphNodeParameter pValue = calculateSimpleValue(p, globalParams); if (pValue != null) { params.put(pName, pValue); } else if (p.getMap() != null) { final Map map = p.getMap() .entrySet() .stream() .collect(Collectors.toMap(e -> e.getKey(), e -> { final GraphNodeParameter gnp = calculateSimpleValue(e.getValue(), globalParams); if (gnp == null) { final String msg = String.format("missing value for param: \"%s\"", e.getKey()); log.debug(msg); return GraphNodeParameter.newNullParam(); } return gnp; })); params.put(pName, GraphNodeParameter.newMapParam(map)); } else if (p.getValues() != null) { final List list = p.getValues() .stream() .map(e -> calculateSimpleValue(e, globalParams)) .collect(Collectors.toList()); params.put(pName, GraphNodeParameter.newListParam(list)); } } } return params; } private GraphNodeParameter calculateSimpleValue(final GraphParameterValue graphValue, final Map globalParams) { String value = graphValue.getValue(); final String ref = graphValue.getRef(); final String prop = graphValue.getProperty(); final String envRef = graphValue.getEnv(); if (StringUtils.isNotBlank(ref) && StringUtils.isNotBlank(globalParams.get(ref))) { return GraphNodeParameter.newSimpleParam(globalParams.get(ref)); } else if (StringUtils.isNotBlank(envRef)) { return GraphNodeParameter.newEnvParam(envRef); } else if (StringUtils.isNotBlank(value)) { final Matcher matcher = pattern.matcher(value); while (matcher.find()) { final String rName = matcher.group(1); final String rValue = globalParams.get(rName); if (StringUtils.isBlank(rValue)) { return null; } value = value.replaceAll(Pattern.quote(matcher.group(0)), rValue); System.out.println("NEW VALUE " + value); } return GraphNodeParameter.newSimpleParam(value); } else if (StringUtils.isNotBlank(prop)) { return GraphNodeParameter.newSimpleParam(env.getProperty(prop)); } else { return null; } } private void checkValidity(final Graph graph) throws WorkflowManagerException { final Set nodesFromArcs = new HashSet<>(); boolean foundSuccess = false; boolean foundStart = false; for (final Arc arc : graph.getArcs()) { if (StringUtils.isBlank(arc.getFrom()) || StringUtils.isBlank(arc.getFrom())) { throw new WorkflowManagerException("Invalid arc: missing from e/o to"); } if (StringUtils.equals(arc.getTo(), GraphNode.SUCCESS_NODE)) { foundSuccess = true; } nodesFromArcs.add(arc.getFrom()); nodesFromArcs.add(arc.getTo()); } if (!foundSuccess) { throw new WorkflowManagerException("Arc to success not found"); } final Set diff = Sets.symmetricDifference(graph.nodeNames(), nodesFromArcs); if (!diff.isEmpty()) { throw new WorkflowManagerException("Missing or invalid nodes in arcs: " + diff); } for (final GraphNode n : graph.nodes()) { if (StringUtils.isBlank(n.getName())) { throw new WorkflowManagerException("Invalid node: missing name"); } if (n.isStart()) { foundStart = true; } if (!this.nodeHelper.isValidType(n.getType())) { throw new WorkflowManagerException("Invalid node type: " + n.getType()); } } if (!foundStart) { throw new WorkflowManagerException("Start node not found"); } } }