SWF-463 - The "scope" attribute should be optional on <method-result>, <evaluation-result> and <set> tags

This commit is contained in:
Jeremy Grelle
2008-02-05 16:33:47 +00:00
parent a756a284cf
commit d93e0ac480
8 changed files with 419 additions and 33 deletions

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -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.

View File

@@ -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) {

View File

@@ -1018,7 +1018,7 @@ 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" default="default">
<xsd:attribute name="scope" type="scopeType" use="optional">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
@@ -1031,7 +1031,7 @@ The available scope types are:
<li>conversation - The result goes out of scope when the overall conversation governing this flow execution ends.
</ol>
<br>
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}".
]]>
</xsd:documentation>
</xsd:annotation>
@@ -1116,12 +1116,13 @@ An action with a name is often referred to as a "named action".
<xsd:documentation>
<![CDATA[
The name of the attribute that will expose the result of expression evaluation.
Used in conjunction with the 'scope' attribute.
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" default="default">
<xsd:attribute name="scope" type="scopeType" use="optional">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
@@ -1134,7 +1135,7 @@ The available scope types are:
<li>conversation - The result goes out of scope when the overall conversation governing this flow execution ends.
</ol>
<br>
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}".
]]>
</xsd:documentation>
</xsd:annotation>
@@ -1159,12 +1160,13 @@ An attribute describing this bean action.
<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).
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" default="default">
<xsd:attribute name="scope" type="scopeType" use="optional">
<xsd:annotation>
<xsd:documentation>
<![CDATA[
@@ -1176,7 +1178,7 @@ The scope the attribute value will be stored in. The available scope types are:
<li>conversation - The attribute goes out of scope when the overall conversation governing this flow execution ends.
</ol>
<br>
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}".
]]>
</xsd:documentation>
</xsd:annotation>

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}