public WorkflowGraph build()

in flux/src/main/java/software/amazon/aws/clients/swf/flux/wf/graph/WorkflowGraphBuilder.java [442:534]


    public WorkflowGraph build() {
        WorkflowStep actualFirstStep = firstStep;
        // if we have any pre-workflow hooks, insert the pre-workflow-hook anchor step and assign the workflow hooks to it.
        List<WorkflowStepHook> preWorkflowHooks = workflowHooksByType.get(StepHook.HookType.PRE);
        if (preWorkflowHooks != null && !preWorkflowHooks.isEmpty()) {
            actualFirstStep = new PreWorkflowHookAnchor();
            addStep(actualFirstStep);
            alwaysTransition(actualFirstStep, firstStep);
            addStepHooks(PreWorkflowHookAnchor.class, preWorkflowHooks);
        }

        // verify that all of the steps in stepImpls have behavior defined in the steps map
        if (!steps.keySet().containsAll(stepImpls.keySet())) {
            Set<Class<? extends WorkflowStep>> diff = new HashSet<>(stepImpls.keySet());
            diff.removeAll(steps.keySet());

            Set<String> names = diff.stream().map(Class::getSimpleName).collect(Collectors.toSet());
            throw new WorkflowGraphBuildException("Found one or more steps with undefined behavior: " + String.join(", ", names));
        }

        // if we have any post-workflow hooks, add the post-workflow-hook anchor step and assign the workflow hooks to it.
        // we will add transitions to it below when we encounter CloseWorkflow transitions.
        List<WorkflowStepHook> postWorkflowHooks = workflowHooksByType.get(StepHook.HookType.POST);
        if (postWorkflowHooks != null && !postWorkflowHooks.isEmpty()) {
            addStep(new PostWorkflowHookAnchor());
            addStepHooks(PostWorkflowHookAnchor.class, postWorkflowHooks);

            // we'll force all of the CloseWorkflow transitions to PostWorkflowHookAnchor instead
            for (Class<? extends WorkflowStep> step : steps.keySet()) {
                for (String resultCode : steps.get(step).keySet()) {
                    if (steps.get(step).get(resultCode) == CloseWorkflow.class) {
                        steps.get(step).put(resultCode, PostWorkflowHookAnchor.class);
                    }
                }
            }

            // finally we'll add in the transition from the anchor to CloseWorkflow.
            alwaysTransition(PostWorkflowHookAnchor.class, CloseWorkflow.class);
        }

        Map<Class<? extends WorkflowStep>, WorkflowGraphNode> nodes = new HashMap<>();
        for (Entry<Class<? extends WorkflowStep>, WorkflowStep> entry : stepImpls.entrySet()) {
            nodes.put(entry.getKey(), new WorkflowGraphNodeImpl(entry.getValue()));
        }

        Set<Class<? extends WorkflowStep>> reachable = new HashSet<>();
        reachable.add(actualFirstStep.getClass()); // the first step is always reachable

        for (Class<? extends WorkflowStep> step : steps.keySet()) {
            WorkflowGraphNode node = nodes.get(step);
            // now look up the nodes for each of the transitions defined for this step,
            // and add a transition entry for it to this step's node
            for (String resultCode : steps.get(step).keySet()) {
                Class<? extends WorkflowStep> nextStep = steps.get(step).get(resultCode);
                if (CloseWorkflow.class.isAssignableFrom(nextStep)) {
                    // if the transition is to CloseWorkflow, we put a null transition in so we end the workflow
                    node.addTransition(resultCode, null);
                } else if (!nodes.containsKey(nextStep)) {
                    throw new WorkflowGraphBuildException("A transition to step " + nextStep.getSimpleName()
                                                          + " was defined from " + step.getSimpleName()
                                                          + " but no behavior was specified for " + nextStep.getSimpleName());
                } else {
                    WorkflowGraphNode nextNode = nodes.get(nextStep);
                    node.addTransition(resultCode, nextNode);
                    reachable.add(nextNode.getStep().getClass());
                }
            }
        }

        // verify that there are no unreachable nodes in the graph
        if (!reachable.containsAll(steps.keySet())) {
            Set<Class<? extends WorkflowStep>> diff = new HashSet<>(steps.keySet());
            diff.removeAll(reachable);

            Set<String> names = diff.stream().map(Class::getSimpleName).collect(Collectors.toSet());
            throw new WorkflowGraphBuildException("Found one or more unreachable steps: " + String.join(", ", names));
        }

        verifyNoLoops(actualFirstStep.getClass(), nodes, new HashSet<>());

        // now we'll loop through hooksForAllSteps and add each entry to every step
        if (!hooksForAllSteps.isEmpty()) {
            for (Class<? extends WorkflowStep> step : steps.keySet()) {
                addStepHooks(step, hooksForAllSteps);
            }
        }

        if (shouldValidateAttributeAvailability) {
            validateAttributeAvailability(actualFirstStep, nodes, initialAttributes, stepHooks);
        }

        return new WorkflowGraphImpl(actualFirstStep, nodes, stepHooks);
    }