SWF-1146 - Validator only runs if signature matches exact model object Class
This commit is contained in:
@@ -17,9 +17,13 @@
|
||||
package org.springframework.webflow.validation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.binding.expression.ExpressionParser;
|
||||
import org.springframework.binding.mapping.MappingResults;
|
||||
@@ -39,6 +43,8 @@ import org.springframework.webflow.execution.RequestContext;
|
||||
* A helper class the encapsulates conventions to invoke validation logic.
|
||||
*
|
||||
* @author Scott Andrews
|
||||
* @author Canny Duck
|
||||
* @author Jeremy Grelle
|
||||
*/
|
||||
public class ValidationHelper {
|
||||
|
||||
@@ -76,8 +82,7 @@ public class ValidationHelper {
|
||||
* @param mappingResults object mapping results
|
||||
*/
|
||||
public ValidationHelper(Object model, RequestContext requestContext, String eventId, String modelName,
|
||||
ExpressionParser expressionParser, MessageCodesResolver messageCodesResolver,
|
||||
MappingResults mappingResults) {
|
||||
ExpressionParser expressionParser, MessageCodesResolver messageCodesResolver, MappingResults mappingResults) {
|
||||
Assert.notNull(model, "The model to validate is required");
|
||||
Assert.notNull(requestContext, "The request context for the validator is required");
|
||||
this.model = model;
|
||||
@@ -183,8 +188,7 @@ public class ValidationHelper {
|
||||
private boolean invokeValidatorValidateMethodForCurrentState(Object model, Object validator) {
|
||||
String methodName = "validate" + StringUtils.capitalize(requestContext.getCurrentState().getId());
|
||||
// preferred
|
||||
Method validateMethod = ReflectionUtils.findMethod(validator.getClass(), methodName, new Class[] {
|
||||
model.getClass(), ValidationContext.class });
|
||||
Method validateMethod = findValidationMethod(model, validator, methodName, ValidationContext.class);
|
||||
if (validateMethod != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Invoking current state validator method '"
|
||||
@@ -196,8 +200,7 @@ public class ValidationHelper {
|
||||
return true;
|
||||
}
|
||||
// mvc 2 compatibility only
|
||||
validateMethod = ReflectionUtils.findMethod(validator.getClass(), methodName, new Class[] { model.getClass(),
|
||||
Errors.class });
|
||||
validateMethod = findValidationMethod(model, validator, methodName, Errors.class);
|
||||
if (validateMethod != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Invoking current state validator method '"
|
||||
@@ -210,8 +213,7 @@ public class ValidationHelper {
|
||||
return true;
|
||||
}
|
||||
// web flow 2.0.0 to 2.0.3 compatibility only [to remove in web flow 3]
|
||||
validateMethod = ReflectionUtils.findMethod(validator.getClass(), methodName, new Class[] { model.getClass(),
|
||||
MessageContext.class });
|
||||
validateMethod = findValidationMethod(model, validator, methodName, MessageContext.class);
|
||||
if (validateMethod != null) {
|
||||
ReflectionUtils.invokeMethod(validateMethod, validator, new Object[] { model,
|
||||
requestContext.getMessageContext() });
|
||||
@@ -232,8 +234,7 @@ public class ValidationHelper {
|
||||
return true;
|
||||
}
|
||||
// preferred
|
||||
Method validateMethod = ReflectionUtils.findMethod(validator.getClass(), "validate", new Class[] {
|
||||
model.getClass(), ValidationContext.class });
|
||||
Method validateMethod = findValidationMethod(model, validator, "validate", ValidationContext.class);
|
||||
if (validateMethod != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Invoking default validator method '" + ClassUtils.getShortName(validator.getClass())
|
||||
@@ -244,8 +245,7 @@ public class ValidationHelper {
|
||||
return true;
|
||||
}
|
||||
// mvc 2 compatibility only
|
||||
validateMethod = ReflectionUtils.findMethod(validator.getClass(), "validate", new Class[] { model.getClass(),
|
||||
Errors.class });
|
||||
validateMethod = findValidationMethod(model, validator, "validate", Errors.class);
|
||||
if (validateMethod != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Invoking default validator method '" + ClassUtils.getShortName(validator.getClass())
|
||||
@@ -258,4 +258,23 @@ public class ValidationHelper {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private Method findValidationMethod(Object model, Object validator, String methodName, Class context) {
|
||||
Class modelClass = AopUtils.getTargetClass(model);
|
||||
|
||||
List modelSearchClasses = new ArrayList();
|
||||
while (modelClass != null) {
|
||||
modelSearchClasses.add(modelClass);
|
||||
modelClass = modelClass.getSuperclass();
|
||||
}
|
||||
for (Iterator iterator = modelSearchClasses.iterator(); iterator.hasNext();) {
|
||||
Class searchClass = (Class) iterator.next();
|
||||
Method method = ReflectionUtils.findMethod(validator.getClass(), methodName, new Class[] { searchClass,
|
||||
context });
|
||||
if (method != null) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import org.springframework.binding.message.MessageBuilder;
|
||||
import org.springframework.binding.message.MessageContext;
|
||||
|
||||
/**
|
||||
* Support class for {@link ValidationHelperTest}
|
||||
* Support class for {@link ValidationHelperTests}
|
||||
*/
|
||||
public class StubModelMessageContext {
|
||||
|
||||
|
||||
@@ -163,7 +163,23 @@ public class ValidationHelperTests extends TestCase {
|
||||
applicationContext.getBeanFactory().registerSingleton("modelValidator", validator);
|
||||
requestContext.getRootFlow().setApplicationContext(applicationContext);
|
||||
|
||||
Object model = new Object();
|
||||
Model model = new Model();
|
||||
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
|
||||
new DefaultMessageCodesResolver(), null);
|
||||
ViewState state1 = new ViewState(requestContext.getRootFlow(), "state1", new StubViewFactory());
|
||||
requestContext.setCurrentState(state1);
|
||||
helper.validate();
|
||||
assertTrue(validator.state1Invoked);
|
||||
assertTrue(validator.fallbackInvoked);
|
||||
}
|
||||
|
||||
public void testStateAndFallbackValidatorInvokedForSubclass() {
|
||||
ModelValidator validator = new ModelValidator();
|
||||
StaticApplicationContext applicationContext = new StaticApplicationContext();
|
||||
applicationContext.getBeanFactory().registerSingleton("modelValidator", validator);
|
||||
requestContext.getRootFlow().setApplicationContext(applicationContext);
|
||||
|
||||
ExtendedModel model = new ExtendedModel();
|
||||
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
|
||||
new DefaultMessageCodesResolver(), null);
|
||||
ViewState state1 = new ViewState(requestContext.getRootFlow(), "state1", new StubViewFactory());
|
||||
@@ -179,7 +195,23 @@ public class ValidationHelperTests extends TestCase {
|
||||
applicationContext.getBeanFactory().registerSingleton("modelValidator", validator);
|
||||
requestContext.getRootFlow().setApplicationContext(applicationContext);
|
||||
|
||||
Object model = new Object();
|
||||
Model model = new Model();
|
||||
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
|
||||
new DefaultMessageCodesResolver(), null);
|
||||
ViewState state1 = new ViewState(requestContext.getRootFlow(), "state2", new StubViewFactory());
|
||||
requestContext.setCurrentState(state1);
|
||||
helper.validate();
|
||||
assertFalse(validator.state1Invoked);
|
||||
assertTrue(validator.fallbackInvoked);
|
||||
}
|
||||
|
||||
public void testFallbackValidatorInvokedForSubclass() {
|
||||
ModelValidator validator = new ModelValidator();
|
||||
StaticApplicationContext applicationContext = new StaticApplicationContext();
|
||||
applicationContext.getBeanFactory().registerSingleton("modelValidator", validator);
|
||||
requestContext.getRootFlow().setApplicationContext(applicationContext);
|
||||
|
||||
ExtendedModel model = new ExtendedModel();
|
||||
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
|
||||
new DefaultMessageCodesResolver(), null);
|
||||
ViewState state1 = new ViewState(requestContext.getRootFlow(), "state2", new StubViewFactory());
|
||||
@@ -195,7 +227,23 @@ public class ValidationHelperTests extends TestCase {
|
||||
applicationContext.getBeanFactory().registerSingleton("modelValidator", validator);
|
||||
requestContext.getRootFlow().setApplicationContext(applicationContext);
|
||||
|
||||
Object model = new Object();
|
||||
Model model = new Model();
|
||||
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
|
||||
new DefaultMessageCodesResolver(), null);
|
||||
ViewState state1 = new ViewState(requestContext.getRootFlow(), "state1", new StubViewFactory());
|
||||
requestContext.setCurrentState(state1);
|
||||
helper.validate();
|
||||
assertTrue(validator.state1Invoked);
|
||||
assertTrue(validator.fallbackInvoked);
|
||||
}
|
||||
|
||||
public void testStateAndFallbackLegacyValidatorInvokedForSubclass() {
|
||||
LegacyModelValidator validator = new LegacyModelValidator();
|
||||
StaticApplicationContext applicationContext = new StaticApplicationContext();
|
||||
applicationContext.getBeanFactory().registerSingleton("modelValidator", validator);
|
||||
requestContext.getRootFlow().setApplicationContext(applicationContext);
|
||||
|
||||
ExtendedModel model = new ExtendedModel();
|
||||
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
|
||||
new DefaultMessageCodesResolver(), null);
|
||||
ViewState state1 = new ViewState(requestContext.getRootFlow(), "state1", new StubViewFactory());
|
||||
@@ -211,7 +259,7 @@ public class ValidationHelperTests extends TestCase {
|
||||
applicationContext.getBeanFactory().registerSingleton("modelValidator", validator);
|
||||
requestContext.getRootFlow().setApplicationContext(applicationContext);
|
||||
|
||||
Object model = new Object();
|
||||
Model model = new Model();
|
||||
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
|
||||
new DefaultMessageCodesResolver(), null);
|
||||
ViewState state1 = new ViewState(requestContext.getRootFlow(), "state2", new StubViewFactory());
|
||||
@@ -227,7 +275,23 @@ public class ValidationHelperTests extends TestCase {
|
||||
applicationContext.getBeanFactory().registerSingleton("modelValidator", validator);
|
||||
requestContext.getRootFlow().setApplicationContext(applicationContext);
|
||||
|
||||
Object model = new Object();
|
||||
Model model = new Model();
|
||||
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
|
||||
new DefaultMessageCodesResolver(), null);
|
||||
ViewState state1 = new ViewState(requestContext.getRootFlow(), "state1", new StubViewFactory());
|
||||
requestContext.setCurrentState(state1);
|
||||
helper.validate();
|
||||
assertTrue(validator.state1Invoked);
|
||||
assertTrue(validator.fallbackInvoked);
|
||||
}
|
||||
|
||||
public void testStateAndFallbackErrorsValidatorInvokedForSubclass() {
|
||||
ErrorsModelValidator validator = new ErrorsModelValidator();
|
||||
StaticApplicationContext applicationContext = new StaticApplicationContext();
|
||||
applicationContext.getBeanFactory().registerSingleton("modelValidator", validator);
|
||||
requestContext.getRootFlow().setApplicationContext(applicationContext);
|
||||
|
||||
ExtendedModel model = new ExtendedModel();
|
||||
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
|
||||
new DefaultMessageCodesResolver(), null);
|
||||
ViewState state1 = new ViewState(requestContext.getRootFlow(), "state1", new StubViewFactory());
|
||||
@@ -243,7 +307,23 @@ public class ValidationHelperTests extends TestCase {
|
||||
applicationContext.getBeanFactory().registerSingleton("modelValidator", validator);
|
||||
requestContext.getRootFlow().setApplicationContext(applicationContext);
|
||||
|
||||
Object model = new Object();
|
||||
Model model = new Model();
|
||||
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
|
||||
new DefaultMessageCodesResolver(), null);
|
||||
ViewState state1 = new ViewState(requestContext.getRootFlow(), "state2", new StubViewFactory());
|
||||
requestContext.setCurrentState(state1);
|
||||
helper.validate();
|
||||
assertFalse(validator.state1Invoked);
|
||||
assertTrue(validator.fallbackInvoked);
|
||||
}
|
||||
|
||||
public void testFallbackErrorsValidatorInvokedForSubclass() {
|
||||
ErrorsModelValidator validator = new ErrorsModelValidator();
|
||||
StaticApplicationContext applicationContext = new StaticApplicationContext();
|
||||
applicationContext.getBeanFactory().registerSingleton("modelValidator", validator);
|
||||
requestContext.getRootFlow().setApplicationContext(applicationContext);
|
||||
|
||||
Model model = new Model();
|
||||
ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, modelName, null,
|
||||
new DefaultMessageCodesResolver(), null);
|
||||
ViewState state1 = new ViewState(requestContext.getRootFlow(), "state2", new StubViewFactory());
|
||||
@@ -266,6 +346,9 @@ public class ValidationHelperTests extends TestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public static class ExtendedModel extends Model {
|
||||
}
|
||||
|
||||
public static class ErrorsModel {
|
||||
private boolean state1Invoked;
|
||||
private boolean fallbackInvoked;
|
||||
@@ -283,7 +366,7 @@ public class ValidationHelperTests extends TestCase {
|
||||
private boolean state1Invoked;
|
||||
private boolean fallbackInvoked;
|
||||
|
||||
public void validateState1(Object object, Errors errors) {
|
||||
public void validateState1(Model model, Errors errors) {
|
||||
state1Invoked = true;
|
||||
}
|
||||
|
||||
@@ -300,11 +383,11 @@ public class ValidationHelperTests extends TestCase {
|
||||
private boolean state1Invoked;
|
||||
private boolean fallbackInvoked;
|
||||
|
||||
public void validateState1(Object object, ValidationContext context) {
|
||||
public void validateState1(Model model, ValidationContext context) {
|
||||
state1Invoked = true;
|
||||
}
|
||||
|
||||
public void validate(Object object, ValidationContext context) {
|
||||
public void validate(Model model, ValidationContext context) {
|
||||
fallbackInvoked = true;
|
||||
}
|
||||
}
|
||||
@@ -313,11 +396,11 @@ public class ValidationHelperTests extends TestCase {
|
||||
private boolean state1Invoked;
|
||||
private boolean fallbackInvoked;
|
||||
|
||||
public void validateState1(Object object, Errors context) {
|
||||
public void validateState1(Model model, Errors context) {
|
||||
state1Invoked = true;
|
||||
}
|
||||
|
||||
public void validate(Object object, Errors context) {
|
||||
public void validate(Model model, Errors context) {
|
||||
fallbackInvoked = true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user