diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/EvaluationAttempt.java b/spring-binding/src/main/java/org/springframework/binding/expression/EvaluationAttempt.java index dc95d4a2..ef55c01b 100644 --- a/spring-binding/src/main/java/org/springframework/binding/expression/EvaluationAttempt.java +++ b/spring-binding/src/main/java/org/springframework/binding/expression/EvaluationAttempt.java @@ -25,6 +25,7 @@ import org.springframework.core.style.ToStringCreator; public class EvaluationAttempt { private Expression expression; + private Object context; /** diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/EvaluationException.java b/spring-binding/src/main/java/org/springframework/binding/expression/EvaluationException.java index fcaa5fb2..1c5bf119 100644 --- a/spring-binding/src/main/java/org/springframework/binding/expression/EvaluationException.java +++ b/spring-binding/src/main/java/org/springframework/binding/expression/EvaluationException.java @@ -15,19 +15,17 @@ */ package org.springframework.binding.expression; -import org.springframework.core.NestedRuntimeException; - /** * Indicates an expression evaluation failed. * * @author Keith Donald */ -public class EvaluationException extends NestedRuntimeException { +public class EvaluationException extends RuntimeException { /** * The evaluation attempt that failed. Transient because an EvaluationAttempt is not serializable. */ - private transient EvaluationAttempt evaluationAttempt; + private EvaluationAttempt evaluationAttempt; /** * Creates a new evaluation exception. @@ -35,7 +33,17 @@ public class EvaluationException extends NestedRuntimeException { * @param cause the underlying cause of this exception */ public EvaluationException(EvaluationAttempt evaluationAttempt, Throwable cause) { - super(evaluationAttempt + " failed - make sure the expression is evaluatable in the context provided", cause); + this(evaluationAttempt, evaluationAttempt + + " failed - make sure the expression is evaluatable in the context provided", cause); + } + + /** + * Creates a new evaluation exception. + * @param evaluationAttempt the evaluation attempt that failed + * @param cause the underlying cause of this exception + */ + public EvaluationException(EvaluationAttempt evaluationAttempt, String message, Throwable cause) { + super(message, cause); this.evaluationAttempt = evaluationAttempt; } diff --git a/spring-binding/src/main/java/org/springframework/binding/expression/el/ELExpression.java b/spring-binding/src/main/java/org/springframework/binding/expression/el/ELExpression.java index abe129c2..98d0f574 100644 --- a/spring-binding/src/main/java/org/springframework/binding/expression/el/ELExpression.java +++ b/spring-binding/src/main/java/org/springframework/binding/expression/el/ELExpression.java @@ -44,7 +44,9 @@ public class ELExpression implements Expression { try { Object result = valueExpression.getValue(ctx); if (result == null && !ctx.isPropertyResolved()) { - throw new EvaluationException(new EvaluationAttempt(this, context), null); + EvaluationAttempt attempt = new EvaluationAttempt(this, context); + throw new EvaluationException(attempt, attempt + + " failed: the expression path did not resolve--is the base variable incorrect?", null); } return result; } catch (javax.el.PropertyNotFoundException e) { @@ -59,7 +61,9 @@ public class ELExpression implements Expression { try { valueExpression.setValue(ctx, value); if (!ctx.isPropertyResolved()) { - throw new EvaluationException(new SetValueAttempt(this, context, value), null); + SetValueAttempt attempt = new SetValueAttempt(this, context, value); + throw new EvaluationException(attempt, attempt + + " failed: the expression path did not resolve--is the base variable incorrect?", null); } } catch (javax.el.PropertyNotFoundException e) { throw new PropertyNotFoundException(new EvaluationAttempt(this, context), e); diff --git a/spring-binding/src/test/java/org/springframework/binding/expression/el/ELExpressionParserTests.java b/spring-binding/src/test/java/org/springframework/binding/expression/el/ELExpressionParserTests.java index d3fab5f1..8d188bf2 100644 --- a/spring-binding/src/test/java/org/springframework/binding/expression/el/ELExpressionParserTests.java +++ b/spring-binding/src/test/java/org/springframework/binding/expression/el/ELExpressionParserTests.java @@ -12,6 +12,7 @@ import javax.el.VariableMapper; import junit.framework.TestCase; import org.jboss.el.ExpressionFactoryImpl; +import org.springframework.binding.expression.EvaluationException; import org.springframework.binding.expression.Expression; import org.springframework.binding.expression.ExpressionVariable; import org.springframework.binding.expression.ParserException; @@ -70,12 +71,24 @@ public class ELExpressionParserTests extends TestCase { assertEquals(new Long(2), exp.getValue(new TestBean())); } - public void testParseEvalExpressionWithContextCustomTestBeanResolver() { + public void testParseEvalExpressionWithContextCustomELVariableResolver() { String expressionString = "specialProperty"; Expression exp = parser.parseExpression(expressionString, new FluentParserContext().evaluate(TestBean.class)); assertEquals("Custom resolver resolved this special property!", exp.getValue(new TestBean())); } + public void testParseBeanEvalExpressionInvalidELVariable() { + try { + String expressionString = "bogus"; + Expression exp = parser.parseExpression(expressionString, new FluentParserContext() + .evaluate(TestBean.class)); + exp.getValue(new TestBean()); + fail("Should have failed"); + } catch (EvaluationException e) { + + } + } + public void testParseLiteralExpression() { String expressionString = "'value'"; Expression exp = parser.parseExpression(expressionString, null); @@ -181,9 +194,10 @@ public class ELExpressionParserTests extends TestCase { public Object getValue(ELContext arg0, Object arg1, Object arg2) { if (arg1 == null && arg2.equals("specialProperty")) { + arg0.setPropertyResolved(true); return "Custom resolver resolved this special property!"; } else { - throw new IllegalStateException(); + return null; } } diff --git a/spring-webflow/src/main/java/org/springframework/webflow/expression/WebFlowOgnlExpressionParser.java b/spring-webflow/src/main/java/org/springframework/webflow/expression/WebFlowOgnlExpressionParser.java index a1c88b5f..a6b86c40 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/expression/WebFlowOgnlExpressionParser.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/expression/WebFlowOgnlExpressionParser.java @@ -31,6 +31,7 @@ import org.springframework.util.ClassUtils; import org.springframework.webflow.core.collection.MutableAttributeMap; import org.springframework.webflow.engine.AnnotatedAction; import org.springframework.webflow.execution.Action; +import org.springframework.webflow.execution.Event; import org.springframework.webflow.execution.RequestContext; /** @@ -50,6 +51,7 @@ public class WebFlowOgnlExpressionParser extends OgnlExpressionParser { addPropertyAccessor(MutableAttributeMap.class, new MutableAttributeMapPropertyAccessor()); addPropertyAccessor(RequestContext.class, new RequestContextPropertyAccessor(new ObjectPropertyAccessor())); addPropertyAccessor(Action.class, new ActionPropertyAccessor()); + addPropertyAccessor(Event.class, new EventPropertyAccessor()); } /** @@ -193,4 +195,18 @@ public class WebFlowOgnlExpressionParser extends OgnlExpressionParser { } } + /** + * Resolves event attributes. + */ + private static class EventPropertyAccessor implements PropertyAccessor { + public Object getProperty(Map context, Object target, Object name) throws OgnlException { + Event event = (Event) target; + return event.getAttributes().get(name.toString()); + } + + public void setProperty(Map context, Object target, Object name, Object value) throws OgnlException { + throw new OgnlException("Cannot set attributes on an Event instance - operation not allowed"); + } + } + } \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/expression/el/EventAttributesElResolver.java b/spring-webflow/src/main/java/org/springframework/webflow/expression/el/EventAttributesElResolver.java new file mode 100644 index 00000000..f3a088d0 --- /dev/null +++ b/spring-webflow/src/main/java/org/springframework/webflow/expression/el/EventAttributesElResolver.java @@ -0,0 +1,78 @@ +/* + * Copyright 2004-2008 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.el; + +import java.util.Iterator; + +import javax.el.ELContext; +import javax.el.ELResolver; +import javax.el.PropertyNotWritableException; + +import org.springframework.webflow.execution.Event; + +/** + * Resolves attributes on an {@link Event} instance. + * @author Keith Donald + */ +public class EventAttributesElResolver extends ELResolver { + + public Class getCommonPropertyType(ELContext elContext, Object base) { + if (base instanceof Event) { + return Object.class; + } else { + return null; + } + } + + public Iterator getFeatureDescriptors(ELContext elContext, Object base) { + return null; + } + + public Class getType(ELContext elContext, Object base, Object property) { + if (base instanceof Event) { + elContext.setPropertyResolved(true); + return Object.class; + } else { + return null; + } + } + + public Object getValue(ELContext elContext, Object base, Object property) { + if (base instanceof Event) { + Event event = (Event) base; + elContext.setPropertyResolved(true); + return event.getAttributes().get(property.toString()); + } else { + return null; + } + } + + public boolean isReadOnly(ELContext elContext, Object base, Object property) { + if (base instanceof Event) { + elContext.setPropertyResolved(true); + return true; + } else { + return false; + } + } + + public void setValue(ELContext elContext, Object base, Object property, Object value) { + if (base instanceof Event) { + elContext.setPropertyResolved(true); + throw new PropertyNotWritableException("Event attributes cannot be set with an expression."); + } + } +} \ No newline at end of file diff --git a/spring-webflow/src/main/java/org/springframework/webflow/expression/el/WebFlowELExpressionParser.java b/spring-webflow/src/main/java/org/springframework/webflow/expression/el/WebFlowELExpressionParser.java index 575a1e73..0b1cd0b0 100644 --- a/spring-webflow/src/main/java/org/springframework/webflow/expression/el/WebFlowELExpressionParser.java +++ b/spring-webflow/src/main/java/org/springframework/webflow/expression/el/WebFlowELExpressionParser.java @@ -59,6 +59,7 @@ public class WebFlowELExpressionParser extends ELExpressionParser { customResolvers.add(new ImplicitFlowVariableELResolver(context)); customResolvers.add(new ScopeSearchingELResolver(context)); customResolvers.add(new SpringBeanWebFlowELResolver(context)); + customResolvers.add(new EventAttributesElResolver()); customResolvers.add(new ActionMethodELResolver()); ELResolver resolver = new DefaultELResolver(customResolvers); return new WebFlowELContext(resolver);