Remove OGNL expression support

Issue: SWF-1694
This commit is contained in:
Rossen Stoyanchev
2017-01-06 16:02:53 -05:00
parent 4fb1b99a8b
commit b263c1c87a
14 changed files with 22 additions and 980 deletions

View File

@@ -121,9 +121,6 @@ subprojects { subproject ->
project("spring-binding") {
description = "Spring Binding"
dependencies {
compile("opensymphony:ognl:2.6.11")
}
}
project("spring-webflow") {
@@ -131,7 +128,6 @@ project("spring-webflow") {
dependencies {
compile(project(":spring-binding"))
compile("opensymphony:ognl:2.6.11")
compile("org.springframework:spring-web:$springVersion")
compile("org.springframework:spring-webmvc:$springVersion")
provided("javax.servlet:javax.servlet-api:$servletVersion")
@@ -155,7 +151,6 @@ project("spring-webflow") {
optional("org.apache.tiles:tiles-extras:$tiles3Version") {
exclude group: "org.slf4j", module: "jcl-over-slf4j"
exclude group: "org.springframework", module: "spring-web"
exclude group: "ognl", module: "ognl"
}
provided("junit:junit:3.8.2")
testCompile("org.springframework:spring-aop:$springVersion")

View File

@@ -17,8 +17,8 @@ package org.springframework.binding.expression;
/**
* An expression capable of evaluating itself against context objects. Encapsulates the details of a previously parsed
* expression string. Provides a common abstraction for expression evaluation independent of any language like OGNL or
* the Unified EL.
* expression string. Provides a common abstraction for expression evaluation independent of any language like
* Spring EL or the Unified EL.
*
* @author Keith Donald
*/

View File

@@ -1,184 +0,0 @@
/*
* Copyright 2004-2012 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.binding.expression.ognl;
import java.lang.reflect.Member;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import ognl.NoSuchPropertyException;
import ognl.Ognl;
import ognl.OgnlException;
import ognl.TypeConverter;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.InvalidPropertyException;
import org.springframework.binding.convert.ConversionException;
import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.expression.EvaluationException;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.PropertyNotFoundException;
import org.springframework.binding.expression.ValueCoercionException;
import org.springframework.binding.expression.spel.SpringELExpression;
/**
* Evaluates a parsed Ognl expression.
*
* @author Keith Donald
* @author Scott Andrews
*
* @deprecated in favor of Spring EL, see {@link SpringELExpression}.
*/
class OgnlExpression implements Expression {
private Object expression;
private Map<String, Expression> variableExpressions;
private Class<?> expectedResultType;
private String expressionString;
private ConversionService conversionService;
/**
* Creates a new OGNL expression.
*/
public OgnlExpression(Object expression, Map<String, Expression> variableExpressions, Class<?> expectedResultType,
String expressionString, ConversionService conversionService) {
this.expression = expression;
this.variableExpressions = variableExpressions;
this.expectedResultType = expectedResultType;
this.expressionString = expressionString;
this.conversionService = conversionService;
}
public boolean equals(Object o) {
if (!(o instanceof OgnlExpression)) {
return false;
}
OgnlExpression other = (OgnlExpression) o;
return expressionString.equals(other.expressionString);
}
public int hashCode() {
return expressionString.hashCode();
}
@SuppressWarnings("rawtypes")
public Object getValue(Object context) throws EvaluationException {
try {
Map evaluationContext = Ognl.addDefaultContext(context, getVariables(context));
Ognl.setTypeConverter(evaluationContext, createTypeConverter());
return Ognl.getValue(expression, evaluationContext, context, expectedResultType);
} catch (NoSuchPropertyException e) {
throw new PropertyNotFoundException(context.getClass(), getExpressionString(), e);
} catch (OgnlException e) {
if (e.getReason() instanceof ValueCoercionException) {
throw (ValueCoercionException) e.getReason();
} else {
throw new EvaluationException(context.getClass(), getExpressionString(),
"An OgnlException occurred getting the value for expression '" + getExpressionString()
+ "' on context [" + context.getClass() + "]", causeFor(e));
}
}
}
@SuppressWarnings("rawtypes")
public void setValue(Object context, Object value) {
try {
Map evaluationContext = Ognl.addDefaultContext(context, getVariables(context));
Ognl.setTypeConverter(evaluationContext, createTypeConverter());
Ognl.setValue(expression, evaluationContext, context, value);
} catch (NoSuchPropertyException e) {
throw new PropertyNotFoundException(context.getClass(), getExpressionString(), e);
} catch (OgnlException e) {
if (e.getReason() instanceof ValueCoercionException) {
throw (ValueCoercionException) e.getReason();
} else {
throw new EvaluationException(context.getClass(), getExpressionString(),
"An OgnlException occurred setting the value of expression '" + getExpressionString()
+ "' on context [" + context.getClass() + "] to [" + value + "]", causeFor(e));
}
}
}
public Class<?> getValueType(Object context) {
try {
// OGNL has no native way to get this information
return new BeanWrapperImpl(context).getPropertyType(expressionString);
} catch (InvalidPropertyException e) {
throw new PropertyNotFoundException(context.getClass(), getExpressionString(), e);
} catch (BeansException e) {
throw new EvaluationException(context.getClass(), getExpressionString(),
"An BeansException occurred getting the value type for expression '" + getExpressionString()
+ "' on context [" + context.getClass() + "]", e);
}
}
public String getExpressionString() {
return expressionString;
}
// internal helpers
private Throwable causeFor(OgnlException e) {
if (e.getReason() != null) {
if (e.getCause() == null) {
try {
e.initCause(e.getReason());
} catch (IllegalStateException ex) {
// we tried
}
}
return e;
} else {
return e;
}
}
@SuppressWarnings("rawtypes")
private TypeConverter createTypeConverter() {
return new TypeConverter() {
public Object convertValue(Map context, Object target, Member member, String propertyName, Object value,
Class toType) throws ValueCoercionException {
try {
return conversionService.executeConversion(value, toType);
} catch (ConversionException e) {
throw new ValueCoercionException(context.getClass(), expressionString, value, toType, e);
}
}
};
}
private Map<String, Object> getVariables(Object context) {
if (variableExpressions == null) {
return Collections.emptyMap();
}
Map<String, Object> variables = new HashMap<String, Object>(variableExpressions.size(), 1);
for (Map.Entry<String, Expression> var : variableExpressions.entrySet()) {
Expression valueExpression = var.getValue();
variables.put(var.getKey(), valueExpression.getValue(context));
}
return variables;
}
public String toString() {
return expressionString;
}
}

View File

@@ -1,76 +0,0 @@
/*
* Copyright 2004-2012 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.binding.expression.ognl;
import ognl.Ognl;
import ognl.OgnlException;
import ognl.OgnlRuntime;
import ognl.PropertyAccessor;
import org.springframework.binding.convert.ConversionService;
import org.springframework.binding.convert.service.DefaultConversionService;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ParserContext;
import org.springframework.binding.expression.ParserException;
import org.springframework.binding.expression.spel.SpringELExpressionParser;
import org.springframework.binding.expression.support.AbstractExpressionParser;
/**
* An expression parser that parses Ognl expressions.
*
* @author Keith Donald
*
* @deprecated in favor of Spring EL, see {@link SpringELExpressionParser}
*/
public class OgnlExpressionParser extends AbstractExpressionParser {
private ConversionService conversionService = new DefaultConversionService();
/**
* The conversion service to use to perform type conversions as needed by the OGNL system. If not specified, the
* default is an instance of {@link DefaultConversionService}.
*/
public ConversionService getConversionService() {
return conversionService;
}
/**
* Sets the conversion service to use to perform type conversions as needed by the OGNL system.
* @param conversionService the conversion service to use
*/
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
/**
* Add a property access strategy for the given class.
* @param clazz the class that contains properties needing access
* @param propertyAccessor the property access strategy
*/
public void addPropertyAccessor(Class<?> clazz, PropertyAccessor propertyAccessor) {
OgnlRuntime.setPropertyAccessor(clazz, propertyAccessor);
}
protected Expression doParseExpression(String expressionString, ParserContext context) throws ParserException {
try {
return new OgnlExpression(Ognl.parseExpression(expressionString),
parseVariableExpressions(context.getExpressionVariables()),
context.getExpectedEvaluationResultType(), expressionString, conversionService);
} catch (OgnlException e) {
throw new ParserException(expressionString, e);
}
}
}

View File

@@ -1,21 +0,0 @@
/*
* Copyright 2004-2012 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.
*/
/**
* Support for the OGNL Expression Language implemented by the OgnlExpressionParser.
*/
package org.springframework.binding.expression.ognl;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2004-2012 the original author or authors.
* Copyright 2004-2016 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.
@@ -28,9 +28,10 @@ import org.springframework.binding.expression.ParserException;
import org.springframework.util.Assert;
/**
* An expression parser that parses Ognl expressions.
* Abstract base class for parsing ${...} style expressions.
*
* @author Keith Donald
* @see org.springframework.binding.expression.beanwrapper.BeanWrapperExpressionParser
*/
public abstract class AbstractExpressionParser implements ExpressionParser {
@@ -117,14 +118,15 @@ public abstract class AbstractExpressionParser implements ExpressionParser {
if (!allowDelimitedEvalExpressions) {
throw new ParserException(
expressionString,
"The expression '"
+ expressionString
+ "' being parsed is expected be a standard OGNL expression. Do not attempt to enclose such expression strings in ${} delimiters--this is redundant. If you need to parse a template that mixes literal text with evaluatable blocks, set the 'template' parser context attribute to true.",
"The expression '" + expressionString + "' being parsed is expected be an expression. " +
"Do not enclose such expression strings in ${} delimiters as it's redundant. " +
"If you need to parse a template that mixes literal text with evaluatable blocks, " +
"set the 'template' parser context attribute to true.",
null);
} else {
int lastIndex = expressionString.length() - getExpressionSuffix().length();
String ognlExpression = expressionString.substring(getExpressionPrefix().length(), lastIndex);
return doParseExpression(ognlExpression, context);
String expression = expressionString.substring(getExpressionPrefix().length(), lastIndex);
return doParseExpression(expression, context);
}
} else {
return doParseExpression(expressionString, context);

View File

@@ -23,7 +23,6 @@ import org.springframework.binding.convert.service.GenericConversionService;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ParserException;
import org.springframework.binding.expression.ValueCoercionException;
import org.springframework.binding.expression.ognl.TestBean;
import org.springframework.binding.expression.support.FluentParserContext;
public class BeanWrapperExpressionParserTests extends TestCase {

View File

@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.binding.expression.ognl;
package org.springframework.binding.expression.beanwrapper;
import java.util.ArrayList;
import java.util.Date;

View File

@@ -1,243 +0,0 @@
/*
* Copyright 2004-2012 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.binding.expression.ognl;
import junit.framework.TestCase;
import org.springframework.binding.convert.converters.StringToDate;
import org.springframework.binding.convert.service.GenericConversionService;
import org.springframework.binding.expression.EvaluationException;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ExpressionVariable;
import org.springframework.binding.expression.ParserException;
import org.springframework.binding.expression.ValueCoercionException;
import org.springframework.binding.expression.support.FluentParserContext;
public class OgnlExpressionParserTests extends TestCase {
private OgnlExpressionParser parser = new OgnlExpressionParser();
private TestBean bean = new TestBean();
public void testParseSimple() {
String exp = "flag";
Expression e = parser.parseExpression(exp, null);
assertNotNull(e);
Boolean b = (Boolean) e.getValue(bean);
assertFalse(b);
}
public void testParseSimpleAllowDelimited() {
parser.setAllowDelimitedEvalExpressions(true);
String exp = "${flag}";
Expression e = parser.parseExpression(exp, null);
assertNotNull(e);
Boolean b = (Boolean) e.getValue(bean);
assertFalse(b);
}
public void testParseSimpleDelimitedNotAllowed() {
String exp = "${flag}";
try {
parser.parseExpression(exp, null);
fail("should have failed");
} catch (ParserException e) {
}
}
public void testParseTemplateSimpleLiteral() {
String exp = "flag";
Expression e = parser.parseExpression(exp, new FluentParserContext().template());
assertNotNull(e);
assertEquals("flag", e.getValue(bean));
}
public void testParseTemplateEmpty() {
Expression e = parser.parseExpression("", new FluentParserContext().template());
assertNotNull(e);
assertEquals("", e.getValue(bean));
}
public void testParseTemplateComposite() {
String exp = "hello ${flag} ${flag} ${flag}";
Expression e = parser.parseExpression(exp, new FluentParserContext().template());
assertNotNull(e);
String str = (String) e.getValue(bean);
assertEquals("hello false false false", str);
}
public void testTemplateEnclosedCompositeNotSupported() {
String exp = "${hello ${flag} ${flag} ${flag}}";
try {
parser.parseExpression(exp, new FluentParserContext().template());
fail("Should've failed - not intended use");
} catch (ParserException e) {
}
}
public void testSyntaxError1() {
try {
parser.parseExpression("${", new FluentParserContext().template());
fail();
} catch (ParserException e) {
}
try {
String exp = "hello ${flag} ${abcd defg";
parser.parseExpression(exp, null);
fail("Should've failed - not intended use");
} catch (ParserException e) {
}
}
public void testSyntaxError2() {
try {
parser.parseExpression("${}", new FluentParserContext().template());
fail("Should've failed - not intended use");
} catch (ParserException e) {
}
try {
String exp = "hello ${flag} ${}";
parser.parseExpression(exp, null);
fail("Should've failed - not intended use");
} catch (ParserException e) {
}
}
public void testCollectionConstructionSyntax() {
// lists
parser.parseExpression("name in {null, \"Untitled\"}", null);
parser.parseExpression("${name in {null, \"Untitled\"}}", new FluentParserContext().template());
// native arrays
parser.parseExpression("new int[] {1, 2, 3}", null);
parser.parseExpression("${new int[] {1, 2, 3}}", new FluentParserContext().template());
// maps
parser.parseExpression("#{ 'foo' : 'foo value', 'bar' : 'bar value' }", null);
parser.parseExpression("${#{ 'foo' : 'foo value', 'bar' : 'bar value' }}", new FluentParserContext().template());
parser.parseExpression("#@java.util.LinkedHashMap@{ 'foo' : 'foo value', 'bar' : 'bar value' }", null);
parser.parseExpression("${#@java.util.LinkedHashMap@{ 'foo' : 'foo value', 'bar' : 'bar value' }}",
new FluentParserContext().template());
// complex examples
parser.parseExpression("b,#{1:2}", null);
parser.parseExpression("${b,#{1:2}}", new FluentParserContext().template());
parser.parseExpression("a${b,#{1:2},e}f${g,#{3:4},j}k", new FluentParserContext().template());
}
public void testVariables() {
Expression exp = parser.parseExpression("#var",
new FluentParserContext().variable(new ExpressionVariable("var", "flag")));
assertFalse((Boolean) exp.getValue(bean));
}
public void testVariablesWithCoersion() {
Expression exp = parser.parseExpression("#var", new FluentParserContext().variable(new ExpressionVariable(
"var", "number", new FluentParserContext().expectResult(Long.class))));
assertEquals(new Long(0), exp.getValue(bean));
}
public void testNestedVariablesWithTemplates() {
Expression exp = parser.parseExpression("#var", new FluentParserContext().variable(new ExpressionVariable(
"var", "${flag}${#var}", new FluentParserContext().template().variable(
new ExpressionVariable("var", "number")))));
assertEquals("false0", exp.getValue(bean));
}
public void testGetExpressionString() {
String expressionString = "maximum";
Expression exp = parser.parseExpression(expressionString, null);
assertEquals("maximum", exp.getExpressionString());
}
public void testGetValueType() {
String exp = "flag";
Expression e = parser.parseExpression(exp, null);
assertEquals(boolean.class, e.getValueType(bean));
}
public void testGetValueTypeNullCollectionValue() {
String exp = "list[0]";
Expression e = parser.parseExpression(exp, null);
assertEquals(null, e.getValueType(bean));
}
public void testGetValueWithCoersion() {
String expressionString = "number";
Expression exp = parser.parseExpression(expressionString, new FluentParserContext().expectResult(String.class));
TestBean context = new TestBean();
assertEquals("0", exp.getValue(context));
}
public void testGetValueCoersionError() {
String expressionString = "number";
Expression exp = parser.parseExpression(expressionString,
new FluentParserContext().expectResult(TestBean.class));
TestBean context = new TestBean();
try {
exp.getValue(context);
fail("Should have failed with coersion");
} catch (ValueCoercionException e) {
}
}
public void testSetValue() {
String expressionString = "number";
Expression exp = parser.parseExpression(expressionString, null);
TestBean context = new TestBean();
exp.setValue(context, 5);
assertEquals(5, context.getNumber());
}
public void testSetValueWithCoersion() {
GenericConversionService cs = (GenericConversionService) parser.getConversionService();
StringToDate converter = new StringToDate();
converter.setPattern("yyyy-MM-dd");
cs.addConverter(converter);
Expression e = parser.parseExpression("date", null);
e.setValue(bean, "2008-9-15");
}
public void testSetBogusValueWithCoersion() {
Expression e = parser.parseExpression("date", null);
try {
e.setValue(bean, "bogus");
fail("Should have failed tme");
} catch (ValueCoercionException ex) {
}
}
public void testReasonCauseLinkingGetValue() {
String exp = "getException()";
Expression e = parser.parseExpression(exp, null);
try {
e.getValue(bean);
} catch (EvaluationException ex) {
assertTrue(ex.getCause().getCause() instanceof IllegalStateException);
}
}
public void testReasonCauseLinkingSetValue() {
String exp = "exceptionProperty";
Expression e = parser.parseExpression(exp, null);
try {
e.setValue(bean, "does not matter");
} catch (EvaluationException ex) {
assertTrue(ex.getCause().getCause() instanceof IllegalStateException);
}
}
}

View File

@@ -1,200 +0,0 @@
/*
* Copyright 2004-2012 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.expression;
import java.util.Map;
import ognl.ObjectPropertyAccessor;
import ognl.Ognl;
import ognl.OgnlException;
import ognl.PropertyAccessor;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.support.StaticListableBeanFactory;
import org.springframework.binding.collection.MapAdaptable;
import org.springframework.binding.expression.ognl.OgnlExpressionParser;
import org.springframework.context.MessageSource;
import org.springframework.webflow.context.ExternalContext;
import org.springframework.webflow.context.ExternalContextHolder;
import org.springframework.webflow.core.collection.MutableAttributeMap;
import org.springframework.webflow.execution.Action;
import org.springframework.webflow.execution.AnnotatedAction;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.expression.spel.WebFlowSpringELExpressionParser;
/**
* An extension of {@link OgnlExpressionParser} that registers Web Flow-specific PropertyAccessors.
*
* @author Keith Donald
*
* @deprecated in favor of Spring EL, see {@link WebFlowSpringELExpressionParser}
*/
@SuppressWarnings("rawtypes")
public class WebFlowOgnlExpressionParser extends OgnlExpressionParser {
/**
* Creates a Web Flow OGNL Expression Parser.
*/
public WebFlowOgnlExpressionParser() {
addPropertyAccessor(MapAdaptable.class, new MapAdaptablePropertyAccessor());
addPropertyAccessor(MutableAttributeMap.class, new MutableAttributeMapPropertyAccessor());
addPropertyAccessor(MessageSource.class, new MessageSourcePropertyAccessor());
addPropertyAccessor(RequestContext.class, new RequestContextPropertyAccessor(new ObjectPropertyAccessor()));
addPropertyAccessor(Action.class, new ActionPropertyAccessor());
}
/**
* Resolves Map Adaptable properties.
*/
private static class MapAdaptablePropertyAccessor implements PropertyAccessor {
public Object getProperty(Map context, Object target, Object name) throws OgnlException {
return ((MapAdaptable) target).asMap().get(name);
}
public void setProperty(Map context, Object target, Object name, Object value) throws OgnlException {
throw new UnsupportedOperationException(
"Cannot mutate immutable attribute collections; operation disallowed");
}
}
/**
* Resolves Mutable Attribute Map properties, also capable of setting properties.
*/
private static class MutableAttributeMapPropertyAccessor extends MapAdaptablePropertyAccessor {
@SuppressWarnings("unchecked")
public void setProperty(Map context, Object target, Object name, Object value) throws OgnlException {
((MutableAttributeMap) target).put((String) name, value);
}
}
/**
* Resolves RequestContext properties. Supports several implicit variables and scope searching routines.
*/
private static class RequestContextPropertyAccessor implements PropertyAccessor {
private static final BeanFactory EMPTY_BEAN_FACTORY = new StaticListableBeanFactory();
private PropertyAccessor delegate;
public RequestContextPropertyAccessor(PropertyAccessor delegate) {
this.delegate = delegate;
}
public Object getProperty(Map context, Object target, Object name) throws OgnlException {
String property = name.toString();
RequestContext requestContext = (RequestContext) target;
if (property.equals("flowRequestContext")) {
return requestContext;
}
if (property.equals("currentUser")) {
return requestContext.getExternalContext().getCurrentUser();
}
if (property.equals("resourceBundle")) {
return requestContext.getActiveFlow().getApplicationContext();
}
if (requestContext.getRequestScope().contains(property)) {
return requestContext.getRequestScope().get(property);
} else if (requestContext.getFlashScope().contains(property)) {
return requestContext.getFlashScope().get(property);
} else if (requestContext.inViewState() && requestContext.getViewScope().contains(property)) {
return requestContext.getViewScope().get(property);
} else if (requestContext.getFlowScope().contains(property)) {
return requestContext.getFlowScope().get(property);
} else if (requestContext.getConversationScope().contains(property)) {
return requestContext.getConversationScope().get(property);
}
BeanFactory bf = getBeanFactory(requestContext);
if (bf.containsBean(property)) {
return bf.getBean(property);
}
return delegate.getProperty(context, target, name);
}
public void setProperty(Map context, Object target, Object name, Object value) throws OgnlException {
String property = name.toString();
RequestContext requestContext = (RequestContext) target;
if (property.equals("flowRequestContext")) {
throw new OgnlException("The 'flowRequestContext' variable is not writeable");
}
if (property.equals("currentUser")) {
throw new OgnlException("The 'currentUser' variable is not writeable");
}
if (property.equals("resourceBundle")) {
throw new OgnlException("The 'resourceBundle' variable is not writeable");
}
if (requestContext.getRequestScope().contains(property)) {
requestContext.getRequestScope().put(property, value);
} else if (requestContext.getFlashScope().contains(property)) {
requestContext.getFlashScope().put(property, value);
} else if (requestContext.inViewState() && requestContext.getViewScope().contains(property)) {
requestContext.getViewScope().put(property, value);
} else if (requestContext.getFlowScope().contains(property)) {
requestContext.getFlowScope().put(property, value);
} else if (requestContext.getConversationScope().contains(property)) {
requestContext.getConversationScope().put(property, value);
} else {
delegate.setProperty(context, target, name, value);
}
}
private BeanFactory getBeanFactory(RequestContext requestContext) {
BeanFactory beanFactory = requestContext.getActiveFlow().getApplicationContext();
return beanFactory != null ? beanFactory : EMPTY_BEAN_FACTORY;
}
}
/**
* Resolves multi action methods.
*/
private static class ActionPropertyAccessor implements PropertyAccessor {
public Object getProperty(Map context, Object target, Object name) throws OgnlException {
Action action = (Action) target;
AnnotatedAction annotated = new AnnotatedAction(action);
annotated.setMethod(name.toString());
return annotated;
}
public void setProperty(Map context, Object target, Object name, Object value) throws OgnlException {
throw new OgnlException("Cannot set properties on a Action instance - operation not allowed");
}
}
/**
* Resolves messages.
*/
private static class MessageSourcePropertyAccessor implements PropertyAccessor {
public Object getProperty(Map context, Object target, Object name) throws OgnlException {
MessageSource messageSource = (MessageSource) target;
ExternalContext externalContext;
Object root = Ognl.getRoot(context);
if (root instanceof RequestContext) {
externalContext = ((RequestContext) root).getExternalContext();
} else {
externalContext = ExternalContextHolder.getExternalContext();
}
if (externalContext != null) {
return messageSource.getMessage(name.toString(), null, null, externalContext.getLocale());
} else {
return messageSource.getMessage(name.toString(), null, null, null);
}
}
public void setProperty(Map context, Object target, Object name, Object value) throws OgnlException {
throw new OgnlException("Cannot set properties on a MessageSource instance - operation not allowed");
}
}
}

View File

@@ -1,199 +0,0 @@
package org.springframework.webflow.expression;
import java.security.Principal;
import java.util.Locale;
import junit.framework.TestCase;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.support.FluentParserContext;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.webflow.TestBean;
import org.springframework.webflow.action.FormAction;
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.StubViewFactory;
import org.springframework.webflow.engine.ViewState;
import org.springframework.webflow.execution.AnnotatedAction;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.TestAction;
import org.springframework.webflow.test.MockRequestContext;
import org.springframework.webflow.test.MockRequestControlContext;
public class WebFlowOgnlExpressionParserTests extends TestCase {
private WebFlowOgnlExpressionParser parser = new WebFlowOgnlExpressionParser();
public void testResolveMap() {
LocalAttributeMap<Object> map = new LocalAttributeMap<Object>();
map.put("foo", "bar");
Expression exp = parser.parseExpression("foo", new FluentParserContext().evaluate(AttributeMap.class));
Expression exp2 = parser.parseExpression("bogus", new FluentParserContext().evaluate(AttributeMap.class));
assertEquals("bar", exp.getValue(map));
assertEquals(null, exp2.getValue(map));
}
public void testSetMap() {
LocalAttributeMap<Object> map = new LocalAttributeMap<Object>();
map.put("foo", "bar");
Expression exp = parser.parseExpression("foo", new FluentParserContext().evaluate(MutableAttributeMap.class));
Expression exp2 = parser
.parseExpression("bogus", new FluentParserContext().evaluate(MutableAttributeMap.class));
exp.setValue(map, "baz");
exp2.setValue(map, "new");
assertEquals("baz", exp.getValue(map));
assertEquals("new", exp2.getValue(map));
}
public void testResolveFlowRequestContext() {
MockRequestContext context = new MockRequestContext();
Expression exp = parser.parseExpression("flowRequestContext",
new FluentParserContext().evaluate(RequestContext.class));
assertSame(context, exp.getValue(context));
}
public void testResolveCurrentUser() {
MockRequestContext context = new MockRequestContext();
context.getMockExternalContext().setCurrentUser("Keith");
Expression exp = parser
.parseExpression("currentUser", new FluentParserContext().evaluate(RequestContext.class));
assertEquals("Keith", ((Principal) exp.getValue(context)).getName());
}
public void testResolveRequestScope() {
MockRequestContext context = new MockRequestContext();
context.getRequestScope().put("foo", "bar");
Expression exp = parser.parseExpression("foo", new FluentParserContext().evaluate(RequestContext.class));
assertEquals("bar", exp.getValue(context));
}
public void testSetRequestScope() {
MockRequestContext context = new MockRequestContext();
context.getRequestScope().put("foo", "bar");
Expression exp = parser.parseExpression("foo", new FluentParserContext().evaluate(RequestContext.class));
exp.setValue(context, "baz");
assertEquals("baz", exp.getValue(context));
}
public void testResolveFlashScope() {
MockRequestContext context = new MockRequestContext();
context.getFlashScope().put("foo", "bar");
Expression exp = parser.parseExpression("foo", new FluentParserContext().evaluate(RequestContext.class));
assertEquals("bar", exp.getValue(context));
}
public void testSetFlashScope() {
MockRequestContext context = new MockRequestContext();
context.getFlashScope().put("foo", "bar");
Expression exp = parser.parseExpression("foo", new FluentParserContext().evaluate(RequestContext.class));
exp.setValue(context, "baz");
assertEquals("baz", exp.getValue(context));
}
public void testResolveViewScope() {
MockRequestControlContext context = new MockRequestControlContext();
ViewState state = new ViewState(context.getRootFlow(), "view", new StubViewFactory());
context.setCurrentState(state);
context.getViewScope().put("foo", "bar");
Expression exp = parser.parseExpression("foo", new FluentParserContext().evaluate(RequestContext.class));
assertEquals("bar", exp.getValue(context));
}
public void testSetViewScope() {
MockRequestControlContext context = new MockRequestControlContext();
ViewState state = new ViewState(context.getRootFlow(), "view", new StubViewFactory());
context.setCurrentState(state);
context.getViewScope().put("foo", "bar");
Expression exp = parser.parseExpression("foo", new FluentParserContext().evaluate(RequestContext.class));
exp.setValue(context, "baz");
assertEquals("baz", exp.getValue(context));
}
public void testResolveFlowScope() {
MockRequestContext context = new MockRequestContext();
context.getFlowScope().put("foo", "bar");
Expression exp = parser.parseExpression("foo", new FluentParserContext().evaluate(RequestContext.class));
assertEquals("bar", exp.getValue(context));
}
public void testSetFlowScope() {
MockRequestContext context = new MockRequestContext();
context.getFlowScope().put("foo", "bar");
Expression exp = parser.parseExpression("foo", new FluentParserContext().evaluate(RequestContext.class));
exp.setValue(context, "baz");
assertEquals("baz", exp.getValue(context));
}
public void testResolveConversationScope() {
MockRequestContext context = new MockRequestContext();
context.getConversationScope().put("foo", "bar");
Expression exp = parser.parseExpression("foo", new FluentParserContext().evaluate(RequestContext.class));
assertEquals("bar", exp.getValue(context));
}
public void testSetConversationScope() {
MockRequestContext context = new MockRequestContext();
context.getConversationScope().put("foo", "bar");
Expression exp = parser.parseExpression("foo", new FluentParserContext().evaluate(RequestContext.class));
exp.setValue(context, "baz");
assertEquals("baz", exp.getValue(context));
}
public void testResolveSpringBean() {
MockRequestContext context = new MockRequestContext();
StaticApplicationContext ac = new StaticApplicationContext();
ac.getBeanFactory().registerSingleton("testBean", new TestBean());
ac.getBeanFactory().registerSingleton("action", new TestAction());
ac.getBeanFactory().registerSingleton("multiAction", new FormAction(TestBean.class));
context.getRootFlow().setApplicationContext(ac);
context.getConversationScope().put("foo", "bar");
Expression exp = parser.parseExpression("foo", new FluentParserContext().evaluate(RequestContext.class));
assertEquals("bar", exp.getValue(context));
}
public void testResolveAction() {
MockRequestContext context = new MockRequestContext();
StaticApplicationContext ac = new StaticApplicationContext();
ac.getBeanFactory().registerSingleton("testBean", new TestBean());
ac.getBeanFactory().registerSingleton("action", new TestAction());
context.getRootFlow().setApplicationContext(ac);
Expression exp = parser.parseExpression("action", new FluentParserContext().evaluate(RequestContext.class));
assertSame(ac.getBean("action"), exp.getValue(context));
}
public void testResolveMultiAction() {
MockRequestContext context = new MockRequestContext();
StaticApplicationContext ac = new StaticApplicationContext();
ac.getBeanFactory().registerSingleton("multiAction", new FormAction());
context.getRootFlow().setApplicationContext(ac);
Expression exp = parser.parseExpression("multiAction.setupForm",
new FluentParserContext().evaluate(RequestContext.class));
AnnotatedAction action = (AnnotatedAction) exp.getValue(context);
assertSame(ac.getBean("multiAction"), action.getTargetAction());
assertEquals("setupForm", action.getMethod());
}
public void testResolveEventAttributes() {
MockRequestContext context = new MockRequestContext();
LocalAttributeMap<Object> attributes = new LocalAttributeMap<Object>();
attributes.put("foo", "bar");
context.setCurrentEvent(new Event(this, "event", attributes));
Expression exp = parser.parseExpression("currentEvent.attributes.foo",
new FluentParserContext().evaluate(RequestContext.class));
assertEquals("bar", exp.getValue(context));
}
public void testResolveMessage() {
MockRequestContext context = new MockRequestContext();
StaticApplicationContext ac = new StaticApplicationContext();
ac.getStaticMessageSource().addMessage("foo", Locale.FRANCE, "bar");
ac.refresh();
context.getRootFlow().setApplicationContext(ac);
context.getMockExternalContext().setLocale(Locale.FRANCE);
Expression exp = parser.parseExpression("resourceBundle.foo",
new FluentParserContext().evaluate(RequestContext.class));
assertEquals("bar", exp.getValue(context));
}
}

View File

@@ -79,21 +79,19 @@
<sect2 xml:id="el-spring-el">
<title>Spring EL</title>
<para>
Starting with version 2.1 Web Flow uses the <link xl:href="http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/expressions.html">Spring Expression Language</link> (Spring EL).
Web Flow uses the <link xl:href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html">Spring Expression Language</link> (Spring EL).
Spring EL was created to provide a single, well-supported expression language for use across all the products in the Spring portfolio.
It is distributed as a separate jar <code>org.springframework.expression</code> in the Spring Framework.
Existing applications will need to remove dependencies on <code>org.jboss.el</code> or <code>org.ognl</code> and use <code>org.springframework.expression</code> instead.
See the section below on EL Portability for other notes on upgrading.
</para>
</sect2>
<sect2 xml:id="el-unified-el">
<title>Unified EL</title>
<para>
In Web Flow 2.0 <link xl:href="http://en.wikipedia.org/wiki/Unified_Expression_Language">Unified EL</link> was the default expression language with <code>jboss-el</code> as the implementation.
Use of Unified EL also implies a dependency on <code>el-api</code> although that is typically <emphasis>provided</emphasis> by your web container.
Tomcat 6 includes it, for example.
Spring EL is the default and recommended expression language to use.
However it is possible to replace it with Unified EL if you wish to do so.
Use of <link xl:href="http://en.wikipedia.org/wiki/Unified_Expression_Language">Unified EL</link>
also implies a dependency on <code>el-api</code> although that is typically <emphasis>provided</emphasis>
by your web container.
Although Spring EL is the default and recommended expression language to use,
it is possible to replace it with Unified EL if you wish to do so.
You need the following Spring configuration to plug in the <code>WebFlowELExpressionParser</code> to the <code>flow-builder-services</code>:
<programlisting language="xml"><![CDATA[
<webflow:flow-builder-services expression-parser="expressionParser"/>
@@ -115,35 +113,6 @@
<property name="conversionService" ref="conversionService"/>
</bean>
<bean id="conversionService" class="somepackage.ApplicationConversionService"/>]]>
</programlisting>
</para>
</sect2>
<sect2 xml:id="el-ognl">
<title>OGNL</title>
<note>
<para>
OGNL support is deprecated as of Web Flow version 2.4.
</para>
</note>
<para>
<link xl:href="http://www.ognl.org">OGNL</link> is the third supported expression language.
OGNL is the EL most familiar to Web Flow version 1.0 users.
Please refer to the <link xl:href="http://www.ognl.org/2.6.9/Documentation/html/LanguageGuide/index.html">OGNL language guide</link> for specifics on its EL syntax.
If you wish to use OGNL this is the Spring configuration necessary to plug it in:
<programlisting language="xml"><![CDATA[
<webflow:flow-builder-services expression-parser="expressionParser"/>
<bean id="expressionParser" class="org.springframework.webflow.expression.WebFlowOgnlExpressionParser"/>]]>
</programlisting>
Note that if your application is registering custom converters it's important to ensure the WebFlowOgnlExpressionParser is configured with the conversion service that has those custom converters.
<programlisting language="xml"><![CDATA[
<webflow:flow-builder-services expression-parser="expressionParser" conversion-service="conversionService"/>
<bean id="expressionParser" class="org.springframework.webflow.expression.WebFlowOgnlExpressionParser">
<property name="conversionService" ref="conversionService"/>
</bean>
<bean id="conversionService" class="somepackage.ApplicationConversionService"/>]]>
</programlisting>
</para>
@@ -152,7 +121,7 @@
<sect1 xml:id="el-portability">
<title>EL portability</title>
<para>
In general, you will find Spring EL, Unified EL and OGNL to have a very similar syntax.
In general, you will find Spring EL and Unified EL to have a very similar syntax.
</para>
<para>
Note however there are some advantages to Spring EL.
@@ -160,7 +129,7 @@
Specifically the automatic detection of generic types as well as the use of formatting annotations is currently supported with Spring EL only.
</para>
<para>
There are some minor changes to keep in mind when upgrading to Spring EL from Unified EL or OGNL as follows:
There are some minor changes to keep in mind when upgrading to Spring EL from Unified EL as follows:
<orderedlist>
<listitem><para>Expressions deliniated with <code>${}</code> in flow definitions must be changed to <code>#{}</code>.</para></listitem>
<listitem><para>Expressions testing the current event <code>#{currentEvent == 'submit'}</code> must be changed to <code>#{currentEvent.id == 'submit'}</code>.</para></listitem>

View File

@@ -339,7 +339,7 @@ public class BookingFlowHandler extends AbstractFlowHandler {
as a custom MessageCodesResolver. You may also enable data binding use
Spring MVC's native BeanWrapper by setting the
<code>useSpringBinding</code> flag to true. This is an alternative to
using OGNL or the Unified EL for view-to-model data binding. See the
using the Unified EL for view-to-model data binding. See the
JavaDoc API of this class for more information.</para>
</sect1>

View File

@@ -393,7 +393,7 @@ public ViewFactoryCreator viewFactoryCreator() {
<title>expression-parser</title>
<para>
Use the <code>expression-parser</code> attribute to customize the <code>ExpressionParser</code> used by the Web Flow system.
The default ExpressionParser uses the Unified EL if available on the classpath, otherwise OGNL is used.
The default ExpressionParser uses the Unified EL if available on the classpath, otherwise Spring EL is used.
</para>
</sect3>
<sect3 xml:id="builder-service-view-factory-creator">