/** * Factory method that creates a new subflow state, a state where a parent flow spawns another flow as a subflow. * This method is an atomic operation that returns a fully initialized state. It encapsulates the selection of the * subflow state implementation as well as the state assembly. * @param id the identifier to assign to the state, must be unique to its owning flow (required) * @param flow the flow that will own (contain) this state (required) * @param entryActions any state entry actions; may be null * @param subflow the subflow definition (required) * @param attributeMapper the subflow input and output attribute mapper; may be null * @param transitions any transitions (paths) out of this state * @param exceptionHandlers any exception handlers; may be null * @param exitActions any state exit actions; may be null * @param attributes attributes to assign to the State, which may also be used to affect state construction; may be * null * @return the fully initialized subflow state instance */ public State createSubflowState(String id, Flow flow, Action[] entryActions, Expression subflow, SubflowAttributeMapper attributeMapper, Transition[] transitions, FlowExecutionExceptionHandler[] exceptionHandlers, Action[] exitActions, AttributeMap<?> attributes) { SubflowState subflowState = new SubflowState(flow, id, subflow); if (attributeMapper != null) { subflowState.setAttributeMapper(attributeMapper); } configureCommonProperties(subflowState, entryActions, transitions, exceptionHandlers, exitActions, attributes); return subflowState; }
/** * Create a new subflow state. * @param flow the owning flow * @param id the state identifier (must be unique to the flow) * @param subflow the subflow to spawn * @throws IllegalArgumentException when this state cannot be added to given flow, e.g. because the id is not unique * @see #setAttributeMapper(SubflowAttributeMapper) */ public SubflowState(Flow flow, String id, Expression subflow) throws IllegalArgumentException { super(flow, id); setSubflow(subflow); }
/** * Create subflow state. * * @param flow the flow * @param id the id * @param subflow the subflow * @param entryAction the entry action * @return the subflow state */ protected SubflowState createSubflowState(final Flow flow, final String id, final String subflow, final Action entryAction) { final SubflowState state = new SubflowState(flow, id, new BasicSubflowExpression(subflow)); if (entryAction != null) { state.getEntryActionList().add(entryAction); } return state; }
val inputMapper = createMapperToSubflowState(mappings); val subflowMapper = createSubflowAttributeMapper(inputMapper, null); subflowState.setAttributeMapper(subflowMapper); LOGGER.trace("Creating transitions to subflow state [{}]", subflowState.getId()); val transitionSet = subflowState.getTransitionSet(); transitionSet.add(createTransition(CasWebflowConstants.TRANSITION_ID_SUCCESS, targetSuccessId)); transitionSet.add(createTransition(CasWebflowConstants.TRANSITION_ID_SUCCESS_WITH_WARNINGS, targetWarningsId));
/** * Create multi factor parent subflow state definitions. * * @param flow the flow * @param id the id */ protected void createMultiFactorParentSubflowStateDefinitions(final Flow flow, final String id) { final EvaluateAction action = createEvaluateAction("generateMfaCredentialsAction"); final SubflowState subflowState = createSubflowState(flow, id, id, action); final List<DefaultMapping> mappings = new ArrayList<>(); mappings.add(createMappingToSubflowState("mfaCredentials", "flowScope.mfaCredentials", true, MultiFactorCredentials.class)); mappings.add(createMappingToSubflowState("mfaService", "flowScope.service", true, MultiFactorAuthenticationSupportingWebApplicationService.class)); final Mapper inputMapper = createMapperToSubflowState(mappings); final SubflowAttributeMapper subflowMapper = createSubflowAttributeMapper(inputMapper, null); subflowState.setAttributeMapper(subflowMapper); final ActionState actionState = (ActionState) flow.getState(STATE_DEFINITION_ID_REAL_SUBMIT); final String targettedStateId = actionState.getTransition(SUCCESS_EVENT_ID).getTargetStateId(); subflowState.getTransitionSet().add(createTransition(MFA_SUCCESS_EVENT_ID, targettedStateId)); subflowState.getTransitionSet().add(createTransition(UNKNOWN_PRINCIPAL_ERROR_EVENT_ID, "viewUnknownPrincipalErrorView")); subflowState.getTransitionSet().add(createTransition(MFA_UNRECOGNIZED_AUTHN_METHOD_ERROR_EVENT_ID, "viewMfaUnrecognizedAuthnMethodErrorView")); }
@SuppressWarnings("unchecked") public void testReturnWithOutput() { subflowState.setAttributeMapper(new SubflowAttributeMapper() { public MutableAttributeMap<Object> createSubflowInput(RequestContext context) { return new LocalAttributeMap<>(); } public void mapSubflowOutput(AttributeMap<?> flowOutput, RequestContext context) { assertEquals("bar", flowOutput.get("foo")); } }); subflowState.getTransitionSet().add(new Transition(on("end"), to("whatev"))); new State(parentFlow, "whatev") { protected void doEnter(RequestControlContext context) throws FlowExecutionException { } }; new EndState(subflow, "end"); subflow.setOutputMapper((source, target) -> { MutableAttributeMap<Object> map = (MutableAttributeMap<Object>) target; map.put("foo", "bar"); return new DefaultMappingResults(source, target, Collections.emptyList()); }); subflowState.enter(context); assertEquals("parent", context.getActiveFlow().getId()); }
public void testEnterEndStateTerminateFlowSession() { final Flow subflow = new Flow("mySubflow"); EndState state = new EndState(subflow, "end"); MockFlowSession session = new MockFlowSession(subflow); Flow parent = new Flow("parent"); SubflowState subflowState = new SubflowState(parent, "subflow", new AbstractGetValueExpression() { public Object getValue(Object context) throws EvaluationException { return subflow; } }); subflowState.getTransitionSet().add(new Transition(on("end"), to("end"))); new EndState(parent, "end"); MockFlowSession parentSession = new MockFlowSession(parent); parentSession.setState(subflowState); session.setParent(parentSession); MockRequestControlContext context = new MockRequestControlContext(new MockFlowExecutionContext(session)); state.enter(context); assertFalse("Active", context.getFlowExecutionContext().isActive()); }
@SuppressWarnings("unchecked") public void testEnterWithInput() { subflowState.setAttributeMapper(new SubflowAttributeMapper() { public MutableAttributeMap<Object> createSubflowInput(RequestContext context) { return new LocalAttributeMap<>("foo", "bar"); } public void mapSubflowOutput(AttributeMap<?> flowOutput, RequestContext context) { } }); subflow.setInputMapper((source, target) -> { MutableAttributeMap<Object> map = (MutableAttributeMap<Object>) source; assertEquals("bar", map.get("foo")); return new DefaultMappingResults(source, target, Collections.emptyList()); }); new State(subflow, "whatev") { protected void doEnter(RequestControlContext context) throws FlowExecutionException { } }; subflowState.enter(context); assertEquals("child", context.getActiveFlow().getId()); }
public void setUp() { parentFlow = new Flow("parent"); subflow = new Flow("child"); subflowState = new SubflowState(parentFlow, "subflow", new AbstractGetValueExpression() { public Object getValue(Object context) throws EvaluationException { return subflow; } }); context = new MockRequestControlContext(parentFlow); context.setCurrentState(subflowState); }
/** * Map the output data produced by the subflow back into the request context (typically flow scope). */ private void mapSubflowOutput(AttributeMap subflowOutput, RequestContext context) { if (getAttributeMapper() != null) { if (logger.isDebugEnabled()) { logger .debug("Messaging the configured attribute mapper to map subflow result attributes to the " + "resuming parent flow -- It will have access to attributes passed up by the completed subflow"); } attributeMapper.mapFlowOutput(subflowOutput, context); } else { if (logger.isDebugEnabled()) { logger .debug("No attribute mapper is configured for the resuming subflow state '" + getId() + "' -- As a result, no attributes of the ending flow will be passed to the resuming parent flow"); } } }
/** * Create subflow state. * * @param flow the flow * @param id the id * @param subflow the subflow * @param entryAction the entry action * @return the subflow state */ protected SubflowState createSubflowState(final Flow flow, final String id, final String subflow, final Action entryAction) { final SubflowState state = new SubflowState(flow, id, new BasicSubflowExpression(subflow, this.loginFlowDefinitionRegistry)); if (entryAction != null) { state.getEntryActionList().add(entryAction); } return state; }
/** * Create the input data map for the spawned subflow session. The returned map will be passed to * {@link Flow#start(RequestControlContext, MutableAttributeMap)}. */ protected MutableAttributeMap createSubflowInput(RequestContext context) { if (getAttributeMapper() != null) { if (logger.isDebugEnabled()) { logger.debug("Messaging the configured attribute mapper to map attributes " + "down to the spawned subflow for access within the subflow"); } return getAttributeMapper().createFlowInput(context); } else { if (logger.isDebugEnabled()) { logger.debug("No attribute mapper configured for this subflow state '" + getId() + "' -- As a result, no attributes will be passed to the spawned subflow '" + subflow.getId() + "'"); } return null; } }
/** * Factory method that creates a new subflow state, a state where a parent flow spawns another flow as a subflow. * This method is an atomic operation that returns a fully initialized state. It encapsulates the selection of the * subflow state implementation as well as the state assembly. * @param id the identifier to assign to the state, must be unique to its owning flow (required) * @param flow the flow that will own (contain) this state (required) * @param entryActions any state entry actions; may be null * @param subflow the subflow definition (required) * @param attributeMapper the subflow input and output attribute mapper; may be null * @param transitions any transitions (paths) out of this state * @param exceptionHandlers any exception handlers; may be null * @param exitActions any state exit actions; may be null * @param attributes attributes to assign to the State, which may also be used to affect state construction; may be * null * @return the fully initialized subflow state instance */ public State createSubflowState(String id, Flow flow, Action[] entryActions, Expression subflow, SubflowAttributeMapper attributeMapper, Transition[] transitions, FlowExecutionExceptionHandler[] exceptionHandlers, Action[] exitActions, AttributeMap attributes) { SubflowState subflowState = new SubflowState(flow, id, subflow); if (attributeMapper != null) { subflowState.setAttributeMapper(attributeMapper); } configureCommonProperties(subflowState, entryActions, transitions, exceptionHandlers, exitActions, attributes); return subflowState; }
@Override public SubflowState createSubflowState(final Flow flow, final String id, final String subflow, final Action entryAction) { if (containsFlowState(flow, id)) { LOGGER.trace("Flow [{}] already contains a definition for state id [{}]", flow.getId(), id); return getTransitionableState(flow, id, SubflowState.class); } val state = new SubflowState(flow, id, new BasicSubflowExpression(subflow, this.loginFlowDefinitionRegistry)); if (entryAction != null) { state.getEntryActionList().add(entryAction); } return state; }
/** * Create a new subflow state. * @param flow the owning flow * @param id the state identifier (must be unique to the flow) * @param subflow the subflow to spawn * @throws IllegalArgumentException when this state cannot be added to given flow, e.g. because the id is not unique * @see #setAttributeMapper(SubflowAttributeMapper) */ public SubflowState(Flow flow, String id, Expression subflow) throws IllegalArgumentException { super(flow, id); setSubflow(subflow); }
/** * Factory method that creates a new subflow state, a state where a parent flow spawns another flow as a subflow. * This method is an atomic operation that returns a fully initialized state. It encapsulates the selection of the * subflow state implementation as well as the state assembly. * @param id the identifier to assign to the state, must be unique to its owning flow (required) * @param flow the flow that will own (contain) this state (required) * @param entryActions any state entry actions; may be null * @param subflow the subflow definition (required) * @param attributeMapper the subflow input and output attribute mapper; may be null * @param transitions any transitions (paths) out of this state * @param exceptionHandlers any exception handlers; may be null * @param exitActions any state exit actions; may be null * @param attributes attributes to assign to the State, which may also be used to affect state construction; may be * null * @return the fully initialized subflow state instance */ public State createSubflowState(String id, Flow flow, Action[] entryActions, Expression subflow, SubflowAttributeMapper attributeMapper, Transition[] transitions, FlowExecutionExceptionHandler[] exceptionHandlers, Action[] exitActions, AttributeMap<?> attributes) { SubflowState subflowState = new SubflowState(flow, id, subflow); if (attributeMapper != null) { subflowState.setAttributeMapper(attributeMapper); } configureCommonProperties(subflowState, entryActions, transitions, exceptionHandlers, exitActions, attributes); return subflowState; }
/** * Create a new subflow state. * @param flow the owning flow * @param id the state identifier (must be unique to the flow) * @param subflow the subflow to spawn * @throws IllegalArgumentException when this state cannot be added to given flow, e.g. because the id is not unique * @see #setAttributeMapper(SubflowAttributeMapper) */ public SubflowState(Flow flow, String id, Expression subflow) throws IllegalArgumentException { super(flow, id); setSubflow(subflow); }
/** * Factory method that creates a new subflow state, a state where a parent flow spawns another flow as a subflow. * This method is an atomic operation that returns a fully initialized state. It encapsulates the selection of the * subflow state implementation as well as the state assembly. * @param id the identifier to assign to the state, must be unique to its owning flow (required) * @param flow the flow that will own (contain) this state (required) * @param entryActions any state entry actions; may be null * @param subflow the subflow definition (required) * @param attributeMapper the subflow input and output attribute mapper; may be null * @param transitions any transitions (paths) out of this state * @param exceptionHandlers any exception handlers; may be null * @param exitActions any state exit actions; may be null * @param attributes attributes to assign to the State, which may also be used to affect state construction; may be * null * @return the fully initialized subflow state instance * @throws FlowArtifactLookupException an exception occured creating the state */ public State createSubflowState(String id, Flow flow, Action[] entryActions, Flow subflow, FlowAttributeMapper attributeMapper, Transition[] transitions, FlowExecutionExceptionHandler[] exceptionHandlers, Action[] exitActions, AttributeMap attributes) throws FlowArtifactLookupException { SubflowState subflowState = new SubflowState(flow, id, subflow); if (attributeMapper != null) { subflowState.setAttributeMapper(attributeMapper); } configureCommonProperties(subflowState, entryActions, transitions, exceptionHandlers, exitActions, attributes); return subflowState; }
/** * Create a new subflow state. * @param flow the owning flow * @param id the state identifier (must be unique to the flow) * @param subflow the subflow to spawn * @throws IllegalArgumentException when this state cannot be added to given flow, e.g. because the id is not unique * @see #setAttributeMapper(FlowAttributeMapper) */ public SubflowState(Flow flow, String id, Flow subflow) throws IllegalArgumentException { super(flow, id); setSubflow(subflow); }