private List<String> terminateNodes(Status endStatus) { List<String> endNodes = new ArrayList<String>(); for (Map.Entry<String, NodeInstance> entry : executionPaths.entrySet()) { if (entry.getValue().started) { NodeDef nodeDef = def.getNode(entry.getValue().nodeName); if (!(nodeDef instanceof ControlNodeDef)) { NodeHandler nodeHandler = newInstance(nodeDef.getHandlerClass()); try { if (endStatus == Status.KILLED) { nodeHandler.kill(new Context(nodeDef, entry.getKey(), null)); } else { if (endStatus == Status.FAILED) { nodeHandler.fail(new Context(nodeDef, entry.getKey(), null)); } } endNodes.add(nodeDef.getName()); } catch (Exception ex) { log.warn( XLog.STD, "Error Changing node state to [{0}] for Node [{1}]", endStatus.toString(), nodeDef.getName(), ex); } } } } return endNodes; }
public static String wrapperToXml(DOMWrapper wrapper, boolean ignorePcdata) { try { NodeDef node; switch (wrapper.getType()) { case DOMWrapper.ELEMENT: node = new WrapperElementDef(wrapper, null, null); break; case DOMWrapper.CDATA: node = new CdataDef(wrapper); break; case DOMWrapper.FREETEXT: node = new TextDef(wrapper); break; case DOMWrapper.COMMENT: node = new CommentDef(wrapper); break; default: throw new Error( "unknown node type " + wrapper.getType() + " while converting node to xml"); } StringWriter sw = new StringWriter(); XMLOutput out = new XMLOutput(sw); out.setIgnorePcdata(ignorePcdata); out.setGlob(true); node.displayXML(out, 0); return sw.toString(); } catch (XOMException e) { throw new Error("[" + e.toString() + "] while converting node to xml"); } }
private List<String> killNodes() { List<String> killedNodes = new ArrayList<String>(); for (Map.Entry<String, NodeInstance> entry : executionPaths.entrySet()) { String node = entry.getKey(); NodeInstance nodeInstance = entry.getValue(); if (nodeInstance.started) { NodeDef nodeDef = def.getNode(nodeInstance.nodeName); NodeHandler nodeHandler = newInstance(nodeDef.getHandlerClass()); try { nodeHandler.kill(new Context(nodeDef, node, null)); killedNodes.add(nodeDef.getName()); } catch (Exception ex) { log.warn(XLog.STD, "Error killing node [{0}]", nodeDef.getName(), ex); } } } return killedNodes; }
private String failNode(String nodeName) { String failedNode = null; for (Map.Entry<String, NodeInstance> entry : executionPaths.entrySet()) { String node = entry.getKey(); NodeInstance nodeInstance = entry.getValue(); if (nodeInstance.started && nodeInstance.nodeName.equals(nodeName)) { NodeDef nodeDef = def.getNode(nodeInstance.nodeName); NodeHandler nodeHandler = newInstance(nodeDef.getHandlerClass()); try { nodeHandler.fail(new Context(nodeDef, node, null)); failedNode = nodeDef.getName(); nodeInstance.started = false; } catch (Exception ex) { log.warn(XLog.STD, "Error failing node [{0}]", nodeDef.getName(), ex); } return failedNode; } } return failedNode; }
public synchronized boolean signal(String executionPath, String signalValue) throws WorkflowException { ParamChecker.notEmpty(executionPath, "executionPath"); ParamChecker.notNull(signalValue, "signalValue"); log.debug( XLog.STD, "Signaling job execution path [{0}] signal value [{1}]", executionPath, signalValue); if (status != Status.RUNNING) { throw new WorkflowException(ErrorCode.E0716); } NodeInstance nodeJob = executionPaths.get(executionPath); if (nodeJob == null) { status = Status.FAILED; log.error("invalid execution path [{0}]", executionPath); } NodeDef nodeDef = null; if (!status.isEndState()) { nodeDef = def.getNode(nodeJob.nodeName); if (nodeDef == null) { status = Status.FAILED; log.error("invalid transition [{0}]", nodeJob.nodeName); } } if (!status.isEndState()) { NodeHandler nodeHandler = newInstance(nodeDef.getHandlerClass()); boolean exiting = true; Context context = new Context(nodeDef, executionPath, signalValue); if (!nodeJob.started) { try { nodeHandler.loopDetection(context); exiting = nodeHandler.enter(context); nodeJob.started = true; } catch (WorkflowException ex) { status = Status.FAILED; List<String> killedNodes = terminateNodes(Status.KILLED); if (killedNodes.size() > 1) { log.warn( XLog.STD, "Workflow completed [{0}], killing [{1}] running nodes", status, killedNodes.size()); } throw ex; } } if (exiting) { List<String> pathsToStart = new ArrayList<String>(); List<String> fullTransitions; try { fullTransitions = nodeHandler.multiExit(context); int last = fullTransitions.size() - 1; // TEST THIS if (last >= 0) { String transitionTo = getTransitionNode(fullTransitions.get(last)); if (nodeDef instanceof ForkNodeDef) { transitionTo = "*"; // WF action cannot hold all transitions for a fork. // transitions are hardcoded in the WF app. } persistentVars.put( nodeDef.getName() + WorkflowInstance.NODE_VAR_SEPARATOR + TRANSITION_TO, transitionTo); } } catch (WorkflowException ex) { status = Status.FAILED; throw ex; } if (context.status == Status.KILLED) { status = Status.KILLED; log.debug(XLog.STD, "Completing job, kill node [{0}]", nodeJob.nodeName); } else { if (context.status == Status.FAILED) { status = Status.FAILED; log.debug(XLog.STD, "Completing job, fail node [{0}]", nodeJob.nodeName); } else { if (context.status == Status.SUCCEEDED) { status = Status.SUCCEEDED; log.debug(XLog.STD, "Completing job, end node [{0}]", nodeJob.nodeName); } /* else if (context.status == Status.SUSPENDED) { status = Status.SUSPENDED; log.debug(XLog.STD, "Completing job, end node [{0}]", nodeJob.nodeName); } */ else { for (String fullTransition : fullTransitions) { // this is the whole trick for forking, we need the // executionpath and the transition // in the case of no forking last element of // executionpath is different from transition // in the case of forking they are the same log.debug( XLog.STD, "Exiting node [{0}] with transition[{1}]", nodeJob.nodeName, fullTransition); String execPathFromTransition = getExecutionPath(fullTransition); String transition = getTransitionNode(fullTransition); def.validateTransition(nodeJob.nodeName, transition); NodeInstance nodeJobInPath = executionPaths.get(execPathFromTransition); if ((nodeJobInPath == null) || (!transition.equals(nodeJobInPath.nodeName))) { // TODO explain this IF better // If the WfJob is signaled with the parent // execution executionPath again // The Fork node will execute again.. and replace // the Node WorkflowJobBean // so this is required to prevent that.. // Question : Should we throw an error in this case // ?? executionPaths.put(execPathFromTransition, new NodeInstance(transition)); pathsToStart.add(execPathFromTransition); } } // signal all new synch transitions for (String pathToStart : pathsToStart) { signal(pathToStart, "::synch::"); } } } } } } if (status.isEndState()) { if (status == Status.FAILED) { List<String> failedNodes = terminateNodes(status); log.warn( XLog.STD, "Workflow completed [{0}], failing [{1}] running nodes", status, failedNodes.size()); } else { List<String> killedNodes = terminateNodes(Status.KILLED); if (killedNodes.size() > 1) { log.warn( XLog.STD, "Workflow completed [{0}], killing [{1}] running nodes", status, killedNodes.size()); } } } return status.isEndState(); }