diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilder.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilder.java index 0e8b50bb..fae12b87 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilder.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilder.java @@ -18,6 +18,7 @@ package org.springframework.webflow.engine.builder.xml; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.LinkedList; @@ -32,16 +33,15 @@ import org.springframework.beans.factory.xml.XmlBeanDefinitionReader; import org.springframework.binding.convert.ConversionException; import org.springframework.binding.convert.ConversionExecutor; import org.springframework.binding.convert.ConversionService; +import org.springframework.binding.convert.support.RuntimeBindingConversionExecutor; import org.springframework.binding.expression.EvaluationException; import org.springframework.binding.expression.Expression; import org.springframework.binding.expression.ExpressionParser; import org.springframework.binding.expression.ParserContext; -import org.springframework.binding.expression.support.CollectionAddingExpression; import org.springframework.binding.expression.support.ParserContextImpl; import org.springframework.binding.mapping.AttributeMapper; import org.springframework.binding.mapping.DefaultAttributeMapper; import org.springframework.binding.mapping.Mapping; -import org.springframework.binding.mapping.RequiredMapping; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigUtils; import org.springframework.context.support.GenericApplicationContext; @@ -373,15 +373,86 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde } } + private AttributeMapper parseInputMapper(Element element, Class sourceType, Class targetType) { + Collection inputs = DomUtils.getChildElementsByTagName(element, "input"); + if (inputs.size() == 0) { + return null; + } + DefaultAttributeMapper inputMapper = new DefaultAttributeMapper(); + for (Iterator it = inputs.iterator(); it.hasNext();) { + parseInputMapping((Element) it.next(), inputMapper, sourceType, targetType); + } + return inputMapper; + } + + private AttributeMapper parseOutputMapper(Element element, Class sourceType, Class targetType) { + Collection inputs = DomUtils.getChildElementsByTagName(element, "output"); + if (inputs.size() == 0) { + return null; + } + DefaultAttributeMapper outputMapper = new DefaultAttributeMapper(); + for (Iterator it = inputs.iterator(); it.hasNext();) { + parseOutputMapping((Element) it.next(), outputMapper, sourceType, targetType); + } + return outputMapper; + } + + private Mapping parseInputMapping(Element element, DefaultAttributeMapper mapper, Class sourceClass, + Class targetClass) { + ExpressionParser parser = getLocalContext().getExpressionParser(); + String name = element.getAttribute("name"); + String value = null; + if (element.hasAttribute("value")) { + value = element.getAttribute("value"); + } else { + value = element.getAttribute(name); + } + Expression source = parser.parseExpression(name, new ParserContextImpl().eval(sourceClass)); + Expression target = parser.parseExpression(value, new ParserContextImpl().eval(targetClass)); + return new Mapping(source, target, parseMappingConversionExecutor(element), parseMappingRequired(element)); + } + + private Mapping parseOutputMapping(Element element, DefaultAttributeMapper mapper, Class sourceClass, + Class targetClass) { + ExpressionParser parser = getLocalContext().getExpressionParser(); + String name = element.getAttribute("name"); + String value = null; + if (element.hasAttribute("value")) { + value = element.getAttribute("value"); + } else { + value = element.getAttribute(name); + } + Expression source = parser.parseExpression(value, new ParserContextImpl().eval(sourceClass)); + Expression target = parser.parseExpression(name, new ParserContextImpl().eval(targetClass)); + return new Mapping(source, target, parseMappingConversionExecutor(element), parseMappingRequired(element)); + } + + private ConversionExecutor parseMappingConversionExecutor(Element element) { + if (element.hasAttribute("type")) { + Class type = (Class) fromStringTo(Class.class).execute(element.getAttribute("type")); + return new RuntimeBindingConversionExecutor(type, getConversionService()); + } else { + return null; + } + } + + private boolean parseMappingRequired(Element element) { + if (element.hasAttribute("required")) { + return ((Boolean) fromStringTo(Boolean.class).execute(element.getAttribute("required"))).booleanValue(); + } else { + return false; + } + } + private void parseAndAddStartActions(Element element, Flow flow) { - Element startElement = DomUtils.getChildElementByTagName(element, "start-actions"); + Element startElement = DomUtils.getChildElementByTagName(element, "on-start"); if (startElement != null) { flow.getStartActionList().addAll(parseActions(startElement)); } } private void parseAndAddEndActions(Element element, Flow flow) { - Element endElement = DomUtils.getChildElementByTagName(element, "end-actions"); + Element endElement = DomUtils.getChildElementByTagName(element, "on-end"); if (endElement != null) { flow.getEndActionList().addAll(parseActions(endElement)); } @@ -479,27 +550,6 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde return new SubflowExpression(subflowId, getLocalContext().getFlowDefinitionLocator()); } - private static class SubflowExpression implements Expression { - - private Expression subflowId; - - private FlowDefinitionLocator flowDefinitionLocator; - - public SubflowExpression(Expression subflowId, FlowDefinitionLocator flowDefinitionLocator) { - this.subflowId = subflowId; - this.flowDefinitionLocator = flowDefinitionLocator; - } - - public Object getValue(Object context) throws EvaluationException { - String subflowId = (String) this.subflowId.getValue(context); - return flowDefinitionLocator.getFlowDefinition(subflowId); - } - - public void setValue(Object context, Object value) throws EvaluationException { - throw new UnsupportedOperationException("Cannot set a subflow expression"); - } - } - private void parseAndAddEndState(Element element, Flow flow) { MutableAttributeMap attributes = parseMetaAttributes(element); if (element.hasAttribute("commit")) { @@ -533,7 +583,7 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde } private Action[] parseEntryActions(Element element) { - Element entryActionsElement = DomUtils.getChildElementByTagName(element, "entry-actions"); + Element entryActionsElement = DomUtils.getChildElementByTagName(element, "on-entry"); if (entryActionsElement != null) { return parseActions(entryActionsElement); } else { @@ -580,7 +630,7 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde } private Action[] parseRenderActions(Element element) { - Element renderActionsElement = DomUtils.getChildElementByTagName(element, "render-actions"); + Element renderActionsElement = DomUtils.getChildElementByTagName(element, "on-render"); if (renderActionsElement != null) { return parseActions(renderActionsElement); } else { @@ -589,7 +639,7 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde } private Action[] parseExitActions(Element element) { - Element exitActionsElement = DomUtils.getChildElementByTagName(element, "exit-actions"); + Element exitActionsElement = DomUtils.getChildElementByTagName(element, "on-exit"); if (exitActionsElement != null) { return parseActions(exitActionsElement); } else { @@ -762,77 +812,6 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde } } - private AttributeMapper parseInputMapper(Element element, Class sourceType, Class targetType) { - Element mapperElement = DomUtils.getChildElementByTagName(element, "input-mapper"); - if (mapperElement != null) { - DefaultAttributeMapper mapper = new DefaultAttributeMapper(); - parseMappings(mapper, mapperElement, sourceType, targetType); - return mapper; - } else { - return null; - } - } - - private AttributeMapper parseOutputMapper(Element element, Class sourceType, Class targetType) { - Element mapperElement = DomUtils.getChildElementByTagName(element, "output-mapper"); - if (mapperElement != null) { - DefaultAttributeMapper mapper = new DefaultAttributeMapper(); - parseMappings(mapper, mapperElement, sourceType, targetType); - return mapper; - } else { - return null; - } - } - - private void parseMappings(DefaultAttributeMapper mapper, Element element, Class sourceClass, Class targetClass) { - ExpressionParser parser = getLocalContext().getExpressionParser(); - List mappingElements = DomUtils.getChildElementsByTagName(element, "mapping"); - for (Iterator it = mappingElements.iterator(); it.hasNext();) { - Element mappingElement = (Element) it.next(); - Expression source = parser.parseExpression(mappingElement.getAttribute("source"), new ParserContextImpl() - .eval(sourceClass)); - Expression target = null; - if (StringUtils.hasText(mappingElement.getAttribute("target"))) { - target = parser.parseExpression(mappingElement.getAttribute("target"), new ParserContextImpl() - .eval(targetClass)); - } else if (StringUtils.hasText(mappingElement.getAttribute("target-collection"))) { - target = new CollectionAddingExpression(parser.parseExpression(mappingElement - .getAttribute("target-collection"), new ParserContextImpl().eval(targetClass))); - } - if (getRequiredAttribute(mappingElement, false)) { - mapper.addMapping(new RequiredMapping(source, target, parseTypeConverter(mappingElement))); - } else { - mapper.addMapping(new Mapping(source, target, parseTypeConverter(mappingElement))); - } - } - } - - private ConversionExecutor parseTypeConverter(Element element) { - String from = element.getAttribute("from"); - String to = element.getAttribute("to"); - if (StringUtils.hasText(from)) { - if (StringUtils.hasText(to)) { - ConversionService service = getLocalContext().getConversionService(); - Class sourceClass = (Class) fromStringTo(Class.class).execute(from); - Class targetClass = (Class) fromStringTo(Class.class).execute(to); - return service.getConversionExecutor(sourceClass, targetClass); - } else { - throw new IllegalArgumentException("Use of the 'from' attribute requires use of the 'to' attribute"); - } - } else { - Assert.isTrue(!StringUtils.hasText(to), "Use of the 'to' attribute requires use of the 'from' attribute"); - } - return null; - } - - private boolean getRequiredAttribute(Element element, boolean defaultValue) { - if (StringUtils.hasText(element.getAttribute("required"))) { - return ((Boolean) fromStringTo(Boolean.class).execute(element.getAttribute("required"))).booleanValue(); - } else { - return defaultValue; - } - } - private FlowExecutionExceptionHandler[] parseExceptionHandlers(Element element) { FlowExecutionExceptionHandler[] transitionExecutingHandlers = parseTransitionExecutingExceptionHandlers(element); FlowExecutionExceptionHandler[] customHandlers = parseCustomExceptionHandlers(element); @@ -892,7 +871,7 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde } private void parseAndSetSecuredAttribute(Element element, MutableAttributeMap attributes) { - Element secured = DomUtils.getChildElementByTagName(element, "secured"); + Element secured = DomUtils.getChildElementByTagName(element, SecurityRule.SECURITY_AUTHORITY_ATTRIBUTE_NAME); if (secured != null) { SecurityRule rule = new SecurityRule(); rule.setRequiredAuthorities(SecurityRule.convertAuthoritiesFromCommaSeparatedString(secured @@ -906,7 +885,7 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde // default to any rule.setComparisonType(SecurityRule.COMPARISON_ANY); } - attributes.put("secured", rule); + attributes.put(SecurityRule.SECURITY_AUTHORITY_ATTRIBUTE_NAME, rule); } } @@ -942,6 +921,27 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde } } + private static class SubflowExpression implements Expression { + + private Expression subflowId; + + private FlowDefinitionLocator flowDefinitionLocator; + + public SubflowExpression(Expression subflowId, FlowDefinitionLocator flowDefinitionLocator) { + this.subflowId = subflowId; + this.flowDefinitionLocator = flowDefinitionLocator; + } + + public Object getValue(Object context) throws EvaluationException { + String subflowId = (String) this.subflowId.getValue(context); + return flowDefinitionLocator.getFlowDefinition(subflowId); + } + + public void setValue(Object context, Object value) throws EvaluationException { + throw new UnsupportedOperationException("Cannot set a subflow expression"); + } + } + public String toString() { return new ToStringCreator(this).append("location", resource).toString(); } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/spring-webflow-2.0.xsd b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/spring-webflow-2.0.xsd index 01e40059..262de194 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/spring-webflow-2.0.xsd +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/spring-webflow-2.0.xsd @@ -1,6 +1,5 @@ - @@ -11,19 +10,18 @@ Spring Web Flow Schema Authors: Keith Donald, Erwin Vervaet
-This Schema defines the Spring Web Flow (SWF) XML syntax. +This schema defines Spring Web Flow's XML-based flow definition language.
-The root "flow" element of this document defines exactly one flow definition. -A flow definition is a blueprint for an executable task that involves a -single user (aka conversation or dialog). +The root "flow" element in this document defines exactly one flow definition. +A flow definition is a blueprint for a carrying out a conversation with a single user.
A flow is composed of one or more states that form the steps of the flow. -Each state executes behavior when entered. What behavior is executed is a +Each state executes a behavior when entered. What behavior is executed is a function of the state's type. Core state types include view states, action states, subflow states, decision states, and end states.
-Each flow definition must specify exactly one start state. -Events that occur in transitionable states drive state transitions. +A flow definition has exactly one start state. +Events that occur within states drive state transitions. ]]> @@ -42,7 +40,15 @@ The unique identifier of this state; must be unique to this flow. - + + + + + + + + + @@ -58,7 +64,7 @@ Goes out of scope when a single call into this flow completes. @@ -67,7 +73,7 @@ Goes out of scope when the next user event is signaled into this flow. @@ -76,7 +82,7 @@ Goes out of scope when this local flow session ends. @@ -90,7 +96,7 @@ Goes out of scope when the overall governing flow execution ends. @@ -99,7 +105,7 @@ Evaluates an arbitrary expression aganst the flow request context. @@ -109,15 +115,7 @@ Multiple fragments may be specified using a comma delimiter. -For example: -
-	<set attribute="fileUploaded" scope="flash" value="true"/>
-
-The above example instructs this flow to set the "fileUploaded" attribute in "flash scope" to "true". -This action always returns a "success" event unless an exception is thrown. +Sets an attribute value in a scope. ]]>
@@ -130,16 +128,16 @@ This action always returns a "success" event unless an exception is thrown. A flow may also exhibit the following characteristics:
  • Be annotated with attributes that define descriptive properties that may affect flow execution. (See the <attribute/> element) -
  • Secured flow +
  • Be secured (See the <secured/> element)
  • Be a persistence context for managing persistent objects during the course of flow execution. @@ -149,24 +147,23 @@ A flow may also exhibit the following characteristics: (See the <var/> element)
  • Map input provided by callers that start it -(See the <input-mapper/> element) +(See the <input/> element)
  • Return output to callers that end it. -(See the <output-mapper/> element) +(See the <output/> element) -
  • Execute custom behaviors at start time and end time. -(See the <start-actions/> and <end-actions/> elements) +
  • Execute actions at start time and end time. +(See the <on-start/> and <on-end/> elements)
  • Define transitions shared by all states. (See the <global-transitions/> element) -
  • Handle exceptions thrown by its states during execution. +
  • Handle exceptions thrown by during flow execution. (See the <exception-handler/> element)
  • Import one or more local bean definition files defining custom flow artifacts (such as actions, exception handlers, view factories, transition criteria, etc). -(See the <import/> element) - +(See the <bean-import/> element)
]]>
@@ -177,7 +174,7 @@ A flow may also exhibit the following characteristics: @@ -186,7 +183,7 @@ An attribute describing this flow. @@ -195,9 +192,9 @@ Indicates this flow is secured The persistence context can be referenced from within this flow by the "entityManager" variable. ]]> @@ -207,33 +204,26 @@ The persistence context can be referenced from within this flow by the "entityMa - - + -
  • The 'source' of each mapping is a Map containing all the input provided by the caller -that launched this flow. -
  • The 'target' of each mapping is this flow execution's RequestContext, exposing access to -data structures such as 'flowScope'. - +Maps a single input attribute provided by the caller into this flow. ]]> - + @@ -243,10 +233,10 @@ Defines flow startup logic to execute. This logic will always execute when this @@ -255,21 +245,18 @@ is driven by the result of action execution (e.g. success, error). -Once paused a view-state may be 'refreshed' by the user, for example, when the -browser refresh button is clicked. A refresh causes the response to be reissued, -at which point control goes back to the user. +Once paused, a view-state may be 'refreshed' by the user. +A refresh causes the response to be reissued and then returns control back to the user.
    -A view state may be configured with one or more <render-action/> elements. Render -actions are executed before the view is rendered. Such actions are often -idempotent and execute without side effects. +A view state may be configured with one or more render-actions using the 'on-render' element. +Render actions are executed immediately before the view is rendered.
    -A view state is a transitionable state. A view state transition is triggered by a -user input event. +A view state is a transitionable state. +A view state transition is triggered by a user event. ]]>
    @@ -278,21 +265,19 @@ user input event. -A decision state is a transitionable state. A decision state transition can be triggered by -evaluating a boolean expression against the flow execution request context. To -define expressions, use the 'if' element. +A decision state is a transitionable state. +A decision state transition can be triggered by evaluating a boolean expression against the flow execution request context. +To define transition expressions, use the 'if' element.
    Examples:
    A simple boolean expression test, using the convenient 'if' element:
         <decision-state id="requiresShipping">
    -	    <if test="flowScope.sale.shipping" then="enterShippingDetails" else="processSale"/>
    +	    <if test="#{sale.requiresShipping}" then="enterShippingDetails" else="processSale"/>
         </decision-state>
     
    ]]> @@ -303,17 +288,11 @@ A simple boolean expression test, using the convenient 'if' element: -A subflow state is a transitionable state. A state transition is triggered by a -subflow result event, which describes the logical subflow outcome that occurred. Typically the -criteria for this transition is the id of the subflow end state that was entered. -
    -While the subflow is active, this flow is suspended waiting for the subflow to complete execution. -When the subflow completes execution by reaching an end state, this state is expected -to respond to the result of that execution. The result of subflow execution, the end state -that was reached, should be used as grounds for a transition out of this state. +A subflow state is a transitionable state. +A transition is triggered by the subflow outcome that was reached. ]]>
    @@ -322,16 +301,13 @@ that was reached, should be used as grounds for a transition out of this state. -A end state is not transitionable--there are never transitions out of an end state. +An end state is not transitionable; there are never transitions out of an end state. When an end-state is entered, an instance of this flow is terminated.
    -When this flow terminates, if it was acting as the "root" or top-level flow the entire -execution (conversation) is terminated. If this flow was acting as a subflow the subflow -session ends and the calling parent session resumes. To resume, the parent session -responds to the result of the subflow, typically by reasoning on the id of the end -state that was reached. +When this flow terminates, if it was the "root" flow the entire execution is terminated. +If this flow was a subflow, its parent flow resumes. ]]>
    @@ -341,54 +317,44 @@ state that was reached.
    - + - - - - - - - -For the output mapper the following mapping characteristics apply: -
      -
    • The 'source' of each mapping is this flow execution's RequestContext, exposing access to -internal data structures such as 'flowScope'. -
    • The 'target' of each mapping is the flow output map that will contain the output returned to the -caller that launched this flow. -
    +Actions to execute when this flow ends. ]]>
    +
    + + + + + + - + @@ -398,8 +364,7 @@ definition resource location. @@ -407,89 +372,89 @@ If not specified, the default start state is the first state defined in the docu - - + - - - - - - - - - - - - - - - + - + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -498,7 +463,7 @@ evaluates to null an error will be reported. @@ -513,12 +478,12 @@ The name of this attribute. - + @@ -527,7 +492,7 @@ string may be an alias (e.g 'int') or a fully-qualified class (e.g. 'java.lang.I @@ -549,20 +514,17 @@ The value of this attribute; a short-cut alternative to an explicit child 'value -When specified without the 'class' or 'bean' attributes, this name is also used as the bean name -of a non-singleton bean in the configured Bean Factory to use as the initial variable value. +The name of this variable. ]]> - + @@ -578,8 +540,6 @@ This scope of this variable. The available scope types are:
  • flow - The variable goes out of scope when this flow session ends.
  • conversation - The variable goes out of scope when the overall conversation governing this flow ends. -
    -If not specified the default scope type is used ('flow' by default). ]]> @@ -587,7 +547,7 @@ If not specified the default scope type is used ('flow' by default). - + @@ -602,7 +562,7 @@ If not specified the default scope type is used ('flow' by default). @@ -611,16 +571,16 @@ An attribute describing this state. - + @@ -630,20 +590,18 @@ Defines state entry logic to be executed. This logic will always execute when th -A transition defines a supported path through the flow. Transitions may be annotated with attributes -and may execute one or more actions before executing. +A path from this state to another state triggered by an event. +Transitions may execute one or more actions. All transition actions must execute successfully for the transition itself to complete. +If no transition target is specified, the transition acts as a simple event handler and does not change the state of the flow. ]]> - + @@ -651,9 +609,8 @@ regardless of what transition is executed. - @@ -666,30 +623,30 @@ execution of this flow definition. Exception handlers may be attached at the sta - + - + - + @@ -699,7 +656,7 @@ desired result type, a type conversion will be attempted. - + - + - + @@ -751,36 +707,32 @@ An attribute describing this transition. - + +The criteria that determines when this transition should execute. The most basic value is a static event id:
     	<transition on="submit" to="state"/>
     
    -... which reads "on an occurrence of the 'submit' event transition to 'state'" +... which reads "on the occurrence of the 'submit' event, transition to 'state'"
    -Sophisticated transitional expressions are also supported when enclosed within ${brackets}: +Sophisticated transitional expressions are also supported when enclosed in a delimited expression:
    -	<transition on="${#result == 'submit' &;amp;& flowScope.attribute == 'foo'}" to="state"/>
    +	<transition on="#{event == 'submit' &;amp;& flowScope.attribute == 'foo'}" to="state"/>
     
    -Custom transition criteria implementations can be referenced by id: +For exotic usage scenarios, custom TransitionCriteria beans can be plugged in as follows:
    -	<transition on="bean:myCustomCriteria" to="state"/>
    +	<transition on="bean:myCustomCriteriaBean" to="state"/>
     
    -The exact interpretation of this attribute value depends on the TextToTransitionCriteria -converter that is installed. ]]>
    @@ -789,28 +741,23 @@ converter that is installed. -The value must be a fully-qualified Exception class name (e.g. example.booking.ItineraryExpiredException). -When an exception is thrown, superclasses of the configured exception class match by default. -
    -Use of this attribute results in an exception handler being attached to the object associated -with this transition definition. Use this attribute or the 'on' attribute, not both. +The value of this attribute must be a fully-qualified java.lang.Exception class name (e.g. example.booking.ItineraryExpiredException). +Superclasses of the configured exception class match by default. Use this attribute or the 'on' attribute, not both. ]]>
    - + -The value of this attribute may be a static state identifier (e.g. to="displayForm") -or an expression to be evaluated at runtime against the request context -(e.g. to="${flowScope.previousViewState}"). Custom target state resolvers implementations -can be referenced by id (e.g. to="bean:myCustomTargetStateResolver"). The -exact interpretation of this attribute value depends on the installed TextToTargetStateResolver. +The value of this attribute may be a static state identifier (e.g. to="displayForm") or a dynamic eval expression (e.g. to="#{flowScope.previousViewState}"). +For exotic usage scenarios, custom target state resolver beans can also be plugged in (e.g. to="bean:myCustomTargetStateResolver"). +If no value is specified, this transition acts as a simple event handler and will not change the state of the flow. ]]> @@ -827,7 +774,7 @@ exact interpretation of this attribute value depends on the installed TextToTarg @@ -836,7 +783,7 @@ An attribute describing this state. @@ -845,101 +792,86 @@ Indicates this state is secured
    - + - + - - - - - - - - -A transition defines a supported path through the flow. Transitions may be annotated with attributes -and may execute one or more actions before executing. -]]> - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + - + -This value may be a logical application view name which gets resolved to a template: +The name of the view to render. Is typically a flow-relative path to a view template: +
    +    priceForm.jsp
    +
    +Can also be an evaluatable expression:
    -	priceForm
    +	${flowScope.myViewExpression}
     
    -It may even be a direct pointer to a view template: +Use the externalRedirect: prefix to redirect to an external URL, typically to interface with an external system. +External redirect query parameters may be specified using ${expressions} that evaluate against the request context:
    -	/WEB-INF/jsp/priceForm.jsp
    -
    -This value may also be a view name expression evaluated against the request context: -
    -	${flowScope.view}
    -
    -Use of the "externalRedirect:" prefix indicates this view state should trigger a -redirect to an absolute external URL, typically to interface with an external system. -External redirect query parameters may be specified using ${expressions} that evaluate -against the request context: -
    -	externalRedirect:/someOtherSystem.htm?orderId=${flowScope.order.id}
    +	externalRedirect:/http://someOtherSystem?orderId=${flowScope.order.id}&callbackUrl=#{flowExecutionUrl}
     
    -Use of the "flowRedirect:" prefix requests a redirect to a new execution of another flow: +Use the flowRedirect: prefix to redirect to another flow:
    -	flowRedirect:myOtherFlow?someData=${flowScope.data}
    +	flowRedirect:myOtherFlow?someData=#{flowScope.data}
     
    -Use of the "bean:" prefix references a custom ViewFactory implementation you define, -exposed by id in either a flow-local context using the "import" element or in the parent -context. +For exotic usages, use the bean: prefix to plug in a custom ViewFactory bean you define.
     	bean:myCustomViewFactory
     

    -Note when no view name is provided, the view to render will be determined by convention. +When this attribute is not specified, the view to render will be determined by convention. The default convention is to treat the id of this view state as the view identifier. ]]>
    @@ -949,8 +881,7 @@ The default convention is to treat the id of this view state as the view identif @@ -959,8 +890,7 @@ Default is false. @@ -970,19 +900,19 @@ Default is false.
    - + - + - + @@ -997,7 +927,7 @@ Default is false. @@ -1006,16 +936,16 @@ An attribute describing this state. - + @@ -1024,25 +954,22 @@ Defines state entry logic to be executed. This logic will always execute when th The form is:
    -	<if test="${criteria}" then="trueStateId" else="falseStateId"/>
    +	<if test="#{criteria}" then="trueStateId" else="falseStateId"/>
     
    ]]>
    - + @@ -1051,8 +978,7 @@ regardless of what transition is executed. @@ -1065,48 +991,33 @@ execution of this flow definition. Exception handlers may be attached at the sta - + +The boolean expression to test. For example:
    -	<if test="${flowScope.sale.shipping} then="enterShippingDetails"/>
    -	<if test="${lastEvent.id == 'search'} then="bindSearchParameters"/>
    +	<if test="#{sale.requiresShipping} then="enterShippingDetails"/>
     
    ]]>
    - + -The value of this attribute may be a static state identifier (e.g. then="displayForm") -or an expression to be evaluated at runtime against the request context -(e.g. then="${flowScope.previousViewState}"). Custom target state resolvers implementations -can be referenced by id (e.g. to="bean:myCustomTargetStateResolver"). The -exact interpretation of this attribute value depends on the installed TextToTargetStateResolver. +The state to transition to if the boolean expression evaluates to true. ]]> - + @@ -1123,7 +1034,7 @@ exact interpretation of this attribute value depends on the installed TextToTarg @@ -1132,89 +1043,73 @@ An attribute describing this state.
    - + - + -
  • The 'source' of each mapping is this flow execution's RequestContext, exposing access to -internal data structures such as 'flowScope'. -
  • The 'target' of each mapping is the input map that will be passed to the subflow. - +An input to pass the subflow. ]]> - + -For the output mapper the following mapping characteristics apply: -
      -
    • The 'source' of each mapping is a Map containing all the output returned by the subflow. -
    • The 'target' of each mapping is this flow execution's RequestContext, exposing access to -data structures such as 'flowScope'. -
    +An output from the subflow to process. ]]>
    - - - - -A transition defines a supported path through the flow. Transitions may be annotated with attributes -and may specify one or more actions to execute before executing. -]]> - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - @@ -1223,8 +1118,8 @@ The id of the flow to be spawned as a subflow when this subflow state is entered @@ -1252,34 +1147,25 @@ An attribute describing this state.
    - + - + -For the output mapper the following mapping characteristics apply: -
      -
    • The 'source' of each mapping is this flow execution's RequestContext, exposing access to -internal data structures such as 'flowScope'. -
    • The 'target' of each mapping is the flow output map that will contain the output returned to the -caller that launched this flow. -
    +An output attribute to return when this flow ends by reaching this state. ]]>
    @@ -1288,8 +1174,7 @@ caller that launched this flow. @@ -1299,47 +1184,31 @@ execution of this flow definition. Exception handlers may be attached at the sta -This value may be a logical application view name which gets resolved to a template: -
    -	priceForm
    -
    -It may even be a direct pointer to a view template: -
    -	/WEB-INF/jsp/priceForm.jsp
    -
    -This value may also be a view name expression evaluated against the request context: -
    -	${flowScope.view}
    -
    -Use of the "externalRedirect:" prefix triggers a redirect to a specific "after conversation completion" -external URL: -
    -    externalRedirect:/home.html
    -
    -Redirect query parameters may also be specified using ${expressions} that evaluate against -the request context: -
    -    externalRedirect:/thankyou.htm?confirmationNumber=${flowScope.order.confirmation.id}
    -
    -Use of the "flowRedirect:" prefix indicates this end state should trigger a redirect that -starts another flow. Flow input parameters may be specified using ${expressions} that -evaluate against the request context: -
    -	flowRedirect:search-flow?firstName=${flowScope.searchCriteria.firstName}
    -
    -Use of the "bean:" prefix references a custom ViewSelector implementation you define, -exposed by id in either a flow-local context using the "import" element or in the parent -context. -
    -    bean:myCustomViewSelector
    -
    -The exact semantics regarding the interpretation of this value are determined by the -installed TextToViewSelector converter. -
    -Note when no view name is provided, this end state will issue no final response. In this case, -it is expected the calling flow controller will handle issuing the final response. +The name of a view to render as a final flow response. +Is often not specified when the caller handles this flow outcome and cares for the response. +When specified, the value can be a flow-relative path to a view template: +
    +    priceForm.jsp
    +
    +It can also be an evaluatable expression: +
    +	#{flowScope.myViewExpression}
    +
    +Use the externalRedirect: prefix to redirect to an external URL: +
    +	externalRedirect:/someController?orderId=#{order.id}
    +
    +Use the flowRedirect: prefix to redirect to another flow: +
    +	flowRedirect:myOtherFlow?someData=#{flowScope.data}
    +
    +For exotic usages, use the bean: prefix to plug in a custom ViewFactory bean you define: +
    +	bean:myCustomViewFactory
    +
    +
    +When this attribute is not specified, no final response will be issued. In this case, +the caller is expected to handle this flow outcome. ]]>
    @@ -1348,7 +1217,7 @@ it is expected the calling flow controller will handle issuing the final respons @@ -1367,10 +1236,9 @@ if the flow is not a persistence-context. -A transition defines a supported path through the flow. Transitions may be annotated with attributes -and may execute one or more actions before executing. +A global transition defines a path through the flow that can be taken from all states. +A global transition may execute one or more actions before executing. +All transition actions must execute successfully for the transition itself to execute. ]]> @@ -1379,37 +1247,19 @@ and may execute one or more actions before executing.
    - + - - - - - - - - - - - - - - @@ -1417,18 +1267,18 @@ The bean id of a custom exception handler implementation to attach. - + For example:
    -    <import resource="orderitem-flow-beans.xml"/>
    +    <bean-import resource="orderitem-flow-beans.xml"/>
     
    ... would look for 'orderitem-flow-beans.xml' in the same directory as this document. ]]> @@ -1442,8 +1292,8 @@ For example: @@ -1452,16 +1302,16 @@ If the security requirement is not met, an AccessDeniedException is thrown.
    - + @@ -1471,7 +1321,7 @@ Method used to match authorities. Accepted values are 'any' and 'all'. Default @@ -1480,7 +1330,7 @@ Matching any of the authorities will allow access diff --git a/spring-webflow/src/test/java/org/springframework/webflow/test/SearchFlowExecutionTests.java b/spring-webflow/src/test/java/org/springframework/webflow/test/SearchFlowExecutionTests.java index 0226c3e5..261d3551 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/test/SearchFlowExecutionTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/test/SearchFlowExecutionTests.java @@ -24,9 +24,9 @@ import org.springframework.webflow.config.FlowDefinitionResource; import org.springframework.webflow.config.FlowDefinitionResourceFactory; import org.springframework.webflow.context.ExternalContext; import org.springframework.webflow.core.collection.AttributeMap; -import org.springframework.webflow.core.expression.el.WebFlowELExpressionParser; import org.springframework.webflow.engine.EndState; import org.springframework.webflow.engine.Flow; +import org.springframework.webflow.expression.el.WebFlowELExpressionParser; import org.springframework.webflow.test.execution.AbstractXmlFlowExecutionTests; /** diff --git a/spring-webflow/src/test/java/org/springframework/webflow/test/search-flow.xml b/spring-webflow/src/test/java/org/springframework/webflow/test/search-flow.xml index fbb2ff2e..0544c993 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/test/search-flow.xml +++ b/spring-webflow/src/test/java/org/springframework/webflow/test/search-flow.xml @@ -2,31 +2,29 @@ - + - + - + - + - + - - - - + + - + \ No newline at end of file