SWF-1146 - Validator only runs if signature matches exact model object Class

This commit is contained in:
Jeremy Grelle
2009-08-05 21:45:57 +00:00
parent 84c2193f73
commit 29a9130377
3 changed files with 127 additions and 25 deletions

View File

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

View File

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

View File

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