replaced action/bean-action/evaluate-action with evaluate/render/set
introduced render renamed set.attribute to set.name renamed evaluate.type to evaluate.result-type made subflow.state subflow dynamic w/ late binding added end-state.commit meta attribute
This commit is contained in:
@@ -10,7 +10,9 @@
|
||||
<classpathentry kind="lib" path="lib/buildtime/cglib.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/clover.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/commons-beanutils.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/commons-collections.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/commons-digester.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/commons-lang.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/commons-fileupload.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/commons-logging.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/commons-validator.jar"/>
|
||||
@@ -21,12 +23,16 @@
|
||||
<classpathentry kind="lib" path="lib/buildtime/el-api.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/hibernate.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/hsqldb.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/log4j.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/jboss-el.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/jta.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/junit.jar"/>
|
||||
<classpathentry kind="lib" path="lib/global/ognl.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/openjpa.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/oro.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/persistence-api.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/portlet-api.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/serp.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/servlet-api.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/spring-aop.jar"/>
|
||||
<classpathentry kind="lib" path="lib/global/spring-beans.jar"/>
|
||||
@@ -35,6 +41,7 @@
|
||||
<classpathentry kind="lib" path="lib/global/spring-core.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/spring-jdbc.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/spring-orm.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/spring-security-core.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/spring-test.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/spring-tx.jar"/>
|
||||
<classpathentry kind="lib" path="lib/global/spring-web.jar"/>
|
||||
@@ -42,11 +49,5 @@
|
||||
<classpathentry kind="lib" path="lib/buildtime/spring-webmvc-portlet.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/struts.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/xalan.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/openjpa.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/commons-lang.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/serp.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/commons-collections.jar"/>
|
||||
<classpathentry kind="lib" path="lib/test/jboss-el.jar"/>
|
||||
<classpathentry kind="lib" path="lib/buildtime/spring-security-core.jar"/>
|
||||
<classpathentry kind="output" path="target/classes"/>
|
||||
</classpath>
|
||||
|
||||
@@ -183,9 +183,6 @@ public abstract class AbstractAction implements Action, InitializingBean {
|
||||
}
|
||||
|
||||
public final Event execute(RequestContext context) throws Exception {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Action '" + getActionNameForLogging() + "' beginning execution");
|
||||
}
|
||||
Event result = doPreExecute(context);
|
||||
if (result == null) {
|
||||
result = doExecute(context);
|
||||
|
||||
@@ -18,13 +18,10 @@ package org.springframework.webflow.action;
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.binding.convert.ConversionService;
|
||||
import org.springframework.binding.convert.support.DefaultConversionService;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.ScopeType;
|
||||
|
||||
/**
|
||||
* Specifies how an action result value should be exposed to an executing flow. The return value is exposed as an
|
||||
@@ -38,57 +35,48 @@ import org.springframework.webflow.execution.ScopeType;
|
||||
public class ActionResultExposer implements Serializable {
|
||||
|
||||
/**
|
||||
* The name of the attribute to index the return value with.
|
||||
* The expression to set the result to.
|
||||
*/
|
||||
private Expression nameExpression;
|
||||
|
||||
/**
|
||||
* The scope of the attribute indexing the return value.
|
||||
*/
|
||||
private ScopeType resultScope;
|
||||
private Expression resultExpression;
|
||||
|
||||
/**
|
||||
* The desired type to expose the result as
|
||||
*/
|
||||
private Class desiredResultType;
|
||||
private Class expectedResultType;
|
||||
|
||||
/**
|
||||
* The {@link ConversionService} to use to convert the result to the desired type
|
||||
*/
|
||||
private ConversionService conversionService = new DefaultConversionService();
|
||||
private ConversionService conversionService;
|
||||
|
||||
/**
|
||||
* Creates a action result exposer
|
||||
* @param nameExpression the result name
|
||||
* @param resultScope the result scope
|
||||
* @param desiredResultType the desired result type
|
||||
* @param resultExpression the result expression
|
||||
* @param expectedResultType the expected result type
|
||||
*/
|
||||
public ActionResultExposer(Expression nameExpression, ScopeType resultScope, Class desiredResultType) {
|
||||
Assert.notNull(nameExpression, "The result name is required");
|
||||
this.nameExpression = nameExpression;
|
||||
this.resultScope = resultScope;
|
||||
this.desiredResultType = desiredResultType;
|
||||
public ActionResultExposer(Expression resultExpression, Class expectedResultType,
|
||||
ConversionService conversionService) {
|
||||
Assert.notNull(resultExpression, "The result expression is required");
|
||||
this.resultExpression = resultExpression;
|
||||
this.expectedResultType = expectedResultType;
|
||||
if (this.expectedResultType != null) {
|
||||
Assert.notNull(conversionService, "A conversionService is required with an expectedResultType");
|
||||
this.conversionService = conversionService;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of the attribute to index the return value with.
|
||||
*/
|
||||
public Expression getNameExpression() {
|
||||
return nameExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the scope the attribute indexing the return value.
|
||||
*/
|
||||
public ScopeType getResultScope() {
|
||||
return resultScope;
|
||||
return resultExpression;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the desired result type to be exposed
|
||||
*/
|
||||
public Class getDesiredResultType() {
|
||||
return desiredResultType;
|
||||
public Class getExpectedResultType() {
|
||||
return expectedResultType;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -97,34 +85,22 @@ public class ActionResultExposer implements Serializable {
|
||||
* @param context the request context
|
||||
*/
|
||||
public void exposeResult(Object result, RequestContext context) {
|
||||
if (resultScope != null) {
|
||||
MutableAttributeMap scopeMap = resultScope.getScope(context);
|
||||
nameExpression.setValue(scopeMap, applyTypeConversion(result, desiredResultType));
|
||||
} else {
|
||||
nameExpression.setValue(context, applyTypeConversion(result, desiredResultType));
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("resultName", nameExpression).append("resultScope", resultScope)
|
||||
.toString();
|
||||
resultExpression.setValue(context, applyTypeConversion(result));
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply type conversion on the supplied value
|
||||
*
|
||||
* @param value the raw value to be converted
|
||||
* @param targetType the target type for the conversion
|
||||
* @return the converted result
|
||||
*/
|
||||
protected Object applyTypeConversion(Object value, Class targetType) {
|
||||
if (value == null || targetType == null) {
|
||||
private Object applyTypeConversion(Object value) {
|
||||
if (expectedResultType == null) {
|
||||
return value;
|
||||
}
|
||||
return conversionService.getConversionExecutor(value.getClass(), targetType).execute(value);
|
||||
return conversionService.getConversionExecutor(value.getClass(), expectedResultType).execute(value);
|
||||
}
|
||||
|
||||
public void setConversionService(ConversionService conversionService) {
|
||||
this.conversionService = conversionService;
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("resultExpression", resultExpression).append("expectedResultType",
|
||||
expectedResultType).toString();
|
||||
}
|
||||
}
|
||||
@@ -52,16 +52,8 @@ public class EvaluateAction extends AbstractAction {
|
||||
|
||||
/**
|
||||
* Create a new evaluate action.
|
||||
* @param expression the expression to evaluate
|
||||
*/
|
||||
public EvaluateAction(Expression expression) {
|
||||
this(expression, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new evaluate action.
|
||||
* @param expression the expression to evaluate
|
||||
* @param evaluationResultExposer the strategy for how the expression result will be exposed to the flow
|
||||
* @param expression the expression to evaluate (required)
|
||||
* @param evaluationResultExposer the strategy for how the expression result will be exposed to the flow (optional)
|
||||
*/
|
||||
public EvaluateAction(Expression expression, ActionResultExposer evaluationResultExposer) {
|
||||
Assert.notNull(expression, "The expression this action should evaluate is required");
|
||||
@@ -69,6 +61,14 @@ public class EvaluateAction extends AbstractAction {
|
||||
this.evaluationResultExposer = evaluationResultExposer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a custom result event factory selector
|
||||
* @param factorySelector the factor for creating the evaluation action result event
|
||||
*/
|
||||
public void setResultEventFactorySelector(ResultEventFactorySelector factorySelector) {
|
||||
this.resultEventFactorySelector = factorySelector;
|
||||
}
|
||||
|
||||
protected Event doExecute(RequestContext context) throws Exception {
|
||||
Object result = expression.getValue(context);
|
||||
if (evaluationResultExposer != null) {
|
||||
|
||||
@@ -480,7 +480,7 @@ public class FormAction extends MultiAction implements InitializingBean {
|
||||
* @param context the action execution context, for accessing and setting data in "flow scope" or "request scope"
|
||||
* @return "success" when binding and validation is successful, "error" if there were binding and/or validation
|
||||
* errors
|
||||
* @throws Exception an <b>unrecoverable</b> exception occured, either checked or unchecked
|
||||
* @throws Exception an <b>unrecoverable</b> exception occurred, either checked or unchecked
|
||||
*/
|
||||
public Event bindAndValidate(RequestContext context) throws Exception {
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2004-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.webflow.action;
|
||||
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.View;
|
||||
|
||||
/**
|
||||
* An action that sets a special attribute that views use to render partial views called "fragments", instead of the
|
||||
* entire view.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public class RenderAction extends AbstractAction {
|
||||
|
||||
/**
|
||||
* The expression for setting the scoped attribute value.
|
||||
*/
|
||||
private Expression[] fragmentExpressions;
|
||||
|
||||
/**
|
||||
* Creates a new render action.
|
||||
* @param fragmentExpressions the set of expressions to resolve the view fragments to render
|
||||
*/
|
||||
public RenderAction(Expression[] fragmentExpressions) {
|
||||
if (fragmentExpressions == null || fragmentExpressions.length == 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"You must provide at least one fragment expression to this render action");
|
||||
}
|
||||
this.fragmentExpressions = fragmentExpressions;
|
||||
}
|
||||
|
||||
protected Event doExecute(RequestContext context) throws Exception {
|
||||
String[] fragments = new String[fragmentExpressions.length];
|
||||
for (int i = 0; i < fragmentExpressions.length; i++) {
|
||||
Expression exp = fragmentExpressions[i];
|
||||
fragments[i] = (String) exp.getValue(context);
|
||||
}
|
||||
context.getFlashScope().put(View.RENDER_FRAGMENTS_ATTRIBUTE, fragments);
|
||||
return success();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,7 +17,6 @@ package org.springframework.webflow.action;
|
||||
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.ScopeType;
|
||||
@@ -32,12 +31,7 @@ public class SetAction extends AbstractAction {
|
||||
/**
|
||||
* The expression for setting the scoped attribute value.
|
||||
*/
|
||||
private Expression attributeExpression;
|
||||
|
||||
/**
|
||||
* The target scope.
|
||||
*/
|
||||
private ScopeType scope;
|
||||
private Expression nameExpression;
|
||||
|
||||
/**
|
||||
* The expression for resolving the scoped attribute value.
|
||||
@@ -46,26 +40,19 @@ public class SetAction extends AbstractAction {
|
||||
|
||||
/**
|
||||
* Creates a new set attribute action.
|
||||
* @param attributeExpression the writeable attribute expression
|
||||
* @param scope the target scope of the attribute
|
||||
* @param valueExpression the evaluatable attribute value expression
|
||||
* @param nameExpression the name of the property to set
|
||||
* @param valueExpression the expression to obtain the new property value
|
||||
*/
|
||||
public SetAction(Expression attributeExpression, ScopeType scope, Expression valueExpression) {
|
||||
Assert.notNull(attributeExpression, "The attribute expression is required");
|
||||
public SetAction(Expression nameExpression, Expression valueExpression) {
|
||||
Assert.notNull(nameExpression, "The name expression is required");
|
||||
Assert.notNull(valueExpression, "The value expression is required");
|
||||
this.attributeExpression = attributeExpression;
|
||||
this.scope = scope;
|
||||
this.nameExpression = nameExpression;
|
||||
this.valueExpression = valueExpression;
|
||||
}
|
||||
|
||||
protected Event doExecute(RequestContext context) throws Exception {
|
||||
Object value = valueExpression.getValue(context);
|
||||
if (scope != null) {
|
||||
MutableAttributeMap scopeMap = scope.getScope(context);
|
||||
attributeExpression.setValue(scopeMap, value);
|
||||
} else {
|
||||
attributeExpression.setValue(context, value);
|
||||
}
|
||||
nameExpression.setValue(context, value);
|
||||
return success();
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright 2004-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.webflow.engine;
|
||||
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
|
||||
/**
|
||||
* A service interface that maps attributes between two flows. Used by the subflow state to map attributes between a
|
||||
* parent flow and its sub flow.
|
||||
* <p>
|
||||
* An attribute mapper may map attributes of a parent flow down to a child flow as <i>input</i> when the child is
|
||||
* spawned as a subflow. In addition, a mapper may map output attributes of a subflow into a resuming parent flow as
|
||||
* <i>output</i> when the child session ends and control is returned to the parent flow.
|
||||
* <p>
|
||||
* For example, say you have the following parent flow session:
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* Parent Flow Session
|
||||
* -------------------
|
||||
* -> flow = myFlow
|
||||
* -> flowScope = [map-> attribute1=value1, attribute2=value2, attribute3=value3]
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* For the "Parent Flow Session" above, there are 3 attributes in flow scope ("attribute1", "attribute2" and
|
||||
* "attribute3", respectively). Any of these three attributes may be mapped as input down to child subflows when those
|
||||
* subflows are spawned. An implementation of this interface performs the actual mapping, encapsulating knowledge of
|
||||
* <i>which</i> attributes should be mapped, and <i>how</i> they will be mapped (for example, will the same attribute
|
||||
* names be used between flows or not?).
|
||||
* <p>
|
||||
* For example:
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* Flow Attribute Mapper Configuration
|
||||
* -----------------------------------
|
||||
* -> inputMappings = [map-> flowScope.attribute1->attribute1, flowScope.attribute3->attribute4]
|
||||
* -> outputMappings = [map-> attribute4->flowScope.attribute3]
|
||||
* </pre>
|
||||
*
|
||||
* <p>
|
||||
* The above example "Flow Attribute Mapper" specifies <code>inputMappings</code> that define which parent attributes
|
||||
* to map as input to the child. In this case, two attributes in flow scope of the parent are mapped, "attribute1" and
|
||||
* "attribute3". "attribute1" is mapped with the name "attribute1" (given the same name in both flows), while
|
||||
* "attribute3" is mapped to "attribute4", given a different name that is local to the child flow.
|
||||
* <p>
|
||||
* Likewise, when a child flow ends the <code>outputMappings</code> define which output attributes to map into the
|
||||
* parent. In this case the subflow output attribute "attribute4" will be mapped up to the parent as "attribute3",
|
||||
* updating the value of "attribute3" in the parent's flow scope. Note: only output attributes exposed by the end state
|
||||
* of the ending subflow are eligible for mapping.
|
||||
* <p>
|
||||
* A FlowAttributeMapper is typically implemented using 2 distinct
|
||||
* {@link org.springframework.binding.mapping.AttributeMapper} implementations: one responsible for input mapping and
|
||||
* one taking care of output mapping.
|
||||
* <p>
|
||||
* Note: because FlowAttributeMappers are singletons, take care not to store and/or modify caller-specific state in a
|
||||
* unsafe manner. The FlowAttributeMapper methods run in an independently executing thread on each invocation so make
|
||||
* sure you deal only with local data or internal, thread-safe services.
|
||||
*
|
||||
* @see org.springframework.webflow.engine.SubflowState
|
||||
* @see org.springframework.binding.mapping.AttributeMapper
|
||||
*
|
||||
* @author Keith Donald
|
||||
* @author Erwin Vervaet
|
||||
*/
|
||||
public interface FlowAttributeMapper {
|
||||
|
||||
/**
|
||||
* Create a map of attributes that should be passed as <i>input</i> to a spawning flow.
|
||||
* <p>
|
||||
* Attributes set in the map returned by this method are availale as input to the subflow when its session is
|
||||
* spawned.
|
||||
* @param context the current request execution context, which gives access to the parent flow scope, the request
|
||||
* scope, any event parameters, etcetera
|
||||
* @return a map of attributes (name=value pairs) to pass as input to the spawning subflow
|
||||
*/
|
||||
public MutableAttributeMap createFlowInput(RequestContext context);
|
||||
|
||||
/**
|
||||
* Map output attributes of an ended flow to a resuming parent flow session. This maps the <i>output</i> of the
|
||||
* child as new input to the resuming parent, typically adding data to flow scope.
|
||||
* @param flowOutput the output attributes exposed by the ended subflow
|
||||
* @param context the current request execution context, which gives access to the parent flow scope
|
||||
*/
|
||||
public void mapFlowOutput(AttributeMap flowOutput, RequestContext context);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package org.springframework.webflow.engine;
|
||||
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
|
||||
/**
|
||||
* A strategy interface used by a subflow state to map subflow input and output attributes.
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public interface SubflowAttributeMapper {
|
||||
|
||||
/**
|
||||
* Create a map of attributes that should be passed as <i>input</i> to a subflow.
|
||||
* @param context the current request execution context
|
||||
* @return a map of attributes to pass as input
|
||||
*/
|
||||
public MutableAttributeMap createFlowInput(RequestContext context);
|
||||
|
||||
/**
|
||||
* Map output attributes of an ended subflow flow to the resuming parent flow.
|
||||
* @param flowOutput the output attributes returned by the ended subflow
|
||||
* @param context the current request execution context, which gives access to the parent flow scope
|
||||
*/
|
||||
public void mapFlowOutput(AttributeMap flowOutput, RequestContext context);
|
||||
}
|
||||
@@ -15,13 +15,13 @@
|
||||
*/
|
||||
package org.springframework.webflow.engine;
|
||||
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.core.collection.LocalAttributeMap;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.execution.FlowExecutionException;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
|
||||
/**
|
||||
* A transitionable state that spawns a subflow when executed. When the subflow this state spawns ends, the ending
|
||||
@@ -29,10 +29,10 @@ import org.springframework.webflow.execution.RequestContext;
|
||||
* <p>
|
||||
* A subflow state may be configured to map input data from its flow -- acting as the parent flow -- down to the subflow
|
||||
* when the subflow is spawned. In addition, output data produced by the subflow may be mapped up to the parent flow
|
||||
* when the subflow ends and the parent flow resumes. See the {@link FlowAttributeMapper} interface definition for more
|
||||
* information on how to do this. The logic for ending a subflow is located in the {@link EndState} implementation.
|
||||
* when the subflow ends and the parent flow resumes. See the {@link SubflowAttributeMapper} interface definition for
|
||||
* more information on how to do this. The logic for ending a subflow is located in the {@link EndState} implementation.
|
||||
*
|
||||
* @see org.springframework.webflow.engine.FlowAttributeMapper
|
||||
* @see org.springframework.webflow.engine.SubflowAttributeMapper
|
||||
* @see org.springframework.webflow.engine.EndState
|
||||
*
|
||||
* @author Keith Donald
|
||||
@@ -41,14 +41,14 @@ import org.springframework.webflow.execution.RequestContext;
|
||||
public class SubflowState extends TransitionableState {
|
||||
|
||||
/**
|
||||
* The subflow that should be spawned when this subflow state is entered. TODO - late binding
|
||||
* The subflow that should be spawned when this subflow state is entered.
|
||||
*/
|
||||
private Flow subflow;
|
||||
private Expression subflow;
|
||||
|
||||
/**
|
||||
* The attribute mapper that should map attributes from the parent flow down to the spawned subflow and visa versa.
|
||||
*/
|
||||
private FlowAttributeMapper attributeMapper = new NoAttributeMapper();
|
||||
private SubflowAttributeMapper subflowAttributeMapper;
|
||||
|
||||
/**
|
||||
* Create a new subflow state.
|
||||
@@ -56,9 +56,9 @@ public class SubflowState extends TransitionableState {
|
||||
* @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)
|
||||
* @see #setAttributeMapper(SubflowAttributeMapper)
|
||||
*/
|
||||
public SubflowState(Flow flow, String id, Flow subflow) throws IllegalArgumentException {
|
||||
public SubflowState(Flow flow, String id, Expression subflow) throws IllegalArgumentException {
|
||||
super(flow, id);
|
||||
setSubflow(subflow);
|
||||
}
|
||||
@@ -66,7 +66,7 @@ public class SubflowState extends TransitionableState {
|
||||
/**
|
||||
* Set the subflow this state will call.
|
||||
*/
|
||||
private void setSubflow(Flow subflow) {
|
||||
private void setSubflow(Expression subflow) {
|
||||
Assert.notNull(subflow, "A subflow state must have a subflow; the subflow is required");
|
||||
this.subflow = subflow;
|
||||
}
|
||||
@@ -74,9 +74,8 @@ public class SubflowState extends TransitionableState {
|
||||
/**
|
||||
* Set the attribute mapper used to map model data between the parent and child flow.
|
||||
*/
|
||||
public void setAttributeMapper(FlowAttributeMapper attributeMapper) {
|
||||
Assert.notNull(attributeMapper, "The attribute mapper is required");
|
||||
this.attributeMapper = attributeMapper;
|
||||
public void setAttributeMapper(SubflowAttributeMapper attributeMapper) {
|
||||
this.subflowAttributeMapper = attributeMapper;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,10 +88,17 @@ public class SubflowState extends TransitionableState {
|
||||
* @throws FlowExecutionException if an exception occurs in this state
|
||||
*/
|
||||
protected void doEnter(RequestControlContext context) throws FlowExecutionException {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Calling subflow '" + subflow.getId() + "'");
|
||||
MutableAttributeMap flowInput;
|
||||
if (subflowAttributeMapper != null) {
|
||||
flowInput = subflowAttributeMapper.createFlowInput(context);
|
||||
} else {
|
||||
flowInput = new LocalAttributeMap();
|
||||
}
|
||||
context.start(subflow, attributeMapper.createFlowInput(context));
|
||||
Flow subflow = (Flow) this.subflow.getValue(context);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Calling subflow '" + subflow.getId() + "' with input " + flowInput);
|
||||
}
|
||||
context.start(subflow, flowInput);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,27 +106,19 @@ public class SubflowState extends TransitionableState {
|
||||
* the subflow.
|
||||
*/
|
||||
public void handleEvent(RequestControlContext context) {
|
||||
attributeMapper.mapFlowOutput(context.getLastEvent().getAttributes(), context);
|
||||
if (subflowAttributeMapper != null) {
|
||||
AttributeMap subflowOutput = context.getLastEvent().getAttributes();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Mapping subflow output " + subflowOutput);
|
||||
}
|
||||
subflowAttributeMapper.mapFlowOutput(subflowOutput, context);
|
||||
}
|
||||
super.handleEvent(context);
|
||||
}
|
||||
|
||||
protected void appendToString(ToStringCreator creator) {
|
||||
creator.append("subflow", subflow.getId()).append("attributeMapper", attributeMapper);
|
||||
creator.append("subflow", subflow).append("subflowAttributeMapper", subflowAttributeMapper);
|
||||
super.appendToString(creator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps no output attributes. The default implementation.
|
||||
*/
|
||||
private class NoAttributeMapper implements FlowAttributeMapper {
|
||||
public MutableAttributeMap createFlowInput(RequestContext context) {
|
||||
logger.debug("No input will be passed to subflow");
|
||||
return new LocalAttributeMap();
|
||||
}
|
||||
|
||||
public void mapFlowOutput(AttributeMap flowOutput, RequestContext context) {
|
||||
logger.debug("No subflow output will be mapped");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -123,6 +123,9 @@ public class ViewState extends TransitionableState {
|
||||
this.redirect = redirect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this view state should render as a popup.
|
||||
*/
|
||||
public boolean getPopup() {
|
||||
return popup;
|
||||
}
|
||||
@@ -135,6 +138,9 @@ public class ViewState extends TransitionableState {
|
||||
this.popup = popup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the view factory.
|
||||
*/
|
||||
public ViewFactory getViewFactory() {
|
||||
return viewFactory;
|
||||
}
|
||||
@@ -242,7 +248,8 @@ public class ViewState extends TransitionableState {
|
||||
|
||||
protected void appendToString(ToStringCreator creator) {
|
||||
super.appendToString(creator);
|
||||
creator.append("viewFactory", viewFactory).append("variables", variables);
|
||||
creator.append("viewFactory", viewFactory).append("variables", variables).append("redirect", redirect).append(
|
||||
"popup", popup);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,15 +15,16 @@
|
||||
*/
|
||||
package org.springframework.webflow.engine.builder;
|
||||
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.mapping.AttributeMapper;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.engine.ActionState;
|
||||
import org.springframework.webflow.engine.DecisionState;
|
||||
import org.springframework.webflow.engine.EndState;
|
||||
import org.springframework.webflow.engine.Flow;
|
||||
import org.springframework.webflow.engine.FlowAttributeMapper;
|
||||
import org.springframework.webflow.engine.FlowExecutionExceptionHandler;
|
||||
import org.springframework.webflow.engine.State;
|
||||
import org.springframework.webflow.engine.SubflowAttributeMapper;
|
||||
import org.springframework.webflow.engine.SubflowState;
|
||||
import org.springframework.webflow.engine.TargetStateResolver;
|
||||
import org.springframework.webflow.engine.Transition;
|
||||
@@ -152,8 +153,8 @@ public class FlowArtifactFactory {
|
||||
* null
|
||||
* @return the fully initialized subflow state instance
|
||||
*/
|
||||
public State createSubflowState(String id, Flow flow, Action[] entryActions, Flow subflow,
|
||||
FlowAttributeMapper attributeMapper, Transition[] transitions,
|
||||
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) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -77,15 +77,6 @@ Goes out of scope when this local flow session ends.
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Goes out of scope when the overall governing flow execution ends.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:enumeration>
|
||||
<xsd:enumeration value="default">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The default scope type.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
@@ -95,79 +86,25 @@ The default scope type.
|
||||
|
||||
<xsd:group name="actionTypes">
|
||||
<xsd:choice>
|
||||
<xsd:element ref="action">
|
||||
<xsd:element ref="evaluate">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Defines a single action to be executed. An action is a Web Flow specific command object
|
||||
that executes arbitrary behavior.
|
||||
<br>
|
||||
The action referenced by this element must implement the org.springframework.webflow.execution.Action
|
||||
interface. The action may be a MultiAction and if so the 'method' attribute can be used to
|
||||
specify the target method to invoke.
|
||||
<br>
|
||||
An action may be annotated with attributes that can be used to affect the action's execution.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element ref="bean-action">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Defines a single method on a bean to execute as an action.
|
||||
<br>
|
||||
The bean to invoke is typically an arbitrary "POJO" (Plain Old Java Object) with no
|
||||
dependency on Spring Web Flow.
|
||||
<br>
|
||||
Use this element when the logic to invoke is encapsulated within an object you define.
|
||||
This element can be used to invoke *any* public method on any bean.
|
||||
<br>
|
||||
If the target method accepts arguments they may be specified in order by using the
|
||||
'method-arguments' sub-element.
|
||||
<br>
|
||||
If the target method returns a value that value may be exposed to the flow using
|
||||
the 'method-result' sub-element.
|
||||
<br>
|
||||
For example:
|
||||
<pre>
|
||||
<bean-action bean="orderClerk" method="placeOrder">
|
||||
<method-arguments>
|
||||
<argument expression="flowScope.order"/>
|
||||
</method-arguments>
|
||||
<method-result name="orderConfirmation"/>
|
||||
</bean-action>
|
||||
</pre>
|
||||
The above example instructs this flow to invoke the "placeOrder" method on the "orderClerk" bean,
|
||||
passing the value of "flowScope.order" as the method argument. After method invocation the
|
||||
method return value is exposed in the default scope under the name "orderConfirmation".
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element ref="evaluate-action">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Defines an arbitrary expression against the flow request context to evaluate as an action.
|
||||
<br>
|
||||
Use this element when the logic to invoke is encapsulated within an object inside
|
||||
the flow request context. This element can be used to invoke *any* public method on
|
||||
a flow-managed bean.
|
||||
<br>
|
||||
For example:
|
||||
<pre>
|
||||
<evaluate-action expression="flowScope.interview.nextQuestion()">
|
||||
<evaluation-result name="question"/>
|
||||
</evaluate-action>
|
||||
</pre>
|
||||
The above example instructs this flow to invoke the "nextQuestion" method on the "interview" bean
|
||||
in flow scope. After method invocation the method return value is exposed in the default
|
||||
scope under the name "question".
|
||||
Evaluates an arbitrary expression aganst the flow request context.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element ref="render">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Requests that a specific fragment of the next view be rendered instead of the entire view.
|
||||
Multiple fragments may be specified using a comma delimiter.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element ref="set">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
@@ -184,7 +121,7 @@ This action always returns a "success" event unless an exception is thrown.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:group>
|
||||
|
||||
@@ -227,7 +164,7 @@ A flow may also exhibit the following characteristics:
|
||||
(See the <exception-handler/> element)
|
||||
|
||||
<li>Import one or more local bean definition files defining custom flow artifacts
|
||||
(such as actions, exception handlers, view selectors, transition criteria, etc).
|
||||
(such as actions, exception handlers, view factories, transition criteria, etc).
|
||||
(See the <import/> element)
|
||||
|
||||
</ul>
|
||||
@@ -288,14 +225,6 @@ that launched this flow.
|
||||
<li>The 'target' of each mapping is this flow execution's RequestContext, exposing access to
|
||||
data structures such as 'flowScope'.
|
||||
</ul>
|
||||
<br>
|
||||
For example:
|
||||
<pre>
|
||||
<input-mapper>
|
||||
<input-attribute name="id"/>
|
||||
</input-mapper>
|
||||
</pre>
|
||||
... maps the value of "id" input attribute to the "id" attribute in this flow's scope.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
@@ -440,14 +369,6 @@ internal data structures such as 'flowScope'.
|
||||
<li>The 'target' of each mapping is the flow output map that will contain the output returned to the
|
||||
caller that launched this flow.
|
||||
</ul>
|
||||
<br>
|
||||
For example:
|
||||
<pre>
|
||||
<output-mapper>
|
||||
<mapping source="flowScope.myFlowAttribute" target="clientOutputAttribute"/>
|
||||
</output-mapper>
|
||||
</pre>
|
||||
... maps the value of "myFlowAttribute" in flow scope to "clientOutputAttribute" in this flow's output map.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
@@ -646,7 +567,7 @@ fully-qualified class (e.g. 'java.lang.Integer'). The class cannot be abstract
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="scope" type="scopeType" default="default">
|
||||
<xsd:attribute name="scope" type="scopeType">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -690,7 +611,7 @@ An attribute describing this state.
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Indicates this state is secured
|
||||
Indicates this state is secured.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
@@ -743,304 +664,8 @@ execution of this flow definition. Exception handlers may be attached at the sta
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="action">
|
||||
<xsd:element name="evaluate">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="attribute" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
An attribute describing this action.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="bean" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The identifier of the action implementation to execute, typically the id of a bean
|
||||
registered in a Spring BeanFactory.
|
||||
<br>
|
||||
If the referenced bean implements the org.springframework.webflow.execution.Action interface it is
|
||||
retrieved from the factory and used as is. If the bean is not an Action an exception is thrown.
|
||||
<br>
|
||||
This is similar to the <ref bean="myBean"/> notation of the Spring beans DTD.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="name" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
An optional name qualifier for this action. When specified this action will
|
||||
qualify execution result event identifiers by this name. For example, if this
|
||||
action is named "placeOrder" and signals a "success" result event after execution,
|
||||
the fully qualified result event the flow can respond to would be "placeOrder.success".
|
||||
<br>
|
||||
This can be used to execute actions in an ordered chain, where the flow responds
|
||||
to the the last action result in the chain:
|
||||
<pre>
|
||||
<action-state id="setupForm">
|
||||
<action name="setupForm" bean="formAction" method="setupForm"/>
|
||||
<action name="loadReferenceData" bean="formAction" method="loadReferenceData"/>
|
||||
<transition on="loadReferenceData.success" to="displayForm">
|
||||
</action-state>
|
||||
</pre>
|
||||
... will execute 'setupForm' followed by 'loadRefenceData', then transition the flow to
|
||||
the 'displayForm' state on a successful 'loadReferenceData' invocation.
|
||||
<br>
|
||||
An action with a name is often referred to as a "named action".
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="method" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The name of the method to invoke on this action.
|
||||
<br>
|
||||
Use this attribute when the action is a "multi action" extending
|
||||
org.springframework.webflow.action.MultiAction. The value should be
|
||||
name of the method to invoke on the multi-action instance.
|
||||
The method's implementation must have the following signature:
|
||||
<pre>
|
||||
public Event <methodName>(RequestContext context);
|
||||
</pre>
|
||||
As an example:
|
||||
<pre>
|
||||
<action bean="formAction" method="setupForm"/>
|
||||
</pre>
|
||||
... might invoke:
|
||||
<pre>
|
||||
public class FormAction extends MultiAction {
|
||||
public Event setupForm(RequestContext context) {
|
||||
return success();
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="bean-action">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="attribute" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
An attribute describing this bean action.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element ref="method-arguments" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Defines the expressions used to resolve the arguments to the bean-action method to invoke.
|
||||
Use this when the target method declares one or more parameters.
|
||||
The order of the argument expression list is significant.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element ref="method-result" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The rule for how the return value of the invoked bean method should be exposed to the flow.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="bean" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The identifier of the the bean to invoke, typically a singleton bean instance registered in
|
||||
the Spring BeanFactory.
|
||||
<br>
|
||||
It is expected the referenced bean be a simple POJO that does not implement the Spring Web Flow
|
||||
Action interface. The method to invoke, specified using the 'method' attribute,
|
||||
will be adapted to the Action interface automatically.
|
||||
<br>
|
||||
This is similar to the <ref bean="myBean"/> notation of the Spring beans DTD.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="name" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
An optional name qualifier for this bean action. When specified this action will
|
||||
qualify execution result event identifiers by this name. For example, if this action
|
||||
is named "placeOrder" and signals a "success" result event after execution, the
|
||||
fully qualified result event the flow can respond to would be "placeOrder.success".
|
||||
<br>
|
||||
This can be used to execute actions in an ordered chain, where the flow responds
|
||||
to the the last action result in the chain:
|
||||
<pre>
|
||||
<action-state id="setupForm">
|
||||
<action name="setupForm" bean="formAction" method="setupForm"/>
|
||||
<action name="loadReferenceData" bean="formAction" method="loadReferenceData"/>
|
||||
<transition on="loadReferenceData.success" to="displayForm">
|
||||
</action-state>
|
||||
</pre>
|
||||
... will execute 'setupForm' followed by 'loadRefenceData', then transition the flow to
|
||||
the 'displayForm' state on a successful 'loadReferenceData' invocation.
|
||||
<br>
|
||||
An action with a name is often referred to as a "named action".
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="method" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The name of the method to invoke on this bean.
|
||||
<br>
|
||||
If the method has parameters the arguments to those parameters should be specified using
|
||||
the 'method-arguments' element.
|
||||
<br>
|
||||
If the method returns a value that should be exposed to this flow, the 'method-result' element
|
||||
should be specified.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="method-arguments">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="argument" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
An argument expression for this bean method. The expression is evaluated against
|
||||
this flow's request context to resolve the argument value.
|
||||
<br>
|
||||
Typically used to pass a value from a flow scope type into this bean method as an argument.
|
||||
<br>
|
||||
Examples:
|
||||
<pre>
|
||||
<argument expression="flowScope.order"/>
|
||||
</pre>
|
||||
... passes in the value of the 'order' attribute in flow scope.
|
||||
<pre>
|
||||
<argument expression="'a constant'"/>
|
||||
</pre>
|
||||
... passes in the 'a constant' literal.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="argument">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="expression" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The value expression for this bean method argument.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="parameter-type" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The method parameter type. Optional. If specified and the argument value does not equal the
|
||||
parameter type, a type conversion will be attempted.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="method-result">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
An expression used to expose the return value of the target bean-action method, such as "#{flowScope.myResult}".
|
||||
Can be a simple literal when used in conjunction with the 'scope' attribute.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="scope" type="scopeType" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The scope of the attribute that will expose the return value of the invoked bean method.
|
||||
The available scope types are:
|
||||
<ol>
|
||||
<li>request - The result goes out of scope when the call into this flow that invoked this method completes.
|
||||
<li>flash - The result goes out of scope when the next user event is signaled.
|
||||
<li>flow - The result goes out of scope when this local flow session ends.
|
||||
<li>conversation - The result goes out of scope when the overall conversation governing this flow execution ends.
|
||||
</ol>
|
||||
<br>
|
||||
If not specified then the name attribute must be a fully resolvable expression such as "#{flowScope.myResult}".
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="type" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The desired method result type. Optional. If specified and the method return value is not of the
|
||||
desired result type, a type conversion will be attempted.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="evaluate-action">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="attribute" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
An attribute describing this bean action.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element ref="evaluation-result" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The rule for how the evaluated expression result should be exposed to the flow. Use of this sub-element is deprecated in
|
||||
favor of the newer "result" and "type" attributes and will be removed in a future release.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="expression" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
@@ -1059,7 +684,7 @@ A fully resolvable expression such as #{flowScope.foo} or #{bean.bar} for exposi
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="type" type="xsd:string" use="optional">
|
||||
<xsd:attribute name="result-type" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -1069,123 +694,31 @@ desired result type, a type conversion will be attempted.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="name" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
An optional name qualifier for this evaluate action. When specified this action will
|
||||
qualify execution result event identifiers by this name. For example, if this
|
||||
action is named "firstInterviewQuestion" and signals a "success" result event after execution,
|
||||
the fully qualified result event the flow can respond to would be "firstInterviewQuestion.success".
|
||||
<br>
|
||||
This can be used to execute actions in an ordered chain, where the flow responds
|
||||
to the the last action result in the chain:
|
||||
<pre>
|
||||
<action-state id="setupForm">
|
||||
<evaluate-action name="firstInterviewQuestion" bean="flowScope.interview.firstQuestion()"/>
|
||||
<action name="setupForm" bean="formAction" method="setupForm"/>
|
||||
<transition on="setupForm.success" to="displayForm">
|
||||
</action-state>
|
||||
</pre>
|
||||
... will execute 'firstInterviewQuestion' followed by 'setupForm', then transition the flow to
|
||||
the 'displayForm' state on a successful 'setupForm' invocation.
|
||||
<br>
|
||||
An action with a name is often referred to as a "named action".
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="evaluation-result">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
This element is deprecated in favor of the newer "result" and "type" attributes on the <evaluate-action> and will
|
||||
be removed in a future release.
|
||||
|
||||
<xsd:element name="render">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="fragments" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The fragments of the next view to render. Multiple fragments may be specified by using the comma delimiter.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The name of the attribute that will expose the result of expression evaluation.
|
||||
It may be used in conjunction with the 'scope' attribute, or else it may be a fully resolvable
|
||||
expression such as #{flowScope.foo}.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="scope" type="scopeType" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The scope of the attribute that will expose the result of expression evaluation.
|
||||
The available scope types are:
|
||||
<ol>
|
||||
<li>request - The result goes out of scope when the call into this flow that evaluated this expression completes.
|
||||
<li>flash - The result goes out of scope when the next user event is signaled.
|
||||
<li>flow - The result goes out of scope when this local flow session ends.
|
||||
<li>conversation - The result goes out of scope when the overall conversation governing this flow execution ends.
|
||||
</ol>
|
||||
<br>
|
||||
If not specified the name attribute must be a fully resolvable expression such as "#{flowScope.foo}".
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="type" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The desired evaluation result type. Optional. If specified and the method return value is not of the
|
||||
desired result type, a type conversion will be attempted.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="set">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="attribute" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
An attribute describing this bean action.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="attribute" type="xsd:string" use="required">
|
||||
<xsd:attribute name="name" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The name of the attribute to set. May be a nested path using Java beans notation (e.g. bean.property) to set a property
|
||||
on a bean in the specified scope, or it may be a fully resolvable expression such as "#{flowScope.foo}" instead.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="scope" type="scopeType" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The scope the attribute value will be stored in. The available scope types are:
|
||||
<ol>
|
||||
<li>request - The attribute goes out of scope when the call into this flow that sets the attribute completes.
|
||||
<li>flash - The attribute goes out of scope when the next user event is signaled.
|
||||
<li>flow - The attribute goes out of scope when this local flow session ends.
|
||||
<li>conversation - The attribute goes out of scope when the overall conversation governing this flow execution ends.
|
||||
</ol>
|
||||
<br>
|
||||
If not specified the attribute to set must be a fully resolvable expression such as "#{flowScope.foo}".
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
@@ -1195,32 +728,6 @@ If not specified the attribute to set must be a fully resolvable expression such
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The attribute value expression.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="name" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
An optional name qualifier for this set action. When specified this action will
|
||||
qualify execution result event identifiers by this name. For example, if this
|
||||
action is named "setFormSubmitted" and signals a "success" result event after execution,
|
||||
the fully qualified result event the flow can respond to would be "setFormSubmitted.success".
|
||||
<br>
|
||||
This can be used to execute actions in an ordered chain, where the flow responds
|
||||
to the the last action result in the chain:
|
||||
<pre>
|
||||
<action-state id="processSubmit">
|
||||
<action name="processSubmit" bean="formAction" method="processSubmit"/>
|
||||
<set name="setFormSubmitted" attribute="formSubmitted" value="true"/>
|
||||
<transition on="setFormSubmitted.success" to="thankYou">
|
||||
</action-state>
|
||||
</pre>
|
||||
... will execute 'processSubmit' followed by 'setFormSubmitted', then transition the flow to
|
||||
the 'thankYou' state on a successful 'setFormSubmitted' invocation.
|
||||
<br>
|
||||
An action with a name is often referred to as a "named action".
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
@@ -1638,32 +1145,38 @@ Defines state entry logic to be executed. This logic will always execute when th
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element ref="attribute-mapper" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Defines an attribute mapper to map attributes to and/or from the subflow. Input attributes
|
||||
may be exposed to the subflow when it is started by using an <input-mapper/>.
|
||||
Output attributes may be mapped from the subflow when it ends by using an <output-mapper/>.
|
||||
<br>
|
||||
For the input mapper the following mapping characteristics apply:
|
||||
<ul>
|
||||
<li>The 'source' of each mapping is the RequestContext, exposing access to internal
|
||||
data structures of this flow such as flowScope.
|
||||
<li>The 'target' of each input mapping is the subflow's input Map.
|
||||
</ul>
|
||||
<br>
|
||||
For the output mapper the following mapping characteristics apply:
|
||||
<ul>
|
||||
<li>The 'source' of each output mapping is the subflow's output Map.
|
||||
<li>The 'target' of each output mapping is the RequestContext, exposing access to
|
||||
internal data structures of this flow such as flowScope.
|
||||
</ul>
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:element>
|
||||
<xsd:element ref="input-mapper" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Maps data from this flow to the subflow as input. The following mapping characteristics apply:
|
||||
<ul>
|
||||
<li>The 'source' of each mapping is this flow execution's RequestContext, exposing access to
|
||||
internal data structures such as 'flowScope'.
|
||||
<li>The 'target' of each mapping is the input map that will be passed to the subflow.
|
||||
</ul>
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element ref="output-mapper" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The mapper that will map flow output attributes when this flow ends by entering this
|
||||
end state. Output attributes act as flow return values.
|
||||
<br>
|
||||
For the output mapper the following mapping characteristics apply:
|
||||
<ul>
|
||||
<li>The 'source' of each mapping is a Map containing all the output returned by the subflow.
|
||||
<li>The 'target' of each mapping is this flow execution's RequestContext, exposing access to
|
||||
data structures such as 'flowScope'.
|
||||
</ul>
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element ref="transition" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
@@ -1697,7 +1210,7 @@ execution of this flow definition. Exception handlers may be attached at the sta
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="flow" type="xsd:string" use="required">
|
||||
<xsd:attribute name="subflow" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
@@ -1706,79 +1219,21 @@ The id of the flow to be spawned as a subflow when this subflow state is entered
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="subflow-attribute-mapper" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The id of a custom bean to use to map attributes to and from the subflow. Specifiy this attribute or one of the
|
||||
input-mapper/output-mapper sub-elements, not both.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="attribute-mapper">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element ref="input-mapper" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The mapper to map input attributes to the subflow when it is started.
|
||||
|
||||
The following mapping characteristics apply:
|
||||
<ul>
|
||||
<li>The 'source' of each mapping is the RequestContext, exposing access to internal
|
||||
data structures of this flow such as flowScope.
|
||||
<li>The 'target' of each input mapping is the subflow's input map.
|
||||
</ul>
|
||||
<br>
|
||||
For example:
|
||||
<pre>
|
||||
<input-mapper>
|
||||
<mapping source="flowScope.myFlowAttribute" target="subflowInputAttribute"/>
|
||||
</input-mapper>
|
||||
</pre>
|
||||
... maps the value of "flowScope.myFlowAttribute" to the "subflowInputAttribute" in the subflow input map.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
<xsd:element ref="output-mapper" minOccurs="0">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The mapper to map output attributes from the subflow when it ends.
|
||||
For the output mapper the following mapping characteristics apply:
|
||||
<ul>
|
||||
<li>The 'source' of each output mapping is the subflow's output map.
|
||||
<li>The 'target' of each output mapping is the RequestContext, exposing access to
|
||||
internal data structures of this flow such as flowScope.
|
||||
</ul>
|
||||
<br>
|
||||
For example:
|
||||
<pre>
|
||||
<output-mapper>
|
||||
<mapping source="aSubflowOutputAttribute" target="flowScope.myFlowAttribute"/>
|
||||
</output-mapper>
|
||||
</pre>
|
||||
... maps the value of "aSubflowOutputAttribute" in the subflow output map to "myFlowAttribute"
|
||||
in flow scope.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:element>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="bean" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
The identifier of a custom flow attribute mapper implementation exported in the
|
||||
Spring bean factory. This is similar to the <ref bean="myBean"/> notation of the Spring beans DTD.
|
||||
<br>
|
||||
Use this as an alternative to the child input-mapper and output-mapper elements
|
||||
when you need full control of attribute mapping behavior for this subflow state.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="end-state">
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
@@ -1825,14 +1280,6 @@ internal data structures such as 'flowScope'.
|
||||
<li>The 'target' of each mapping is the flow output map that will contain the output returned to the
|
||||
caller that launched this flow.
|
||||
</ul>
|
||||
<br>
|
||||
For example:
|
||||
<pre>
|
||||
<output-mapper>
|
||||
<mapping source="flowScope.myFlowAttribute" target="clientOutputAttribute"/>
|
||||
</output-mapper>
|
||||
</pre>
|
||||
... maps the value of "myFlowAttribute" in flow scope to "clientOutputAttribute" in this flow's output map.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
@@ -1891,13 +1338,23 @@ context.
|
||||
The exact semantics regarding the interpretation of this value are determined by the
|
||||
installed TextToViewSelector converter.
|
||||
<br>
|
||||
Note when no view name is provided, this view state will make a "null" view selection. A null
|
||||
view does not request the rendering of a view, it only pauses the flow and returns control
|
||||
the client. Use a null view when another state is expected to generate the response.
|
||||
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.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="commit" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
<![CDATA[
|
||||
Indicates if this end-state is a "commit state". If true, any changes made to managed entities attached to the flow's persistence context
|
||||
will be flushed in a system transaction when this state is reached. If false, no flush will occur. This attribute has no effect
|
||||
if the flow is not a persistence-context.
|
||||
]]>
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
@@ -22,50 +22,49 @@ import org.springframework.binding.mapping.MappingContext;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
import org.springframework.webflow.core.collection.LocalAttributeMap;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.engine.FlowAttributeMapper;
|
||||
import org.springframework.webflow.engine.SubflowAttributeMapper;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
|
||||
/**
|
||||
* Convenient base class for attribute mapper implementations. Encapsulates common attribute mapper workflow. Contains
|
||||
* no state. Subclasses must override the {@link #getInputMapper()} and {@link #getOutputMapper()} methods to return the
|
||||
* input mapper and output mapper, respectively.
|
||||
* Convenient base class for subflow attribute mapper implementations. Encapsulates common attribute mapper workflow.
|
||||
* Contains no state. Subclasses must override the {@link #getInputMapper()} and {@link #getOutputMapper()} methods to
|
||||
* return the input mapper and output mapper, respectively.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
public abstract class AbstractFlowAttributeMapper implements FlowAttributeMapper, Serializable {
|
||||
public abstract class AbstractSubflowAttributeMapper implements SubflowAttributeMapper, Serializable {
|
||||
|
||||
/**
|
||||
* Returns the input mapper to use to map attributes of a parent flow {@link RequestContext} to a subflow input
|
||||
* attribute {@link AttributeMap map}.
|
||||
* {@link AttributeMap attribute map}.
|
||||
* @return the input mapper, or null if none
|
||||
* @see #createFlowInput(RequestContext)
|
||||
*/
|
||||
protected abstract AttributeMapper getInputMapper();
|
||||
|
||||
/**
|
||||
* Returns the output mapper to use to map attributes from a subflow output attribute map to the
|
||||
* {@link RequestContext}.
|
||||
* Returns the output mapper to use to map attributes from a subflow output {@link AttributeMap attribute map} to
|
||||
* the {@link RequestContext}.
|
||||
* @return the output mapper, or null if none
|
||||
* @see #mapFlowOutput(AttributeMap, RequestContext)
|
||||
*/
|
||||
protected abstract AttributeMapper getOutputMapper();
|
||||
|
||||
public MutableAttributeMap createFlowInput(RequestContext context) {
|
||||
if (getInputMapper() != null) {
|
||||
AttributeMapper inputMapper = getInputMapper();
|
||||
if (inputMapper != null) {
|
||||
LocalAttributeMap input = new LocalAttributeMap();
|
||||
// map from request context to input map
|
||||
getInputMapper().map(context, input, getMappingContext(context));
|
||||
inputMapper.map(context, input, getMappingContext(context));
|
||||
return input;
|
||||
} else {
|
||||
// an empty, but modifiable map
|
||||
return new LocalAttributeMap();
|
||||
}
|
||||
}
|
||||
|
||||
public void mapFlowOutput(AttributeMap subflowOutput, RequestContext context) {
|
||||
AttributeMapper outputMapper = getOutputMapper();
|
||||
if (getOutputMapper() != null && subflowOutput != null) {
|
||||
// map from subflow output map to request context
|
||||
getOutputMapper().map(subflowOutput, context, getMappingContext(context));
|
||||
outputMapper.map(subflowOutput, context, getMappingContext(context));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,23 +13,19 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.webflow.engine.builder.xml;
|
||||
package org.springframework.webflow.engine.support;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.springframework.binding.mapping.AttributeMapper;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.webflow.engine.support.AbstractFlowAttributeMapper;
|
||||
|
||||
/**
|
||||
* Simple flow attribute mapper that holds an input and output mapper strategy. This is an internal helper class of the
|
||||
* XmlFlowBuilder.
|
||||
*
|
||||
* @see org.springframework.webflow.engine.builder.xml.XmlFlowBuilder
|
||||
* Simple flow attribute mapper that holds an input and output mapper strategy.
|
||||
*
|
||||
* @author Keith Donald
|
||||
*/
|
||||
final class ImmutableFlowAttributeMapper extends AbstractFlowAttributeMapper implements Serializable {
|
||||
public final class GenericSubflowAttributeMapper extends AbstractSubflowAttributeMapper implements Serializable {
|
||||
|
||||
private final AttributeMapper inputMapper;
|
||||
|
||||
@@ -40,7 +36,7 @@ final class ImmutableFlowAttributeMapper extends AbstractFlowAttributeMapper imp
|
||||
* @param inputMapper the input mapping strategy
|
||||
* @param outputMapper the output mapping strategy
|
||||
*/
|
||||
public ImmutableFlowAttributeMapper(AttributeMapper inputMapper, AttributeMapper outputMapper) {
|
||||
public GenericSubflowAttributeMapper(AttributeMapper inputMapper, AttributeMapper outputMapper) {
|
||||
this.inputMapper = inputMapper;
|
||||
this.outputMapper = outputMapper;
|
||||
}
|
||||
@@ -21,9 +21,9 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.webflow.engine.AnnotatedAction;
|
||||
import org.springframework.webflow.engine.TransitionCriteria;
|
||||
import org.springframework.webflow.engine.WildcardTransitionCriteria;
|
||||
import org.springframework.webflow.execution.Action;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
|
||||
/**
|
||||
@@ -85,7 +85,7 @@ public class TransitionCriteriaChain implements TransitionCriteria {
|
||||
* Create a transition criteria chain chaining given list of actions.
|
||||
* @param actions the actions (and their execution properties) to chain together
|
||||
*/
|
||||
public static TransitionCriteria criteriaChainFor(AnnotatedAction[] actions) {
|
||||
public static TransitionCriteria criteriaChainFor(Action[] actions) {
|
||||
if (actions == null || actions.length == 0) {
|
||||
return WildcardTransitionCriteria.INSTANCE;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,11 @@ package org.springframework.webflow.execution;
|
||||
*/
|
||||
public interface View {
|
||||
|
||||
/**
|
||||
* Well-known attribute name for storing a render fragments value.
|
||||
*/
|
||||
public static final String RENDER_FRAGMENTS_ATTRIBUTE = "webflow-render-fragments";
|
||||
|
||||
/**
|
||||
* Render this view's content.
|
||||
*/
|
||||
|
||||
44
spring-webflow/src/test/java/log4j.xml
Normal file
44
spring-webflow/src/test/java/log4j.xml
Normal file
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
|
||||
|
||||
<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
|
||||
|
||||
<!-- Appenders -->
|
||||
<appender name="console" class="org.apache.log4j.ConsoleAppender">
|
||||
<param name="Target" value="System.out" />
|
||||
<layout class="org.apache.log4j.PatternLayout">
|
||||
<param name="ConversionPattern" value="%-5p: %c - %m%n" />
|
||||
</layout>
|
||||
</appender>
|
||||
|
||||
<logger name="org.springframework.beans">
|
||||
<level value="warn" />
|
||||
</logger>
|
||||
|
||||
<logger name="org.springframework.jdbc">
|
||||
<level value="warn" />
|
||||
</logger>
|
||||
|
||||
<logger name="org.springframework.transaction">
|
||||
<level value="warn" />
|
||||
</logger>
|
||||
|
||||
<logger name="org.springframework.orm">
|
||||
<level value="warn" />
|
||||
</logger>
|
||||
|
||||
<logger name="org.springframework.web">
|
||||
<level value="debug" />
|
||||
</logger>
|
||||
|
||||
<logger name="org.springframework.webflow">
|
||||
<level value="debug" />
|
||||
</logger>
|
||||
|
||||
<!-- Root Logger -->
|
||||
<root>
|
||||
<priority value="warn" />
|
||||
<appender-ref ref="console" />
|
||||
</root>
|
||||
|
||||
</log4j:configuration>
|
||||
@@ -2,160 +2,37 @@ package org.springframework.webflow.action;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.jboss.el.ExpressionFactoryImpl;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.binding.expression.support.ParserContextImpl;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.core.expression.WebFlowOgnlExpressionParser;
|
||||
import org.springframework.webflow.core.expression.el.WebFlowELExpressionParser;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.RequestContextHolder;
|
||||
import org.springframework.webflow.execution.ScopeType;
|
||||
import org.springframework.binding.convert.support.DefaultConversionService;
|
||||
import org.springframework.binding.expression.support.StaticExpression;
|
||||
import org.springframework.webflow.test.MockRequestContext;
|
||||
|
||||
public class ActionResultExposerTests extends TestCase {
|
||||
|
||||
public void testExposeResult_ScopeSpecified() {
|
||||
|
||||
String valueToSet = "myValue";
|
||||
|
||||
ExpressionParser parser = new WebFlowELExpressionParser(new ExpressionFactoryImpl());
|
||||
Expression nameExpression = parser.parseExpression("#{foo}", new ParserContextImpl()
|
||||
.eval(MutableAttributeMap.class));
|
||||
|
||||
ActionResultExposer exposer = new ActionResultExposer(nameExpression, ScopeType.REQUEST, null);
|
||||
|
||||
RequestContext context = new MockRequestContext();
|
||||
|
||||
exposer.exposeResult(valueToSet, context);
|
||||
|
||||
assertTrue("Key 'foo' not found in request scope", context.getRequestScope().contains("foo"));
|
||||
assertEquals("Value stored at key 'foo' is incorrect", valueToSet, context.getRequestScope().get("foo"));
|
||||
public void testEvaluateExpressionNullResult() throws Exception {
|
||||
StaticExpression resultExpression = new StaticExpression("");
|
||||
ActionResultExposer exposer = new ActionResultExposer(resultExpression, null, null);
|
||||
MockRequestContext context = new MockRequestContext();
|
||||
exposer.exposeResult("foo", context);
|
||||
assertEquals("foo", resultExpression.getValue(null));
|
||||
}
|
||||
|
||||
public void testExposeResult_ScopeSpecifiedWithTypeConversion() {
|
||||
|
||||
String valueToSet = "true";
|
||||
|
||||
ExpressionParser parser = new WebFlowELExpressionParser(new ExpressionFactoryImpl());
|
||||
Expression nameExpression = parser.parseExpression("#{foo}", new ParserContextImpl()
|
||||
.eval(MutableAttributeMap.class));
|
||||
|
||||
ActionResultExposer exposer = new ActionResultExposer(nameExpression, ScopeType.REQUEST, Boolean.class);
|
||||
|
||||
RequestContext context = new MockRequestContext();
|
||||
|
||||
exposer.exposeResult(valueToSet, context);
|
||||
|
||||
assertTrue("Key 'foo' not found in request scope", context.getRequestScope().contains("foo"));
|
||||
assertEquals("Value stored at key 'foo' is incorrect", Boolean.TRUE, context.getRequestScope().get("foo"));
|
||||
public void testEvaluateExpressionResultExposerWithTypeConversion() throws Exception {
|
||||
StaticExpression resultExpression = new StaticExpression("");
|
||||
ActionResultExposer exposer = new ActionResultExposer(resultExpression, Integer.class,
|
||||
new DefaultConversionService());
|
||||
MockRequestContext context = new MockRequestContext();
|
||||
exposer.exposeResult("3", context);
|
||||
assertEquals(new Integer(3), resultExpression.getValue(null));
|
||||
}
|
||||
|
||||
public void testExposeResult_ScopeExpression() {
|
||||
public void testEvaluateExpressionResultExposerWithTypeConversionForgotArgument() throws Exception {
|
||||
StaticExpression resultExpression = new StaticExpression("");
|
||||
try {
|
||||
new ActionResultExposer(resultExpression, Integer.class, null);
|
||||
fail("should have failed iae");
|
||||
} catch (IllegalArgumentException e) {
|
||||
|
||||
String valueToSet = "myValue";
|
||||
|
||||
ExpressionParser parser = new WebFlowELExpressionParser(new ExpressionFactoryImpl());
|
||||
Expression nameExpression = parser.parseExpression("#{requestScope.foo}", new ParserContextImpl()
|
||||
.eval(RequestContext.class));
|
||||
|
||||
ActionResultExposer exposer = new ActionResultExposer(nameExpression, null, null);
|
||||
|
||||
RequestContext context = new MockRequestContext();
|
||||
RequestContextHolder.setRequestContext(context);
|
||||
|
||||
exposer.exposeResult(valueToSet, context);
|
||||
|
||||
assertTrue("Key 'foo' not found in request scope", context.getRequestScope().contains("foo"));
|
||||
assertEquals("Value stored at key 'foo' is incorrect", valueToSet, context.getRequestScope().get("foo"));
|
||||
}
|
||||
|
||||
public void testExposeResult_ScopeExpressionWithTypeConversion() {
|
||||
|
||||
String valueToSet = "true";
|
||||
|
||||
ExpressionParser parser = new WebFlowELExpressionParser(new ExpressionFactoryImpl());
|
||||
Expression nameExpression = parser.parseExpression("#{requestScope.foo}", new ParserContextImpl()
|
||||
.eval(RequestContext.class));
|
||||
|
||||
ActionResultExposer exposer = new ActionResultExposer(nameExpression, null, Boolean.class);
|
||||
|
||||
RequestContext context = new MockRequestContext();
|
||||
RequestContextHolder.setRequestContext(context);
|
||||
|
||||
exposer.exposeResult(valueToSet, context);
|
||||
|
||||
assertTrue("Key 'foo' not found in request scope", context.getRequestScope().contains("foo"));
|
||||
assertEquals("Value stored at key 'foo' is incorrect", Boolean.TRUE, context.getRequestScope().get("foo"));
|
||||
}
|
||||
|
||||
public void testExposeResult_SearchExpression() {
|
||||
|
||||
String valueToSet = "myValue";
|
||||
|
||||
ExpressionParser parser = new WebFlowELExpressionParser(new ExpressionFactoryImpl());
|
||||
Expression nameExpression = parser.parseExpression("#{bean.foo}", new ParserContextImpl()
|
||||
.eval(RequestContext.class));
|
||||
|
||||
ActionResultExposer exposer = new ActionResultExposer(nameExpression, null, null);
|
||||
|
||||
RequestContext context = new MockRequestContext();
|
||||
RequestContextHolder.setRequestContext(context);
|
||||
TestBean bean = new TestBean();
|
||||
context.getRequestScope().put("bean", bean);
|
||||
|
||||
exposer.exposeResult(valueToSet, context);
|
||||
|
||||
assertEquals("Value of foo is incorrect", valueToSet, bean.getFoo());
|
||||
}
|
||||
|
||||
public void testExposeResult_OGNL_ScopeSpecified() {
|
||||
|
||||
String valueToSet = "myValue";
|
||||
|
||||
ExpressionParser parser = new WebFlowOgnlExpressionParser();
|
||||
Expression nameExpression = parser.parseExpression("${foo}", new ParserContextImpl()
|
||||
.eval(MutableAttributeMap.class));
|
||||
|
||||
ActionResultExposer exposer = new ActionResultExposer(nameExpression, ScopeType.REQUEST, null);
|
||||
|
||||
RequestContext context = new MockRequestContext();
|
||||
|
||||
exposer.exposeResult(valueToSet, context);
|
||||
|
||||
assertTrue("Key 'foo' not found in request scope", context.getRequestScope().contains("foo"));
|
||||
assertEquals("Value stored at key 'foo' is incorrect", valueToSet, context.getRequestScope().get("foo"));
|
||||
}
|
||||
|
||||
public void testExposeResult_OGNL_ScopeExpression() {
|
||||
|
||||
String valueToSet = "myValue";
|
||||
|
||||
ExpressionParser parser = new WebFlowOgnlExpressionParser();
|
||||
Expression nameExpression = parser.parseExpression("${requestScope.foo}", new ParserContextImpl()
|
||||
.eval(RequestContext.class));
|
||||
|
||||
ActionResultExposer exposer = new ActionResultExposer(nameExpression, null, null);
|
||||
|
||||
RequestContext context = new MockRequestContext();
|
||||
|
||||
exposer.exposeResult(valueToSet, context);
|
||||
|
||||
assertTrue("Key 'foo' not found in request scope", context.getRequestScope().contains("foo"));
|
||||
assertEquals("Value stored at key 'foo' is incorrect", valueToSet, context.getRequestScope().get("foo"));
|
||||
}
|
||||
|
||||
public class TestBean {
|
||||
|
||||
private String foo;
|
||||
|
||||
public String getFoo() {
|
||||
return foo;
|
||||
}
|
||||
|
||||
public void setFoo(String foo) {
|
||||
this.foo = foo;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,116 +17,30 @@ package org.springframework.webflow.action;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.jboss.el.ExpressionFactoryImpl;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.binding.expression.support.ParserContextImpl;
|
||||
import org.springframework.binding.expression.support.StaticExpression;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.core.expression.el.WebFlowELExpressionParser;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.RequestContextHolder;
|
||||
import org.springframework.webflow.execution.ScopeType;
|
||||
import org.springframework.webflow.test.MockRequestContext;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link EvaluateAction}.
|
||||
*
|
||||
* @author Jeremy Grelle
|
||||
*/
|
||||
public class EvaluateActionTests extends TestCase {
|
||||
|
||||
private MockRequestContext context = new MockRequestContext();
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
RequestContextHolder.setRequestContext(context);
|
||||
context.getFlowScope().put("foo", "bar");
|
||||
context.getFlowScope().put("bean", new TestBean());
|
||||
}
|
||||
|
||||
public void testEvaluateExpressionNoResult() throws Exception {
|
||||
EvaluateAction action = new EvaluateAction(new StaticExpression("bar"));
|
||||
public void testEvaluateExpressionNoResultExposer() throws Exception {
|
||||
EvaluateAction action = new EvaluateAction(new StaticExpression("bar"), null);
|
||||
MockRequestContext context = new MockRequestContext();
|
||||
Event result = action.execute(context);
|
||||
assertEquals("bar", result.getId());
|
||||
assertNull(context.getFlowScope().get("baz"));
|
||||
}
|
||||
|
||||
public void testEvaluateExpressionResult_ScopeSpecfied() throws Exception {
|
||||
ExpressionParser parser = new WebFlowELExpressionParser(new ExpressionFactoryImpl());
|
||||
Expression nameExpression = parser.parseExpression("#{baz}", new ParserContextImpl()
|
||||
.eval(MutableAttributeMap.class));
|
||||
|
||||
EvaluateAction action = new EvaluateAction(new StaticExpression("bar"), new ActionResultExposer(nameExpression,
|
||||
ScopeType.FLOW, null));
|
||||
public void testEvaluateExpressionResultExposer() throws Exception {
|
||||
StaticExpression resultExpression = new StaticExpression("");
|
||||
EvaluateAction action = new EvaluateAction(new StaticExpression("bar"), new ActionResultExposer(
|
||||
resultExpression, null, null));
|
||||
MockRequestContext context = new MockRequestContext();
|
||||
Event result = action.execute(context);
|
||||
assertEquals("bar", result.getId());
|
||||
assertEquals("bar", context.getFlowScope().get("baz"));
|
||||
assertEquals("bar", resultExpression.getValue(null));
|
||||
}
|
||||
|
||||
public void testBeanResult_ScopeSpecified() throws Exception {
|
||||
ExpressionParser parser = new WebFlowELExpressionParser(new ExpressionFactoryImpl());
|
||||
Expression nameExpression = parser.parseExpression("#{baz}", new ParserContextImpl()
|
||||
.eval(MutableAttributeMap.class));
|
||||
|
||||
TestBean bean = new TestBean();
|
||||
|
||||
EvaluateAction action = new EvaluateAction(new StaticExpression(bean), new ActionResultExposer(nameExpression,
|
||||
ScopeType.FLOW, null));
|
||||
Event result = action.execute(context);
|
||||
assertEquals("success", result.getId());
|
||||
assertEquals(bean, context.getFlowScope().get("baz"));
|
||||
}
|
||||
|
||||
public void testStringResult_ScopeSpecifiedWithTypeConversion() throws Exception {
|
||||
ExpressionParser parser = new WebFlowELExpressionParser(new ExpressionFactoryImpl());
|
||||
Expression nameExpression = parser.parseExpression("#{baz}", new ParserContextImpl()
|
||||
.eval(MutableAttributeMap.class));
|
||||
|
||||
EvaluateAction action = new EvaluateAction(new StaticExpression("true"), new ActionResultExposer(
|
||||
nameExpression, ScopeType.FLOW, Boolean.class));
|
||||
Event result = action.execute(context);
|
||||
assertEquals("true", result.getId());
|
||||
assertEquals(Boolean.TRUE, context.getFlowScope().get("baz"));
|
||||
}
|
||||
|
||||
public void testEvaluateExpressionResult_ScopeExpression() throws Exception {
|
||||
ExpressionParser parser = new WebFlowELExpressionParser(new ExpressionFactoryImpl());
|
||||
Expression nameExpression = parser.parseExpression("#{flowScope.baz}", new ParserContextImpl()
|
||||
.eval(MutableAttributeMap.class));
|
||||
|
||||
EvaluateAction action = new EvaluateAction(new StaticExpression("bar"), new ActionResultExposer(nameExpression,
|
||||
null, null));
|
||||
Event result = action.execute(context);
|
||||
assertEquals("bar", result.getId());
|
||||
assertEquals("bar", context.getFlowScope().get("baz"));
|
||||
}
|
||||
|
||||
public void testEvaluateExpressionResult_ScopeSearch() throws Exception {
|
||||
ExpressionParser parser = new WebFlowELExpressionParser(new ExpressionFactoryImpl());
|
||||
Expression nameExpression = parser.parseExpression("#{baz.foo}", new ParserContextImpl()
|
||||
.eval(RequestContext.class));
|
||||
|
||||
TestBean bean = new TestBean();
|
||||
context.getFlowScope().put("baz", bean);
|
||||
|
||||
EvaluateAction action = new EvaluateAction(new StaticExpression("bar"), new ActionResultExposer(nameExpression,
|
||||
null, null));
|
||||
Event result = action.execute(context);
|
||||
assertEquals("bar", result.getId());
|
||||
assertEquals("bar", bean.getFoo());
|
||||
}
|
||||
|
||||
public class TestBean {
|
||||
|
||||
private String foo;
|
||||
|
||||
public String getFoo() {
|
||||
return foo;
|
||||
}
|
||||
|
||||
public void setFoo(String foo) {
|
||||
this.foo = foo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package org.springframework.webflow.action;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.expression.support.StaticExpression;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.execution.View;
|
||||
import org.springframework.webflow.test.MockRequestContext;
|
||||
|
||||
public class RenderActionTests extends TestCase {
|
||||
public void testRenderAction() throws Exception {
|
||||
StaticExpression name = new StaticExpression("frag1");
|
||||
StaticExpression name2 = new StaticExpression("frag2");
|
||||
RenderAction action = new RenderAction(new Expression[] { name, name2 });
|
||||
MockRequestContext context = new MockRequestContext();
|
||||
Event result = action.execute(context);
|
||||
assertEquals("success", result.getId());
|
||||
String[] fragments = (String[]) context.getFlashScope().getArray(View.RENDER_FRAGMENTS_ATTRIBUTE,
|
||||
String[].class);
|
||||
assertEquals("frag1", fragments[0]);
|
||||
assertEquals("frag2", fragments[1]);
|
||||
}
|
||||
|
||||
public void testIllegalNullArg() {
|
||||
try {
|
||||
new RenderAction(null);
|
||||
fail("iae");
|
||||
} catch (IllegalArgumentException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void testIllegalEmptyArg() {
|
||||
try {
|
||||
new RenderAction(new Expression[0]);
|
||||
fail("iae");
|
||||
} catch (IllegalArgumentException e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,182 +2,17 @@ package org.springframework.webflow.action;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.jboss.el.ExpressionFactoryImpl;
|
||||
import org.springframework.binding.expression.Expression;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.binding.expression.ognl.OgnlExpressionParser;
|
||||
import org.springframework.binding.expression.support.ParserContextImpl;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.core.expression.WebFlowOgnlExpressionParser;
|
||||
import org.springframework.webflow.core.expression.el.WebFlowELExpressionParser;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
import org.springframework.webflow.execution.RequestContextHolder;
|
||||
import org.springframework.webflow.execution.ScopeType;
|
||||
import org.springframework.binding.expression.support.StaticExpression;
|
||||
import org.springframework.webflow.execution.Event;
|
||||
import org.springframework.webflow.test.MockRequestContext;
|
||||
|
||||
public class SetActionTests extends TestCase {
|
||||
|
||||
public void testExecute_AttrExpression_ScopeSpecified() throws Exception {
|
||||
|
||||
String valueToSet = "myValue";
|
||||
|
||||
ExpressionParser parser = new WebFlowELExpressionParser(new ExpressionFactoryImpl());
|
||||
|
||||
Expression attributeExpression = parser.parseExpression("#{foo}", new ParserContextImpl()
|
||||
.eval(MutableAttributeMap.class));
|
||||
ScopeType scope = ScopeType.REQUEST;
|
||||
Expression valueExpression = parser.parseExpression(valueToSet, new ParserContextImpl()
|
||||
.eval(RequestContext.class));
|
||||
|
||||
SetAction action = new SetAction(attributeExpression, scope, valueExpression);
|
||||
|
||||
RequestContext context = new MockRequestContext();
|
||||
action.execute(context);
|
||||
|
||||
assertTrue(context.getRequestScope().contains("foo"));
|
||||
assertEquals(valueToSet, context.getRequestScope().get("foo"));
|
||||
}
|
||||
|
||||
public void testExecute_ScopeExpression() throws Exception {
|
||||
|
||||
String valueToSet = "myValue";
|
||||
|
||||
ExpressionParser parser = new WebFlowELExpressionParser(new ExpressionFactoryImpl());
|
||||
|
||||
Expression attributeExpression = parser.parseExpression("#{requestScope.foo}", new ParserContextImpl()
|
||||
.eval(RequestContext.class));
|
||||
ScopeType scope = null;
|
||||
Expression valueExpression = parser.parseExpression(valueToSet, new ParserContextImpl()
|
||||
.eval(RequestContext.class));
|
||||
|
||||
SetAction action = new SetAction(attributeExpression, scope, valueExpression);
|
||||
|
||||
RequestContext context = new MockRequestContext();
|
||||
RequestContextHolder.setRequestContext(context);
|
||||
action.execute(context);
|
||||
|
||||
assertTrue(context.getRequestScope().contains("foo"));
|
||||
assertEquals(valueToSet, context.getRequestScope().get("foo"));
|
||||
}
|
||||
|
||||
public void testExecute_SearchExpression() throws Exception {
|
||||
|
||||
String valueToSet = "myValue";
|
||||
|
||||
ExpressionParser parser = new WebFlowELExpressionParser(new ExpressionFactoryImpl());
|
||||
|
||||
Expression attributeExpression = parser.parseExpression("#{bean.foo}", new ParserContextImpl()
|
||||
.eval(RequestContext.class));
|
||||
ScopeType scope = null;
|
||||
Expression valueExpression = parser.parseExpression(valueToSet, new ParserContextImpl()
|
||||
.eval(RequestContext.class));
|
||||
|
||||
SetAction action = new SetAction(attributeExpression, scope, valueExpression);
|
||||
|
||||
RequestContext context = new MockRequestContext();
|
||||
RequestContextHolder.setRequestContext(context);
|
||||
TestBean bean = new TestBean();
|
||||
context.getRequestScope().put("bean", bean);
|
||||
|
||||
action.execute(context);
|
||||
|
||||
assertEquals(valueToSet, bean.getFoo());
|
||||
}
|
||||
|
||||
public void testExecute_OGNL_ScopeSpecified() throws Exception {
|
||||
|
||||
String valueToSet = "myValue";
|
||||
|
||||
OgnlExpressionParser parser = new WebFlowOgnlExpressionParser();
|
||||
|
||||
Expression attributeExpression = parser.parseExpression("${foo}", new ParserContextImpl()
|
||||
.eval(MutableAttributeMap.class));
|
||||
ScopeType scope = ScopeType.REQUEST;
|
||||
Expression valueExpression = parser.parseExpression(valueToSet, new ParserContextImpl()
|
||||
.eval(RequestContext.class));
|
||||
|
||||
SetAction action = new SetAction(attributeExpression, scope, valueExpression);
|
||||
|
||||
RequestContext context = new MockRequestContext();
|
||||
action.execute(context);
|
||||
|
||||
assertTrue(context.getRequestScope().contains("foo"));
|
||||
assertEquals(valueToSet, context.getRequestScope().get("foo"));
|
||||
}
|
||||
|
||||
public void testExecute_OGNL_ScopeExpression() throws Exception {
|
||||
|
||||
String valueToSet = "myValue";
|
||||
|
||||
OgnlExpressionParser parser = new WebFlowOgnlExpressionParser();
|
||||
|
||||
Expression attributeExpression = parser.parseExpression("${requestScope.foo}", new ParserContextImpl()
|
||||
.eval(RequestContext.class));
|
||||
Expression valueExpression = parser.parseExpression(valueToSet, new ParserContextImpl()
|
||||
.eval(RequestContext.class));
|
||||
|
||||
SetAction action = new SetAction(attributeExpression, null, valueExpression);
|
||||
|
||||
RequestContext context = new MockRequestContext();
|
||||
action.execute(context);
|
||||
|
||||
assertTrue(context.getRequestScope().contains("foo"));
|
||||
assertEquals(valueToSet, context.getRequestScope().get("foo"));
|
||||
}
|
||||
|
||||
public void testExecute_LegacyOGNL_ScopeSpecified() throws Exception {
|
||||
|
||||
String valueToSet = "myValue";
|
||||
|
||||
OgnlExpressionParser parser = new WebFlowOgnlExpressionParser();
|
||||
parser.setAllowUndelimitedEvalExpressions(true);
|
||||
|
||||
Expression attributeExpression = parser.parseExpression("foo", new ParserContextImpl()
|
||||
.eval(MutableAttributeMap.class));
|
||||
ScopeType scope = ScopeType.REQUEST;
|
||||
Expression valueExpression = parser.parseExpression("'" + valueToSet + "'", new ParserContextImpl()
|
||||
.eval(RequestContext.class));
|
||||
|
||||
SetAction action = new SetAction(attributeExpression, scope, valueExpression);
|
||||
|
||||
RequestContext context = new MockRequestContext();
|
||||
action.execute(context);
|
||||
|
||||
assertTrue(context.getRequestScope().contains("foo"));
|
||||
assertEquals(valueToSet, context.getRequestScope().get("foo"));
|
||||
}
|
||||
|
||||
public void testExecute_LegacyOGNL_ScopeExpression() throws Exception {
|
||||
|
||||
String valueToSet = "myValue";
|
||||
|
||||
OgnlExpressionParser parser = new WebFlowOgnlExpressionParser();
|
||||
parser.setAllowUndelimitedEvalExpressions(true);
|
||||
|
||||
Expression attributeExpression = parser.parseExpression("requestScope.foo", new ParserContextImpl()
|
||||
.eval(RequestContext.class));
|
||||
Expression valueExpression = parser.parseExpression("'" + valueToSet + "'", new ParserContextImpl()
|
||||
.eval(RequestContext.class));
|
||||
|
||||
SetAction action = new SetAction(attributeExpression, null, valueExpression);
|
||||
|
||||
RequestContext context = new MockRequestContext();
|
||||
action.execute(context);
|
||||
|
||||
assertTrue(context.getRequestScope().contains("foo"));
|
||||
assertEquals(valueToSet, context.getRequestScope().get("foo"));
|
||||
}
|
||||
|
||||
public class TestBean {
|
||||
|
||||
private String foo;
|
||||
|
||||
public String getFoo() {
|
||||
return foo;
|
||||
}
|
||||
|
||||
public void setFoo(String foo) {
|
||||
this.foo = foo;
|
||||
}
|
||||
public void testSetAction() throws Exception {
|
||||
StaticExpression name = new StaticExpression("");
|
||||
SetAction action = new SetAction(name, new StaticExpression("bar"));
|
||||
MockRequestContext context = new MockRequestContext();
|
||||
Event result = action.execute(context);
|
||||
assertEquals("success", result.getId());
|
||||
assertEquals("bar", name.getValue(null));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ package org.springframework.webflow.engine;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.binding.expression.EvaluationException;
|
||||
import org.springframework.binding.expression.support.AbstractGetValueExpression;
|
||||
import org.springframework.binding.mapping.DefaultAttributeMapper;
|
||||
import org.springframework.binding.mapping.Mapping;
|
||||
import org.springframework.binding.mapping.MappingBuilder;
|
||||
@@ -74,12 +76,16 @@ public class EndStateTests extends TestCase {
|
||||
}
|
||||
|
||||
public void testEnterEndStateTerminateFlowSession() {
|
||||
Flow subflow = new Flow("mySubflow");
|
||||
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", subflow);
|
||||
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");
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ package org.springframework.webflow.engine;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.binding.expression.EvaluationException;
|
||||
import org.springframework.binding.expression.support.AbstractGetValueExpression;
|
||||
import org.springframework.binding.mapping.AttributeMapper;
|
||||
import org.springframework.binding.mapping.MappingContext;
|
||||
import org.springframework.webflow.core.collection.AttributeMap;
|
||||
@@ -43,7 +45,11 @@ public class SubflowStateTests extends TestCase {
|
||||
public void setUp() {
|
||||
parentFlow = new Flow("parent");
|
||||
subflow = new Flow("child");
|
||||
subflowState = new SubflowState(parentFlow, "subflow", subflow);
|
||||
subflowState = new SubflowState(parentFlow, "subflow", new AbstractGetValueExpression() {
|
||||
public Object getValue(Object context) throws EvaluationException {
|
||||
return subflow;
|
||||
}
|
||||
});
|
||||
context = new MockRequestControlContext(parentFlow);
|
||||
context.setCurrentState(subflowState);
|
||||
}
|
||||
@@ -58,7 +64,7 @@ public class SubflowStateTests extends TestCase {
|
||||
}
|
||||
|
||||
public void testEnterWithInput() {
|
||||
subflowState.setAttributeMapper(new FlowAttributeMapper() {
|
||||
subflowState.setAttributeMapper(new SubflowAttributeMapper() {
|
||||
public MutableAttributeMap createFlowInput(RequestContext context) {
|
||||
return new LocalAttributeMap("foo", "bar");
|
||||
}
|
||||
@@ -81,7 +87,7 @@ public class SubflowStateTests extends TestCase {
|
||||
}
|
||||
|
||||
public void testReturnWithOutput() {
|
||||
subflowState.setAttributeMapper(new FlowAttributeMapper() {
|
||||
subflowState.setAttributeMapper(new SubflowAttributeMapper() {
|
||||
public MutableAttributeMap createFlowInput(RequestContext context) {
|
||||
return new LocalAttributeMap();
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ import org.springframework.webflow.core.collection.LocalAttributeMap;
|
||||
import org.springframework.webflow.core.collection.MutableAttributeMap;
|
||||
import org.springframework.webflow.execution.RequestContext;
|
||||
|
||||
class TestAttributeMapper implements FlowAttributeMapper {
|
||||
class TestSubflowAttributeMapper implements SubflowAttributeMapper {
|
||||
public MutableAttributeMap createFlowInput(RequestContext context) {
|
||||
LocalAttributeMap inputMap = new LocalAttributeMap();
|
||||
inputMap.put("childInputAttribute", context.getFlowScope().get("parentInputAttribute"));
|
||||
@@ -0,0 +1,14 @@
|
||||
<flow xmlns="http://www.springframework.org/schema/webflow"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
|
||||
|
||||
<start-actions>
|
||||
<evaluate expression="action"/>
|
||||
<evaluate expression="action" result="result"/>
|
||||
<evaluate expression="action" result="result" result-type="integer"/>
|
||||
<evaluate expression="multiAction.actionMethod" result="result" result-type="integer"/>
|
||||
</start-actions>
|
||||
|
||||
<end-state id="end" />
|
||||
|
||||
</flow>
|
||||
@@ -0,0 +1,11 @@
|
||||
<flow xmlns="http://www.springframework.org/schema/webflow"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
|
||||
|
||||
<start-actions>
|
||||
<evaluate expression="testBean.foo"/>
|
||||
</start-actions>
|
||||
|
||||
<end-state id="end"/>
|
||||
|
||||
</flow>
|
||||
@@ -4,33 +4,26 @@
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">
|
||||
|
||||
<view-state id="enterCriteria" view="searchCriteria">
|
||||
<render-actions>
|
||||
<action bean="formAction" method="setupForm"/>
|
||||
<render-actions>
|
||||
<evaluate expression="formAction.setupForm" />
|
||||
</render-actions>
|
||||
<transition on="search" to="displayResults">
|
||||
<action bean="formAction" method="bindAndValidate"/>
|
||||
<evaluate expression="formAction.bindAndValidate" />
|
||||
</transition>
|
||||
</view-state>
|
||||
|
||||
<view-state id="displayResults" view="searchResults">
|
||||
<render-actions>
|
||||
<bean-action bean="phonebook" method="search">
|
||||
<method-arguments>
|
||||
<argument expression="${flowScope.searchCriteria}"/>
|
||||
</method-arguments>
|
||||
<method-result name="results"/>
|
||||
</bean-action>
|
||||
<render-actions>
|
||||
<evaluate expression="phonebook.search(searchCriteria)" result="results" />
|
||||
</render-actions>
|
||||
<transition on="newSearch" to="enterCriteria"/>
|
||||
<transition on="select" to="browseDetails"/>
|
||||
</view-state>
|
||||
|
||||
<subflow-state id="browseDetails" flow="detail-flow">
|
||||
<attribute-mapper>
|
||||
<input-mapper>
|
||||
<mapping source="${requestParameters.id}" target="${id}" from="string" to="long"/>
|
||||
</input-mapper>
|
||||
</attribute-mapper>
|
||||
<subflow-state id="browseDetails" subflow="detail-flow">
|
||||
<input-mapper>
|
||||
<mapping source="${requestParameters.id}" target="${id}" from="string" to="long"/>
|
||||
</input-mapper>
|
||||
<transition on="finish" to="displayResults"/>
|
||||
</subflow-state>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user