From 508d6a7dfb195d96b748c4a9465a75be9c262859 Mon Sep 17 00:00:00 2001
From: Rossen Stoyanchev
* The xmlns of the XSD expected to be resolved:
- *
+ *
* This package also contains the definition of the XML-based flow definition language, defined within
- * {@code spring-webflow-2.0.xsd}. See this schema for a detailed description of language elements.
+ * {@code spring-webflow-2.4.xsd}. See this schema for a detailed description of language elements.
*/
package org.springframework.webflow.engine.model.builder.xml;
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/builder/MvcViewFactoryCreator.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/builder/MvcViewFactoryCreator.java
index 7cebb7ec..3760f8ed 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/builder/MvcViewFactoryCreator.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/builder/MvcViewFactoryCreator.java
@@ -37,6 +37,7 @@ import org.springframework.webflow.mvc.portlet.PortletMvcViewFactory;
import org.springframework.webflow.mvc.servlet.ServletMvcViewFactory;
import org.springframework.webflow.mvc.view.AbstractMvcViewFactory;
import org.springframework.webflow.mvc.view.FlowViewResolver;
+import org.springframework.webflow.validation.ValidationHintResolver;
import org.springframework.webflow.validation.WebFlowMessageCodesResolver;
/**
@@ -49,12 +50,12 @@ import org.springframework.webflow.validation.WebFlowMessageCodesResolver;
* By default, this implementation creates view factories that resolve their views by loading flow-relative resources,
* such as .jsp templates located in a flow working directory. This class also supports rendering views resolved by
* pre-existing Spring MVC {@link ViewResolver view resolvers}.
- *
+ *
* @see ServletMvcViewFactory
* @see PortletMvcViewFactory
* @see FlowResourceFlowViewResolver
* @see DelegatingFlowViewResolver
- *
+ *
* @author Keith Donald
* @author Scott Andrews
*/
@@ -170,7 +171,8 @@ public class MvcViewFactoryCreator implements ViewFactoryCreator, ApplicationCon
}
public ViewFactory createViewFactory(Expression viewId, ExpressionParser expressionParser,
- ConversionService conversionService, BinderConfiguration binderConfiguration, Validator validator) {
+ ConversionService conversionService, BinderConfiguration binderConfiguration,
+ Validator validator, ValidationHintResolver validationHintResolver) {
if (useSpringBeanBinding) {
expressionParser = new BeanWrapperExpressionParser(conversionService);
}
@@ -183,6 +185,7 @@ public class MvcViewFactoryCreator implements ViewFactoryCreator, ApplicationCon
viewFactory.setFieldMarkerPrefix(fieldMarkerPrefix);
}
viewFactory.setValidator(validator);
+ viewFactory.setValidationHintResolver(validationHintResolver);
return viewFactory;
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java
index e5382b6e..4a3cf3bb 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/mvc/view/AbstractMvcView.java
@@ -42,6 +42,7 @@ import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageResolver;
import org.springframework.core.style.ToStringCreator;
import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.MessageCodesResolver;
import org.springframework.validation.Validator;
@@ -52,14 +53,17 @@ import org.springframework.webflow.definition.TransitionDefinition;
import org.springframework.webflow.engine.builder.BinderConfiguration;
import org.springframework.webflow.engine.builder.BinderConfiguration.Binding;
import org.springframework.webflow.execution.Event;
+import org.springframework.webflow.execution.FlowExecutionException;
import org.springframework.webflow.execution.FlowExecutionKey;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.View;
+import org.springframework.webflow.validation.BeanValidationHintResolver;
import org.springframework.webflow.validation.ValidationHelper;
+import org.springframework.webflow.validation.ValidationHintResolver;
/**
* Base view implementation for the Spring Web MVC Servlet and Spring Web MVC Portlet frameworks.
- *
+ *
* @author Keith Donald
*/
public abstract class AbstractMvcView implements View {
@@ -94,6 +98,8 @@ public abstract class AbstractMvcView implements View {
private boolean userEventProcessed;
+ private ValidationHintResolver validationHintResolver = new BeanValidationHintResolver();
+
/**
* Creates a new MVC view.
* @param view the Spring MVC view to render
@@ -124,6 +130,12 @@ public abstract class AbstractMvcView implements View {
this.validator = validator;
}
+ public void setValidationHintResolver(ValidationHintResolver validationHintResolver) {
+ if (validationHintResolver != null) {
+ this.validationHintResolver = validationHintResolver;
+ }
+ }
+
/**
* Sets the configuration describing how this view should bind to its model to access data for rendering.
* @param binderConfiguration the model binder configuration
@@ -356,7 +368,7 @@ public abstract class AbstractMvcView implements View {
* considered. In the absence of binding configuration all request parameters will be used to update matching fields
* on the model.
* <flow-builder-services> tag.
- *
+ *
* @author Jeremy Grelle
*/
class FlowBuilderServicesBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
@@ -53,6 +53,7 @@ class FlowBuilderServicesBeanDefinitionParser extends AbstractSingleBeanDefiniti
private static final String EXPRESSION_PARSER_PROPERTY = "expressionParser";
private static final String VIEW_FACTORY_CREATOR_PROPERTY = "viewFactoryCreator";
private static final String VALIDATOR_PROPERTY = "validator";
+ private static final String VALIDATION_HINT_RESOLVER_PROPERTY = "validationHintResolver";
protected String getBeanClassName(Element element) {
return FLOW_BUILDER_SERVICES_CLASS_NAME;
@@ -68,6 +69,7 @@ class FlowBuilderServicesBeanDefinitionParser extends AbstractSingleBeanDefiniti
parseExpressionParser(element, parserContext, builder);
parseViewFactoryCreator(element, parserContext, builder);
parseValidator(element, parserContext, builder);
+ parseValidationHintResolver(element, parserContext, builder);
parseDevelopment(element, builder);
parserContext.popAndRegisterContainingComponent();
@@ -115,6 +117,13 @@ class FlowBuilderServicesBeanDefinitionParser extends AbstractSingleBeanDefiniti
}
}
+ private void parseValidationHintResolver(Element element, ParserContext context, BeanDefinitionBuilder definitionBuilder) {
+ String resolver = element.getAttribute(VALIDATION_HINT_RESOLVER_PROPERTY);
+ if (StringUtils.hasText(resolver)) {
+ definitionBuilder.addPropertyReference(VALIDATION_HINT_RESOLVER_PROPERTY, resolver);
+ }
+ }
+
private void parseDevelopment(Element element, BeanDefinitionBuilder definitionBuilder) {
String development = element.getAttribute(DEVELOPMENT_ATTR);
if (StringUtils.hasText(development)) {
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowBuilderContext.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowBuilderContext.java
index 439cce0e..8cbebed6 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowBuilderContext.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/FlowBuilderContext.java
@@ -21,6 +21,7 @@ import org.springframework.context.ApplicationContext;
import org.springframework.validation.Validator;
import org.springframework.webflow.core.collection.AttributeMap;
import org.springframework.webflow.definition.registry.FlowDefinitionLocator;
+import org.springframework.webflow.validation.ValidationHintResolver;
/**
* Provides services needed to a direct a flow builder through building a flow definition.
@@ -77,6 +78,11 @@ public interface FlowBuilderContext {
*/
public Validator getValidator();
+ /**
+ * Return the {@link ValidationHintResolver}.
+ */
+ public ValidationHintResolver getValidationHintResolver();
+
/**
* Returns the application context hosting the flow system.
* @return the application context
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/ViewFactoryCreator.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/ViewFactoryCreator.java
index 5bc7480a..099b093f 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/ViewFactoryCreator.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/ViewFactoryCreator.java
@@ -21,6 +21,7 @@ import org.springframework.binding.expression.ExpressionParser;
import org.springframework.validation.Validator;
import org.springframework.webflow.execution.View;
import org.springframework.webflow.execution.ViewFactory;
+import org.springframework.webflow.validation.ValidationHintResolver;
/**
* A factory for ViewFactory objects. This is an SPI interface and conceals specific types of view factories from the
@@ -36,10 +37,12 @@ public interface ViewFactoryCreator {
* @param conversionService an optional conversion service to use to format text values
* @param binderConfiguration information on how the rendered view binds to a model that provides its data
* @param validator a global validator to invoke
+ * @param validationHintResolver a custom ValidationHintResolver to use
* @return the view factory
*/
public ViewFactory createViewFactory(Expression viewId, ExpressionParser expressionParser,
- ConversionService conversionService, BinderConfiguration binderConfiguration, Validator validator);
+ ConversionService conversionService, BinderConfiguration binderConfiguration,
+ Validator validator, ValidationHintResolver validationHintResolver);
/**
* Get the default id of the view to render in the provided view state by convention.
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilder.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilder.java
index 3a883c89..7f8b53a2 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilder.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/model/FlowModelFlowBuilder.java
@@ -114,7 +114,7 @@ import org.springframework.webflow.security.SecurityRule;
/**
* Builds a runtime {@link Flow} definition object from a {@link FlowModel}.
- *
+ *
* @author Keith Donald
*/
public class FlowModelFlowBuilder extends AbstractFlowBuilder {
@@ -535,6 +535,11 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
getLocalContext().getExpressionParser().parseExpression(state.getModel(),
new FluentParserContext().evaluate(RequestContext.class)));
}
+ if (state.getValidationHints() != null) {
+ attributes.put("validationHints",
+ getLocalContext().getExpressionParser().parseExpression(state.getValidationHints(),
+ new FluentParserContext().evaluate(RequestContext.class)));
+ }
parseAndPutSecured(state.getSecured(), attributes);
getLocalContext().getFlowArtifactFactory().createViewState(state.getId(), flow,
parseViewVariables(state.getVars()), parseActions(state.getOnEntryActions()), viewFactory, redirect,
@@ -622,7 +627,7 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder {
BinderConfiguration binderConfiguration = createBinderConfiguration(binderModel);
return getLocalContext().getViewFactoryCreator().createViewFactory(viewId,
getLocalContext().getExpressionParser(), getLocalContext().getConversionService(), binderConfiguration,
- getLocalContext().getValidator());
+ getLocalContext().getValidator(), getLocalContext().getValidationHintResolver());
}
private BinderConfiguration createBinderConfiguration(BinderModel binderModel) {
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/model/LocalFlowBuilderContext.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/model/LocalFlowBuilderContext.java
index 357d7d70..90323cf7 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/model/LocalFlowBuilderContext.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/model/LocalFlowBuilderContext.java
@@ -25,6 +25,7 @@ import org.springframework.webflow.definition.registry.FlowDefinitionLocator;
import org.springframework.webflow.engine.builder.FlowArtifactFactory;
import org.springframework.webflow.engine.builder.FlowBuilderContext;
import org.springframework.webflow.engine.builder.ViewFactoryCreator;
+import org.springframework.webflow.validation.ValidationHintResolver;
/**
* A builder context that delegates to a flow-local bean factory for builder services. Such builder services override
@@ -102,4 +103,12 @@ class LocalFlowBuilderContext implements FlowBuilderContext {
}
}
+ public ValidationHintResolver getValidationHintResolver() {
+ if (localFlowContext.containsLocalBean("validationHintResolver")) {
+ return localFlowContext.getBean("validationHintResolver", ValidationHintResolver.class);
+ } else {
+ return parent.getValidationHintResolver();
+ }
+ }
+
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/FlowBuilderContextImpl.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/FlowBuilderContextImpl.java
index a798e1a9..859ece94 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/FlowBuilderContextImpl.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/FlowBuilderContextImpl.java
@@ -31,6 +31,7 @@ import org.springframework.webflow.definition.registry.FlowDefinitionLocator;
import org.springframework.webflow.engine.builder.FlowArtifactFactory;
import org.springframework.webflow.engine.builder.FlowBuilderContext;
import org.springframework.webflow.engine.builder.ViewFactoryCreator;
+import org.springframework.webflow.validation.ValidationHintResolver;
/**
* Generic implementation of a flow builder context, suitable for use by most flow assembly systems.
@@ -109,6 +110,10 @@ public class FlowBuilderContextImpl implements FlowBuilderContext {
return flowBuilderServices.getValidator();
}
+ public ValidationHintResolver getValidationHintResolver() {
+ return flowBuilderServices.getValidationHintResolver();
+ }
+
/**
* Factory method that creates the conversion service the flow builder will use. Subclasses may override. The
* default implementation registers Web Flow-specific converters thought to be useful for most builder
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/FlowBuilderServices.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/FlowBuilderServices.java
index 8252f8c1..bde49e8d 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/FlowBuilderServices.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/builder/support/FlowBuilderServices.java
@@ -28,17 +28,18 @@ import org.springframework.webflow.engine.State;
import org.springframework.webflow.engine.builder.FlowArtifactFactory;
import org.springframework.webflow.engine.builder.FlowBuilderContext;
import org.springframework.webflow.engine.builder.ViewFactoryCreator;
+import org.springframework.webflow.validation.ValidationHintResolver;
/**
* A simple holder for configuring the services used by flow builders. These services are exposed to a builder in a
* {@link FlowBuilderContext}.
- *
+ *
* Note this class does not attempt to default any service implementations other than the {@link FlowArtifactFactory},
* which is more like builder helper objects than a service. It is expected clients inject non-null references to
* concrete service implementations appropriate for their environment.
- *
+ *
* @see FlowBuilderContextImpl
- *
+ *
* @author Keith Donald
*/
public class FlowBuilderServices implements ApplicationContextAware, InitializingBean {
@@ -72,6 +73,11 @@ public class FlowBuilderServices implements ApplicationContextAware, Initializin
*/
private Validator validator;
+ /**
+ * A ValidationHintResolver for resolving string based validation hints.
+ */
+ private ValidationHintResolver validationHintResolver;
+
/**
* The Spring application context that provides access to the services of the application.
*/
@@ -122,6 +128,14 @@ public class FlowBuilderServices implements ApplicationContextAware, Initializin
this.validator = validator;
}
+ public ValidationHintResolver getValidationHintResolver() {
+ return validationHintResolver;
+ }
+
+ public void setValidationHintResolver(ValidationHintResolver validationHintResolver) {
+ this.validationHintResolver = validationHintResolver;
+ }
+
public boolean getDevelopment() {
return development;
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/AbstractModel.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/AbstractModel.java
index 4ec419eb..a7c6f910 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/AbstractModel.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/AbstractModel.java
@@ -18,9 +18,11 @@ package org.springframework.webflow.engine.model;
import java.util.Collections;
import java.util.LinkedList;
+import org.springframework.util.StringUtils;
+
/**
* Contains basic merge functions that can be utilized by other models.
- *
+ *
* @author Scott Andrews
*/
public abstract class AbstractModel implements Model {
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/ViewStateModel.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/ViewStateModel.java
index 5ae06ade..cac27999 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/ViewStateModel.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/ViewStateModel.java
@@ -22,7 +22,7 @@ import org.springframework.util.StringUtils;
/**
* Model support for view states.
- *
+ *
* @author Scott Andrews
*/
public class ViewStateModel extends AbstractTransitionableStateModel {
@@ -35,6 +35,8 @@ public class ViewStateModel extends AbstractTransitionableStateModel {
private String model;
+ private String validationHints;
+
private LinkedList
* <?xml version="1.0" encoding="UTF-8"?>
* <flow xmlns="http://www.springframework.org/schema/webflow"
@@ -35,13 +35,13 @@ import org.xml.sax.SAXException;
* xsi:schemaLocation="http://www.springframework.org/schema/webflow
* http://www.springframework.org/schema/webflow/spring-webflow.xsd">
*
- *
+ *
* @author Erwin Vervaet
* @author Ben Hale
*/
class WebFlowEntityResolver implements EntityResolver {
- private static final String[] WEBFLOW_VERSIONS = new String[] { "spring-webflow-2.0" };
+ private static final String[] WEBFLOW_VERSIONS = new String[] { "spring-webflow-2.4", "spring-webflow-2.0" };
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
if (systemId != null && systemId.indexOf("spring-webflow.xsd") > -1) {
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilder.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilder.java
index 68de9b4e..663d7392 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilder.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/XmlFlowModelBuilder.java
@@ -60,7 +60,7 @@ import org.xml.sax.SAXException;
/**
* Builds a flow model from a XML-based flow definition resource.
- *
+ *
* @author Keith Donald
* @author Scott Andrews
*/
@@ -570,6 +570,7 @@ public class XmlFlowModelBuilder implements FlowModelBuilder {
state.setRedirect(element.getAttribute("redirect"));
state.setPopup(element.getAttribute("popup"));
state.setModel(element.getAttribute("model"));
+ state.setValidationHints(element.getAttribute("validation-hints"));
state.setVars(parseVars(element));
state.setBinder(parseBinder(element));
state.setOnRenderActions(parseOnRenderActions(element));
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/package-info.java b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/package-info.java
index f1d20c6a..ca930cde 100644
--- a/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/package-info.java
+++ b/spring-webflow/src/main/java/org/springframework/webflow/engine/model/builder/xml/package-info.java
@@ -18,7 +18,7 @@
* Defines the XmlFlowModelBuilder, for building FlowModels from XML-based resources.
*
*
@@ -74,7 +78,7 @@ public class ValidationHelper { *
* For example: model.validateEnterBookingDetails(VaticationContext) or
* context.getBean("modelValidator").validateEnterBookingDetails(model, VaticationContext)
- *
+ *
* @param model the object to validate
* @param requestContext the context for the request
* @param eventId the event triggering validation
@@ -102,17 +106,24 @@ public class ValidationHelper {
this.validator = validator;
}
+ /**
+ * Provide validation hints such as validation groups to use against a JSR-303 provider.
+ */
+ public void setValidationHints(Object[] validationHints) {
+ this.validationHints = validationHints;
+ }
+
/**
* Invoke the validators available by convention.
*/
public void validate() {
if (this.validator != null) {
- invokeValidatorDefaultValidateMethod(model, this.validator);
+ invokeValidatorDefaultValidateMethod(this.validator);
}
invokeModelValidationMethod(model);
Object modelValidator = getModelValidator();
if (modelValidator != null) {
- invokeModelValidator(model, modelValidator);
+ invokeModelValidator(modelValidator);
}
}
@@ -191,12 +202,12 @@ public class ValidationHelper {
return null;
}
- private void invokeModelValidator(Object model, Object validator) {
- invokeValidatorValidateMethodForCurrentState(model, validator);
- invokeValidatorDefaultValidateMethod(model, validator);
+ private void invokeModelValidator(Object validator) {
+ invokeValidatorValidateMethodForCurrentState(validator);
+ invokeValidatorDefaultValidateMethod(validator);
}
- private boolean invokeValidatorValidateMethodForCurrentState(Object model, Object validator) {
+ private boolean invokeValidatorValidateMethodForCurrentState(Object validator) {
String methodName = "validate" + StringUtils.capitalize(requestContext.getCurrentState().getId());
// preferred
Method validateMethod = findValidationMethod(model, validator, methodName, ValidationContext.class);
@@ -233,7 +244,7 @@ public class ValidationHelper {
return false;
}
- private boolean invokeValidatorDefaultValidateMethod(Object model, Object validator) {
+ private boolean invokeValidatorDefaultValidateMethod(Object validator) {
if (validator instanceof Validator) {
// Spring Framework Validator type
Validator springValidator = (Validator) validator;
@@ -243,7 +254,19 @@ public class ValidationHelper {
if (springValidator.supports(model.getClass())) {
MessageContextErrors errors = new MessageContextErrors(requestContext.getMessageContext(), modelName,
model, expressionParser, messageCodesResolver, mappingResults);
- springValidator.validate(model, errors);
+
+ if (this.validationHints != null) {
+ if (springValidator instanceof SmartValidator) {
+ ((SmartValidator) springValidator).validate(model, errors, this.validationHints);
+ }
+ else {
+ logger.warn("Validation hints provided but validator not an instance of SmartValidator: ["
+ + springValidator.getClass().getName() + "]");
+ }
+ }
+ else {
+ springValidator.validate(model, errors);
+ }
} else {
if (logger.isDebugEnabled()) {
logger.debug("Spring Validator '" + ClassUtils.getShortName(validator.getClass())
@@ -295,4 +318,5 @@ public class ValidationHelper {
}
return null;
}
+
}
diff --git a/spring-webflow/src/main/java/org/springframework/webflow/validation/ValidationHintResolver.java b/spring-webflow/src/main/java/org/springframework/webflow/validation/ValidationHintResolver.java
new file mode 100644
index 00000000..3d8ea900
--- /dev/null
+++ b/spring-webflow/src/main/java/org/springframework/webflow/validation/ValidationHintResolver.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2008-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.validation;
+
+import org.springframework.webflow.execution.FlowExecutionException;
+
+/**
+ * A strategy for resolving String-based hints to Objects such as validation
+ * groups against a JSR-303 provider.
+ *
+ * @author Rossen Stoyanchev
+ * @since 2.4
+ */
+public interface ValidationHintResolver {
+
+ /**
+ * Resolve the given String hints. Implementations may raise a
+ * {@link FlowExecutionException} if a hint cannot be resolved.
+ *
+ * @param model the model object that will be validated using the hints
+ * @param flowId the current flow id
+ * @param stateId the current state id
+ * @param hints the hints to resolve
+ *
+ * @return an array of resolved hints
+ */
+ Object[] resolveValidationHints(Object model, String flowId, String stateId, String[] hints);
+
+}
diff --git a/spring-webflow/src/main/resources/META-INF/spring.schemas b/spring-webflow/src/main/resources/META-INF/spring.schemas
index d7334c4c..dae657a7 100644
--- a/spring-webflow/src/main/resources/META-INF/spring.schemas
+++ b/spring-webflow/src/main/resources/META-INF/spring.schemas
@@ -1,3 +1,4 @@
http\://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd=org/springframework/webflow/config/spring-webflow-config-2.0.xsd
http\://www.springframework.org/schema/webflow-config/spring-webflow-config-2.3.xsd=org/springframework/webflow/config/spring-webflow-config-2.3.xsd
-http\://www.springframework.org/schema/webflow-config/spring-webflow-config.xsd=org/springframework/webflow/config/spring-webflow-config-2.3.xsd
\ No newline at end of file
+http\://www.springframework.org/schema/webflow-config/spring-webflow-config-2.4.xsd=org/springframework/webflow/config/spring-webflow-config-2.4.xsd
+http\://www.springframework.org/schema/webflow-config/spring-webflow-config.xsd=org/springframework/webflow/config/spring-webflow-config-2.4.xsd
\ No newline at end of file
diff --git a/spring-webflow/src/main/resources/org/springframework/webflow/config/spring-webflow-config-2.4.xsd b/spring-webflow/src/main/resources/org/springframework/webflow/config/spring-webflow-config-2.4.xsd
index 04fb8040..321cd0b1 100644
--- a/spring-webflow/src/main/resources/org/springframework/webflow/config/spring-webflow-config-2.4.xsd
+++ b/spring-webflow/src/main/resources/org/springframework/webflow/config/spring-webflow-config-2.4.xsd
@@ -12,7 +12,7 @@
@@ -523,6 +523,21 @@ Displays the view in a popup dialog. Default is false.
end-state that issues an internal redirect. To enable this feature set FlowHandlerAdapter.saveOutputToFlashScopeOnRedirect. See