SWF-928 Evaluation exceptions caused by type conversion errors within target Expression of a data mapping not treating as a type conversion error

SWF-780 Transition on-exception not handled with ognl

Introduced ValueCoercionException as a subclass of EvaluationException for type conversion failures.  OGNL exceptions will no longer be wrapped directly,  the reason for the ognl exception will be passed instead.
This commit is contained in:
Scott Andrews
2008-10-31 22:33:38 +00:00
parent 987e14bcca
commit 4c3c1158f5
12 changed files with 137 additions and 22 deletions

View File

@@ -0,0 +1,72 @@
/*
* 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.binding.expression;
import org.springframework.core.style.StylerUtils;
/**
* An evaluation exception indicating a expression failed to evaluate because the source value could not be coerced to
* the target class
* @author Scott Andrews
*/
public class ValueCoercionException extends EvaluationException {
private Object value;
private Class targetClass;
/**
* Creates a new property not found exception
* @param contextClass the class of object upon which coercion was attempted
* @param property the property that could not be coerced
* @param value the value that could not be coerced
* @param targetClass the class the value could not be coerced to
*/
public ValueCoercionException(Class contextClass, String property, Object value, Class targetClass) {
this(contextClass, property, value, targetClass, null);
this.value = value;
this.targetClass = targetClass;
}
/**
* Creates a new property not found exception
* @param contextClass the class of object upon which coercion was attempted
* @param property the property that could not be coerced
* @param value the value that could not be coerced
* @param targetClass the class the value could not be coerced to
* @param cause root cause of the failure
*/
public ValueCoercionException(Class contextClass, String property, Object value, Class targetClass, Throwable cause) {
super(contextClass, property, "value " + StylerUtils.style(value) + " could not be coerced to ["
+ targetClass.getName() + "]", cause);
this.value = value;
this.targetClass = targetClass;
}
/**
* @return the value that could not be coerced
*/
public Object getValue() {
return value;
}
/**
* @return the class the value could not be coerced to
*/
public Class getTargetClass() {
return targetClass;
}
}

View File

@@ -23,12 +23,14 @@ import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.NotReadablePropertyException;
import org.springframework.beans.NotWritablePropertyException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.binding.convert.ConversionException;
import org.springframework.binding.convert.ConversionExecutor;
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;
/**
* An expression that delegates to a {@link BeanWrapperImpl bean wrapper} to evaluate or set a property of a context.
@@ -45,6 +47,7 @@ import org.springframework.binding.expression.PropertyNotFoundException;
* support method invocation, arithmetic operations, or logic operations.
*
* @author Keith Donald
* @author Scott Andrews
*/
public class BeanWrapperExpression implements Expression {
@@ -99,6 +102,8 @@ public class BeanWrapperExpression implements Expression {
beanWrapper.setPropertyValue(expression, value);
} catch (NotWritablePropertyException e) {
throw new PropertyNotFoundException(context.getClass(), expression, e);
} catch (TypeMismatchException e) {
throw new ValueCoercionException(context.getClass(), expression, value, e.getRequiredType(), e);
} catch (BeansException e) {
throw new EvaluationException(context.getClass(), getExpressionString(),
"A BeansException occurred setting the value of expression '" + getExpressionString()

View File

@@ -7,9 +7,7 @@ import javax.el.PropertyNotFoundException;
import javax.el.PropertyNotWritableException;
import javax.el.ValueExpression;
import org.springframework.binding.convert.ConversionException;
import org.springframework.binding.convert.ConversionService;
import org.springframework.core.style.StylerUtils;
import org.springframework.util.Assert;
/**
@@ -93,12 +91,7 @@ class BindingValueExpression extends ValueExpression {
if (expectedType == null) {
return value;
} else {
try {
return conversionService.executeConversion(value, expectedType);
} catch (ConversionException e) {
throw new ELException("Unable to coerce value " + StylerUtils.style(value) + " to expected type ["
+ expectedType + "]", e);
}
return conversionService.executeConversion(value, expectedType);
}
}

View File

@@ -19,15 +19,18 @@ import javax.el.ELContext;
import javax.el.ELException;
import javax.el.ValueExpression;
import org.springframework.binding.convert.ConversionExecutionException;
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.util.Assert;
/**
* Evaluates a parsed EL expression.
*
* @author Jeremy Grelle
* @author Scott Andrews
*/
public class ELExpression implements Expression {
@@ -82,6 +85,8 @@ public class ELExpression implements Expression {
}
} catch (javax.el.PropertyNotFoundException e) {
throw new PropertyNotFoundException(context.getClass(), getExpressionString(), e);
} catch (ConversionExecutionException e) {
throw new ValueCoercionException(context.getClass(), getExpressionString(), value, e.getTargetClass(), e);
} catch (ELException e) {
throw new EvaluationException(context.getClass(), getExpressionString(),
"An ELException occurred setting the value of expression '" + getExpressionString()

View File

@@ -29,15 +29,18 @@ import ognl.TypeConverter;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.InvalidPropertyException;
import org.springframework.binding.convert.ConversionExecutionException;
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;
/**
* Evaluates a parsed Ognl expression.
*
* @author Keith Donald
* @author Scott Andrews
*/
class OgnlExpression implements Expression {
@@ -97,9 +100,15 @@ class OgnlExpression implements Expression {
} catch (NoSuchPropertyException e) {
throw new PropertyNotFoundException(context.getClass(), getExpressionString(), e);
} catch (OgnlException e) {
throw new EvaluationException(context.getClass(), getExpressionString(),
"An OgnlException occurred setting the value of expression '" + getExpressionString()
+ "' on context [" + context.getClass() + "] to [" + value + "]", e);
if (e.getReason() instanceof ConversionExecutionException) {
ConversionExecutionException conversionEx = (ConversionExecutionException) e.getReason();
throw new ValueCoercionException(context.getClass(), expressionString, value, conversionEx
.getTargetClass(), conversionEx);
} else {
throw new EvaluationException(context.getClass(), getExpressionString(),
"An OgnlException occurred setting the value of expression '" + getExpressionString()
+ "' on context [" + context.getClass() + "] to [" + value + "]", e.getReason());
}
}
}

View File

@@ -19,6 +19,7 @@ import org.springframework.binding.convert.ConversionExecutionException;
import org.springframework.binding.convert.ConversionExecutor;
import org.springframework.binding.expression.EvaluationException;
import org.springframework.binding.expression.Expression;
import org.springframework.binding.expression.ValueCoercionException;
import org.springframework.binding.mapping.Mapping;
import org.springframework.util.Assert;
@@ -27,6 +28,7 @@ import org.springframework.util.Assert;
* a source object to a property on a target object, optionally applying a type conversion during the mapping process.
*
* @author Keith Donald
* @author Scott Andrews
*/
public class DefaultMapping implements Mapping {
@@ -132,6 +134,8 @@ public class DefaultMapping implements Mapping {
try {
targetExpression.setValue(context.getTarget(), targetValue);
context.setSuccessResult(sourceValue, targetValue);
} catch (ValueCoercionException e) {
context.setTypeConversionErrorResult(e);
} catch (EvaluationException e) {
context.setTargetAccessError(sourceValue, e);
}

View File

@@ -22,6 +22,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.binding.convert.ConversionExecutionException;
import org.springframework.binding.expression.EvaluationException;
import org.springframework.binding.expression.ValueCoercionException;
import org.springframework.binding.mapping.Mapping;
import org.springframework.binding.mapping.MappingResult;
import org.springframework.binding.mapping.MappingResults;
@@ -34,6 +35,7 @@ import org.springframework.binding.mapping.results.TypeConversionError;
/**
* Default mapping context implementation.
* @author Keith Donald
* @author Scott Andrews
*/
public class DefaultMappingContext {
@@ -117,6 +119,17 @@ public class DefaultMappingContext {
add(new MappingResult(currentMapping, new TypeConversionError(exception)));
}
/**
* Indicates the current mapping ended with a 'type conversion' error. This means the value obtained from the source
* could not be converted to a type that could be assigned to the target expression.
* @param exception the coercion exception that occurred, containing the original source value that could not be
* converted during the mapping attempt, as well as the desired target type to which conversion could not be
* performed
*/
public void setTypeConversionErrorResult(ValueCoercionException exception) {
add(new MappingResult(currentMapping, new TypeConversionError(exception)));
}
/**
* Indicates a error occurred accessing the source mapping expression.
* @param error the error that occurred

View File

@@ -16,6 +16,7 @@
package org.springframework.binding.mapping.results;
import org.springframework.binding.convert.ConversionExecutionException;
import org.springframework.binding.expression.ValueCoercionException;
import org.springframework.binding.mapping.Result;
import org.springframework.core.style.ToStringCreator;
@@ -23,6 +24,7 @@ import org.springframework.core.style.ToStringCreator;
* Indicates a type conversion occurred during a mapping operation.
*
* @author Keith Donald
* @author Scott Andrews
*/
public class TypeConversionError extends Result {
@@ -30,7 +32,7 @@ public class TypeConversionError extends Result {
private Class targetType;
private ConversionExecutionException exception;
private Exception exception;
/**
* Creates a new type conversion error.
@@ -38,10 +40,18 @@ public class TypeConversionError extends Result {
*/
public TypeConversionError(ConversionExecutionException exception) {
this.exception = exception;
this.originalValue = exception.getValue();
this.targetType = exception.getTargetClass();
}
public TypeConversionError(ValueCoercionException exception) {
this.exception = exception;
this.originalValue = exception.getValue();
this.targetType = exception.getTargetClass();
}
public Object getOriginalValue() {
return exception.getValue();
return originalValue;
}
public Object getMappedValue() {
@@ -62,13 +72,13 @@ public class TypeConversionError extends Result {
* Returns the target type of the conversion attempt.
*/
public Class getTargetClass() {
return exception.getTargetClass();
return targetType;
}
/**
* Returns the backing type conversion exception that occurred.
*/
public ConversionExecutionException getException() {
public Exception getException() {
return exception;
}