diff --git a/spring-webflow/src/main/java/org/springframework/webflow/action/ActionResultExposer.java b/spring-webflow/src/main/java/org/springframework/webflow/action/ActionResultExposer.java index ebbb875e..6791b7e6 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/action/ActionResultExposer.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/action/ActionResultExposer.java @@ -17,8 +17,10 @@ package org.springframework.webflow.action; import java.io.Serializable; +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; @@ -36,7 +38,7 @@ public class ActionResultExposer implements Serializable { /** * The name of the attribute to index the return value with. */ - private String resultName; + private Expression nameExpression; /** * The scope of the attribute indexing the return value. @@ -45,21 +47,20 @@ public class ActionResultExposer implements Serializable { /** * Creates a action result exposer - * @param resultName the result name + * @param nameExpression the result name * @param resultScope the result scope */ - public ActionResultExposer(String resultName, ScopeType resultScope) { - Assert.notNull(resultName, "The result name is required"); - Assert.notNull(resultScope, "The result scope is required"); - this.resultName = resultName; + public ActionResultExposer(Expression nameExpression, ScopeType resultScope) { + Assert.notNull(nameExpression, "The result name is required"); + this.nameExpression = nameExpression; this.resultScope = resultScope; } /** * Returns name of the attribute to index the return value with. */ - public String getResultName() { - return resultName; + public Expression getNameExpression() { + return nameExpression; } /** @@ -75,10 +76,16 @@ public class ActionResultExposer implements Serializable { * @param context the request context */ public void exposeResult(Object result, RequestContext context) { - resultScope.getScope(context).put(resultName, result); + if (resultScope != null) { + MutableAttributeMap scopeMap = resultScope.getScope(context); + nameExpression.setValue(scopeMap, result); + } else { + nameExpression.setValue(context, result); + } } public String toString() { - return new ToStringCreator(this).append("resultName", resultName).append("resultScope", resultScope).toString(); + return new ToStringCreator(this).append("resultName", nameExpression).append("resultScope", resultScope) + .toString(); } } \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/action/SetAction.java b/spring-webflow/src/main/java/org/springframework/webflow/action/SetAction.java index ec956752..ad50a366 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/action/SetAction.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/action/SetAction.java @@ -52,7 +52,6 @@ public class SetAction extends AbstractAction { */ public SetAction(Expression attributeExpression, ScopeType scope, Expression valueExpression) { Assert.notNull(attributeExpression, "The attribute expression is required"); - Assert.notNull(scope, "The scope type is required"); Assert.notNull(valueExpression, "The value expression is required"); this.attributeExpression = attributeExpression; this.scope = scope; @@ -61,8 +60,12 @@ public class SetAction extends AbstractAction { protected Event doExecute(RequestContext context) throws Exception { Object value = valueExpression.getValue(context); - MutableAttributeMap scopeMap = scope.getScope(context); - attributeExpression.setValue(scopeMap, value); + if (scope != null) { + MutableAttributeMap scopeMap = scope.getScope(context); + attributeExpression.setValue(scopeMap, value); + } else { + attributeExpression.setValue(context, value); + } return success(); } } \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/core/expression/WebFlowOgnlExpressionParser.java b/spring-webflow/src/main/java/org/springframework/webflow/core/expression/WebFlowOgnlExpressionParser.java index 4244dad4..dc243fd4 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/core/expression/WebFlowOgnlExpressionParser.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/core/expression/WebFlowOgnlExpressionParser.java @@ -31,7 +31,7 @@ import org.springframework.webflow.execution.RequestContext; * * @author Keith Donald */ -class WebFlowOgnlExpressionParser extends OgnlExpressionParser { +public class WebFlowOgnlExpressionParser extends OgnlExpressionParser { /** * Creates a webflow-specific ognl expression parser. diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilder.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilder.java index b85f1f3d..ec1a4b8f 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilder.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/XmlFlowBuilder.java @@ -805,8 +805,17 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde } private ActionResultExposer parseActionResultExposer(Element element) { - String resultName = element.getAttribute(NAME_ATTRIBUTE); - return new ActionResultExposer(resultName, parseScope(element, ScopeType.REQUEST)); + String nameExpressionString = element.getAttribute(NAME_ATTRIBUTE); + ScopeType scope = parseScope(element, null); + Expression nameExpression; + if (scope != null) { + nameExpression = getExpressionParser().parseExpression(nameExpressionString, + new ParserContextImpl().eval(MutableAttributeMap.class)); + } else { + nameExpression = getExpressionParser().parseExpression(nameExpressionString, + new ParserContextImpl().eval(RequestContext.class)); + } + return new ActionResultExposer(nameExpression, scope); } private AnnotatedAction parseAnnotatedEvaluateAction(Element element) { @@ -839,12 +848,20 @@ public class XmlFlowBuilder extends AbstractFlowBuilder implements ResourceHolde } private Action parseSetAction(Element element) { + ScopeType scope = parseScope(element, null); String attributeExpressionString = element.getAttribute(ATTRIBUTE_ATTRIBUTE); - Expression attributeExpression = getExpressionParser().parseExpression(attributeExpressionString, - new ParserContextImpl().eval(MutableAttributeMap.class)); + Expression attributeExpression; + if (scope != null) { + attributeExpression = getExpressionParser().parseExpression(attributeExpressionString, + new ParserContextImpl().eval(MutableAttributeMap.class)); + } else { + attributeExpression = getExpressionParser().parseExpression(attributeExpressionString, + new ParserContextImpl().eval(RequestContext.class)); + } + Expression valueExpression = getExpressionParser().parseExpression(element.getAttribute(VALUE_ATTRIBUTE), new ParserContextImpl().eval(RequestContext.class)); - return new SetAction(attributeExpression, parseScope(element, ScopeType.REQUEST), valueExpression); + return new SetAction(attributeExpression, scope, valueExpression); } private ScopeType parseScope(Element element, ScopeType defaultValue) { diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/spring-webflow-2.0.xsd b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/spring-webflow-2.0.xsd index 983cf1f9..6f138c7d 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/spring-webflow-2.0.xsd +++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/xml/spring-webflow-2.0.xsd @@ -1018,7 +1018,7 @@ Can be a simple literal when used in conjunction with the 'scope' attribute. - + conversation - The result goes out of scope when the overall conversation governing this flow execution ends.
-If not specified the default scope type is used ('request' by default). +If not specified then the name attribute must be a fully resolvable expression such as "#{flowScope.myResult}". ]]>
@@ -1116,12 +1116,13 @@ An action with a name is often referred to as a "named action".
- + conversation - The result goes out of scope when the overall conversation governing this flow execution ends.
-If not specified the default scope type is used ('request' by default). +If not specified the name attribute must be a fully resolvable expression such as "#{flowScope.foo}". ]]>
@@ -1159,12 +1160,13 @@ An attribute describing this bean action.
- + conversation - The attribute goes out of scope when the overall conversation governing this flow execution ends.
-If not specified the default scope type is used ('request' by default). +If not specified the attribute to set must be a fully resolvable expression such as "#{flowScope.foo}". ]]>
diff --git a/spring-webflow/src/test/java/org/springframework/webflow/action/ActionResultExposerTests.java b/spring-webflow/src/test/java/org/springframework/webflow/action/ActionResultExposerTests.java new file mode 100644 index 00000000..88a8c62d --- /dev/null +++ b/spring-webflow/src/test/java/org/springframework/webflow/action/ActionResultExposerTests.java @@ -0,0 +1,121 @@ +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.ScopeType; +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); + + 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_ScopeExpression() { + + 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); + + 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_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); + + RequestContext context = new MockRequestContext(); + 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); + + 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); + + 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; + } + } +} diff --git a/spring-webflow/src/test/java/org/springframework/webflow/action/EvaluateActionTests.java b/spring-webflow/src/test/java/org/springframework/webflow/action/EvaluateActionTests.java index 6ba8ff26..a6ca6f0d 100644 --- a/spring-webflow/src/test/java/org/springframework/webflow/action/EvaluateActionTests.java +++ b/spring-webflow/src/test/java/org/springframework/webflow/action/EvaluateActionTests.java @@ -17,9 +17,15 @@ 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.TestBean; +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.ScopeType; import org.springframework.webflow.test.MockRequestContext; @@ -42,19 +48,69 @@ public class EvaluateActionTests extends TestCase { assertNull(context.getFlowScope().get("baz")); } - public void testEvaluateExpressionResult() throws Exception { - EvaluateAction action = new EvaluateAction(new StaticExpression("bar"), new ActionResultExposer("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)); Event result = action.execute(context); assertEquals("bar", result.getId()); assertEquals("bar", context.getFlowScope().get("baz")); } - public void testBeanResult() throws Exception { - EvaluateAction action = new EvaluateAction(new StaticExpression(new TestBean()), new ActionResultExposer("baz", + 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)); Event result = action.execute(context); assertEquals("success", result.getId()); - assertEquals(new TestBean(), context.getFlowScope().get("baz")); + assertEquals(bean, 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)); + 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)); + 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; + } } } diff --git a/spring-webflow/src/test/java/org/springframework/webflow/action/SetActionTests.java b/spring-webflow/src/test/java/org/springframework/webflow/action/SetActionTests.java new file mode 100644 index 00000000..72235177 --- /dev/null +++ b/spring-webflow/src/test/java/org/springframework/webflow/action/SetActionTests.java @@ -0,0 +1,180 @@ +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.ScopeType; +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(); + 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(); + 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; + } + } +}