public TemplateModel get()

in core/src/main/java/org/apache/brooklyn/core/workflow/WorkflowExpressionResolution.java [145:251]


        public TemplateModel get(String key) throws TemplateModelException {
            List<Throwable> errors = MutableList.of();

            if ("workflow".equals(key)) {
                return new WorkflowExplicitModel();
            }
            if ("entity".equals(key)) {
                Entity entity = context.getEntity();
                if (entity!=null) {
                    return TemplateProcessor.EntityAndMapTemplateModel.forEntity(entity, null);
                }
            }

            if ("output".equals(key)) {
                if (context.getOutput()!=null) return TemplateProcessor.wrapAsTemplateModel(context.getOutput());
                if (context.currentStepInstance!=null && context.currentStepInstance.getOutput() !=null) return TemplateProcessor.wrapAsTemplateModel(context.currentStepInstance.getOutput());
                Object previousStepOutput = context.getPreviousStepOutput();
                if (previousStepOutput!=null) return TemplateProcessor.wrapAsTemplateModel(previousStepOutput);
                return ifNoMatches();
            }

            Object candidate = null;

            if (stage.after(WorkflowExpressionStage.STEP_PRE_INPUT)) {
                //somevar -> workflow.current_step.output.somevar
                WorkflowStepInstanceExecutionContext currentStep = context.currentStepInstance;
                if (currentStep != null && stage.after(WorkflowExpressionStage.STEP_OUTPUT)) {
                    if (currentStep.getOutput() instanceof Map) {
                        candidate = ((Map) currentStep.getOutput()).get(key);
                        if (candidate != null) return TemplateProcessor.wrapAsTemplateModel(candidate);
                    }
                }

                //somevar -> workflow.current_step.input.somevar
                try {
                    if (currentStep!=null) {
                        candidate = currentStep.getInput(key, Object.class);
                    }
                } catch (Throwable t) {
                    Exceptions.propagateIfFatal(t);
                    if (stage==WorkflowExpressionStage.STEP_INPUT && WorkflowVariableResolutionStackEntry.isStackForSettingVariable(RESOLVE_STACK.getAll(true), key) && Exceptions.getFirstThrowableOfType(t, WorkflowVariableRecursiveReference.class)!=null) {

                        // input evaluation can look at local input, and will gracefully handle some recursive references.
                        // this is needed so we can handle things like env:=${env} in input, and also {message:="Hi ${name}", name:="Bob"}.
                        // but there are
                        // if we have a chain input1:=input2, and input input2:=input1 with both defined on step and on workflow
                        //
                        // (a) eval of either will give recursive reference error and allow retry immediately;
                        //     then it's a bit weird, inconsistent, step input1 will resolve to local input2 which resolves as global input1;
                        //     but step input2 will resolve to local input1 which this time will resolve as global input2.
                        //     and whichever is invoked first will cause both to be stored as resolved, so if input2 resolved first then
                        //     step input1 subsequently returns global input2.
                        //
                        // (b) recursive reference error only recoverable at the outermost stage,
                        //     so step input1 = global input2, step input2 = global input1,
                        //     prevents inconsistency but blocks useful things, eg log ${message} wrapped with message:="Hi ${name}",
                        //     then invoked with name: "person who says ${message}" to refer to a previous step's message,
                        //     or even name:="Mr ${name}" to refer to an outer variable.
                        //     in this case if name is resolved first then message resolves as Hi Mr X, but if message resolved first
                        //     it only recovers when resolving message which would become "Hi X", and if message:="${greeting} ${name}"
                        //     then it fails to find a local ${greeting}. (with strategy (a) these both do what is expected.)
                        //
                        // (to handle this we include stage in the stack, needed in both cases above)
                        //
                        // ideally we would know which vars are from a wrapper, but that info is lost when we build up the step
                        //
                        // (c) we could just fail fast, disallow the nice things we wanted, require explicit
                        //
                        // (d) we could fail in edge cases, so the obvious cases above work as expected, but anything more sophisticated, eg A calling B calling A, will fail
                        //
                        // settled on (d) effectively; we allow local references, and fail on recursive references, with exceptions.
                        // the main exception, handled here, is if we are setting an input
                        candidate = null;
                        errors.add(t);
                    } else {
                        throw Exceptions.propagate(t);
                    }
                }
                if (candidate != null) return TemplateProcessor.wrapAsTemplateModel(candidate);
            }

            //workflow.previous_step.output.somevar
            if (stage.after(WorkflowExpressionStage.WORKFLOW_INPUT)) {
                Object prevStepOutput = context.getPreviousStepOutput();
                if (prevStepOutput instanceof Map) {
                    candidate = ((Map) prevStepOutput).get(key);
                    if (candidate != null) return TemplateProcessor.wrapAsTemplateModel(candidate);
                }
            }

            //workflow.scratch.somevar
            if (stage.after(WorkflowExpressionStage.WORKFLOW_INPUT)) {
                candidate = context.getWorkflowScratchVariables().get(key);
                if (candidate != null) return TemplateProcessor.wrapAsTemplateModel(candidate);
            }

            //workflow.input.somevar
            if (context.input.containsKey(key)) {
                candidate = context.getInput(key);
                // the subtlety around step input above doesn't apply here as workflow inputs are not resolved with freemarker
                if (candidate != null) return TemplateProcessor.wrapAsTemplateModel(candidate);
            }

            if (!errors.isEmpty()) Exceptions.propagate("Errors resolving "+key, errors);

            return ifNoMatches();
        }