diff --git a/spring-faces/src/main/java/org/springframework/faces/config/FacesFlowBuilderServicesBeanDefinitionParser.java b/spring-faces/src/main/java/org/springframework/faces/config/FacesFlowBuilderServicesBeanDefinitionParser.java index 2eaeab25..e9b391d9 100644 --- a/spring-faces/src/main/java/org/springframework/faces/config/FacesFlowBuilderServicesBeanDefinitionParser.java +++ b/spring-faces/src/main/java/org/springframework/faces/config/FacesFlowBuilderServicesBeanDefinitionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2004-2012 the original author or authors. + * Copyright 2004-2014 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. @@ -28,7 +28,7 @@ import org.w3c.dom.Element; /** * Parser for the flow-builder-services tag. - * + * * @author Jeremy Grelle * @author Christian Dupuis */ @@ -48,12 +48,16 @@ public class FacesFlowBuilderServicesBeanDefinitionParser extends AbstractSingle private static final String ENABLE_MANAGED_BEANS_ATTR = "enable-managed-beans"; private static final String EXPRESSION_PARSER_ATTR = "expression-parser"; private static final String VIEW_FACTORY_CREATOR_ATTR = "view-factory-creator"; + private static final String VALIDATOR_ATTR = "validator"; + private static final String VALIDATION_HINT_RESOLVER_ATTR = "validation-hint-resolver"; // --------------------------- Bean Configuration Properties --------------------- // private static final String CONVERSION_SERVICE_PROPERTY = "conversionService"; private static final String DEVELOPMENT_PROPERTY = "development"; 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 +72,8 @@ public class FacesFlowBuilderServicesBeanDefinitionParser extends AbstractSingle parseExpressionParser(element, parserContext, definitionBuilder, parseEnableManagedBeans(element, definitionBuilder)); parseViewFactoryCreator(element, parserContext, definitionBuilder); + parseValidator(element, parserContext, definitionBuilder); + parseValidationHintResolver(element, parserContext, definitionBuilder); parseDevelopment(element, definitionBuilder); parserContext.popAndRegisterContainingComponent(); @@ -102,6 +108,20 @@ public class FacesFlowBuilderServicesBeanDefinitionParser extends AbstractSingle definitionBuilder.addPropertyReference(VIEW_FACTORY_CREATOR_PROPERTY, viewFactoryCreator); } + private void parseValidator(Element element, ParserContext context, BeanDefinitionBuilder definitionBuilder) { + String validator = element.getAttribute(VALIDATOR_ATTR); + if (StringUtils.hasText(validator)) { + definitionBuilder.addPropertyReference(VALIDATOR_PROPERTY, validator); + } + } + + private void parseValidationHintResolver(Element element, ParserContext context, BeanDefinitionBuilder definitionBuilder) { + String resolver = element.getAttribute(VALIDATION_HINT_RESOLVER_ATTR); + if (StringUtils.hasText(resolver)) { + definitionBuilder.addPropertyReference(VALIDATION_HINT_RESOLVER_PROPERTY, resolver); + } + } + private void parseExpressionParser(Element element, ParserContext context, BeanDefinitionBuilder definitionBuilder, boolean enableManagedBeans) { diff --git a/spring-faces/src/main/java/org/springframework/faces/config/FlowBuilderServicesBuilder.java b/spring-faces/src/main/java/org/springframework/faces/config/FlowBuilderServicesBuilder.java index 4a633e7f..c0ce07ba 100644 --- a/spring-faces/src/main/java/org/springframework/faces/config/FlowBuilderServicesBuilder.java +++ b/spring-faces/src/main/java/org/springframework/faces/config/FlowBuilderServicesBuilder.java @@ -23,9 +23,12 @@ import org.springframework.faces.model.converter.FacesConversionService; import org.springframework.faces.webflow.FacesSpringELExpressionParser; import org.springframework.faces.webflow.JsfViewFactoryCreator; import org.springframework.util.Assert; +import org.springframework.validation.Validator; import org.springframework.webflow.engine.builder.ViewFactoryCreator; import org.springframework.webflow.engine.builder.support.FlowBuilderServices; import org.springframework.webflow.expression.spel.WebFlowSpringELExpressionParser; +import org.springframework.webflow.validation.BeanValidationHintResolver; +import org.springframework.webflow.validation.ValidationHintResolver; /** * A builder for {@link FlowBuilderServices} instances for use in JSF applications. @@ -46,6 +49,10 @@ public class FlowBuilderServicesBuilder { private ViewFactoryCreator viewFactoryCreator = new JsfViewFactoryCreator(); + private Validator validator; + + private ValidationHintResolver validationHintResolver; + private boolean enableDevelopmentMode; @@ -89,6 +96,26 @@ public class FlowBuilderServicesBuilder { return this; } + /** + * Set the {@link Validator} to use for validating a model declared on a view state. + * By default no validator is set. + * @param validator the validator to use + */ + public FlowBuilderServicesBuilder setValidator(Validator validator) { + this.validator = validator; + return this; + } + + /** + * The {@link ValidationHintResolver} to use to resolve validation hints such as bean validation groups. + * By default a {@link BeanValidationHintResolver} is used. + * @param resolver the resolver to use + */ + public FlowBuilderServicesBuilder setValidationHintResolver(ValidationHintResolver resolver) { + this.validationHintResolver = resolver; + return this; + } + /** * Put all flows in development mode. When set to {@code true}, changes to a flow * definition are auto-detected and result in a flow refresh. @@ -108,6 +135,8 @@ public class FlowBuilderServicesBuilder { flowBuilderServices.setConversionService(this.conversionService); flowBuilderServices.setExpressionParser(getExpressionParser()); flowBuilderServices.setViewFactoryCreator(this.viewFactoryCreator); + flowBuilderServices.setValidator(this.validator); + flowBuilderServices.setValidationHintResolver(this.validationHintResolver); flowBuilderServices.setDevelopment(this.enableDevelopmentMode); return flowBuilderServices; } diff --git a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowActionListener.java b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowActionListener.java index dd051472..0c2f6ba9 100644 --- a/spring-faces/src/main/java/org/springframework/faces/webflow/FlowActionListener.java +++ b/spring-faces/src/main/java/org/springframework/faces/webflow/FlowActionListener.java @@ -30,7 +30,9 @@ import org.apache.commons.logging.LogFactory; import org.springframework.binding.expression.Expression; import org.springframework.util.StringUtils; import org.springframework.validation.MessageCodesResolver; +import org.springframework.validation.Validator; import org.springframework.webflow.definition.TransitionDefinition; +import org.springframework.webflow.engine.builder.model.FlowModelFlowBuilder; import org.springframework.webflow.execution.RequestContext; import org.springframework.webflow.execution.RequestContextHolder; import org.springframework.webflow.execution.View; @@ -60,21 +62,10 @@ public class FlowActionListener implements ActionListener { private final MessageCodesResolver messageCodesResolver = new WebFlowMessageCodesResolver(); - private ValidationHintResolver validationHintResolver = new BeanValidationHintResolver(); - - public FlowActionListener(ActionListener delegate) { this.delegate = delegate; } - public void setValidationHintResolver(ValidationHintResolver validationHintResolver) { - this.validationHintResolver = validationHintResolver; - } - - public ValidationHintResolver getValidationHintResolver() { - return validationHintResolver; - } - public void processAction(ActionEvent actionEvent) throws AbortProcessingException { if (!JsfUtils.isFlowRequest()) { this.delegate.processAction(actionEvent); @@ -167,8 +158,21 @@ public class FlowActionListener implements ActionListener { String modelName = getModelExpression(requestContext).getExpressionString(); - new ValidationHelper(model, requestContext, - eventId, modelName, null, this.messageCodesResolver, null, this.validationHintResolver).validate(); + String attr = FlowModelFlowBuilder.VALIDATOR_FLOW_ATTR; + Validator validator = (Validator) requestContext.getActiveFlow().getAttributes().get(attr); + + ValidationHelper helper = new ValidationHelper(model, requestContext, eventId, + modelName, null, this.messageCodesResolver, null, getHintResolver(requestContext)); + helper.setValidator(validator); + helper.validate(); + } + + private ValidationHintResolver getHintResolver(RequestContext requestContext) { + + ValidationHintResolver hintResolver = (ValidationHintResolver) requestContext.getActiveFlow() + .getAttributes().get(FlowModelFlowBuilder.VALIDATION_HINT_RESOLVER_FLOW_ATTR); + + return (hintResolver != null ? hintResolver : new BeanValidationHintResolver()); } } diff --git a/spring-faces/src/main/resources/org/springframework/faces/config/spring-faces-2.4.xsd b/spring-faces/src/main/resources/org/springframework/faces/config/spring-faces-2.4.xsd index 24fde4b9..02261301 100644 --- a/spring-faces/src/main/resources/org/springframework/faces/config/spring-faces-2.4.xsd +++ b/spring-faces/src/main/resources/org/springframework/faces/config/spring-faces-2.4.xsd @@ -45,6 +45,26 @@ The custom ExpressionParser implementation to use to compile expression strings + + + + + + + + + + + + + + diff --git a/spring-faces/src/test/java/org/springframework/faces/config/AbstractFacesFlowBuilderServicesConfigurationTests.java b/spring-faces/src/test/java/org/springframework/faces/config/AbstractFacesFlowBuilderServicesConfigurationTests.java index d2cf0351..fe1c8b3e 100644 --- a/spring-faces/src/test/java/org/springframework/faces/config/AbstractFacesFlowBuilderServicesConfigurationTests.java +++ b/spring-faces/src/test/java/org/springframework/faces/config/AbstractFacesFlowBuilderServicesConfigurationTests.java @@ -11,12 +11,13 @@ import org.springframework.binding.expression.Expression; import org.springframework.binding.expression.ExpressionParser; import org.springframework.binding.expression.spel.SpringELExpressionParser; import org.springframework.context.ApplicationContext; -import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.faces.model.converter.FacesConversionService; import org.springframework.faces.webflow.FacesSpringELExpressionParser; import org.springframework.faces.webflow.JSFMockHelper; import org.springframework.faces.webflow.JsfViewFactoryCreator; import org.springframework.validation.Validator; +import org.springframework.faces.config.EmptySpringValidator; +import org.springframework.faces.config.MyBeanValidationHintResolver; import org.springframework.webflow.engine.builder.BinderConfiguration; import org.springframework.webflow.engine.builder.ViewFactoryCreator; import org.springframework.webflow.engine.builder.support.FlowBuilderServices; @@ -68,6 +69,8 @@ public abstract class AbstractFacesFlowBuilderServicesConfigurationTests extends assertTrue(this.builderServices.getExpressionParser() instanceof WebFlowSpringELExpressionParser); assertTrue(this.builderServices.getViewFactoryCreator() instanceof TestViewFactoryCreator); assertTrue(this.builderServices.getConversionService() instanceof TestConversionService); + assertTrue(builderServices.getValidator() instanceof EmptySpringValidator); + assertTrue(builderServices.getValidationHintResolver() instanceof MyBeanValidationHintResolver); assertTrue(this.builderServices.getDevelopment()); } diff --git a/spring-faces/src/test/java/org/springframework/faces/config/EmptySpringValidator.java b/spring-faces/src/test/java/org/springframework/faces/config/EmptySpringValidator.java new file mode 100644 index 00000000..8ef3eaa1 --- /dev/null +++ b/spring-faces/src/test/java/org/springframework/faces/config/EmptySpringValidator.java @@ -0,0 +1,15 @@ +package org.springframework.faces.config; + +import org.springframework.validation.Errors; +import org.springframework.validation.Validator; + +public class EmptySpringValidator implements Validator { + + public boolean supports(Class clazz) { + return false; + } + + public void validate(Object target, Errors errors) { + } + +} diff --git a/spring-faces/src/test/java/org/springframework/faces/config/FacesFlowBuilderServicesJavaConfigTests.java b/spring-faces/src/test/java/org/springframework/faces/config/FacesFlowBuilderServicesJavaConfigTests.java index 04d8de0b..36961040 100644 --- a/spring-faces/src/test/java/org/springframework/faces/config/FacesFlowBuilderServicesJavaConfigTests.java +++ b/spring-faces/src/test/java/org/springframework/faces/config/FacesFlowBuilderServicesJavaConfigTests.java @@ -8,6 +8,7 @@ import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.webflow.engine.builder.ViewFactoryCreator; import org.springframework.webflow.engine.builder.support.FlowBuilderServices; import org.springframework.webflow.expression.spel.WebFlowSpringELExpressionParser; +import org.springframework.webflow.validation.BeanValidationHintResolver; public class FacesFlowBuilderServicesJavaConfigTests extends AbstractFacesFlowBuilderServicesConfigurationTests { @@ -36,6 +37,8 @@ public class FacesFlowBuilderServicesJavaConfigTests extends AbstractFacesFlowBu .setExpressionParser(customExpressionParser()) .setViewFactoryCreator(customViewFactoryCreator()) .setConversionService(customConversionService()) + .setValidator(new EmptySpringValidator()) + .setValidationHintResolver(new MyBeanValidationHintResolver()) .setDevelopmentMode(true) .build(); } diff --git a/spring-faces/src/test/java/org/springframework/faces/config/MyBeanValidationHintResolver.java b/spring-faces/src/test/java/org/springframework/faces/config/MyBeanValidationHintResolver.java new file mode 100644 index 00000000..5847eba6 --- /dev/null +++ b/spring-faces/src/test/java/org/springframework/faces/config/MyBeanValidationHintResolver.java @@ -0,0 +1,7 @@ +package org.springframework.faces.config; + +import org.springframework.webflow.validation.BeanValidationHintResolver; + +public class MyBeanValidationHintResolver extends BeanValidationHintResolver { + +} diff --git a/spring-faces/src/test/java/org/springframework/faces/config/flow-builder-services.xml b/spring-faces/src/test/java/org/springframework/faces/config/flow-builder-services.xml index ab97d5aa..1f85ccf0 100644 --- a/spring-faces/src/test/java/org/springframework/faces/config/flow-builder-services.xml +++ b/spring-faces/src/test/java/org/springframework/faces/config/flow-builder-services.xml @@ -6,7 +6,7 @@ http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/faces - http://www.springframework.org/schema/faces/spring-faces-2.2.xsd"> + http://www.springframework.org/schema/faces/spring-faces-2.4.xsd"> @@ -15,7 +15,10 @@ + conversion-service="customConversionService" + validator="customValidator" + validation-hint-resolver="customValidationHintResolver" + development="true" /> @@ -32,4 +35,8 @@ + + + + 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 97d1577f..9bf9002d 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 @@ -1,5 +1,5 @@ /* - * Copyright 2004-2012 the original author or authors. + * Copyright 2004-2014 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. @@ -119,6 +119,13 @@ import org.springframework.webflow.security.SecurityRule; */ public class FlowModelFlowBuilder extends AbstractFlowBuilder { + private static final boolean IS_SPRING_FACES_PRESENT = ClassUtils.isPresent( + "org.springframework.faces.webflow.FlowActionListener", FlowModelFlowBuilder.class.getClassLoader()); + + public static final String VALIDATOR_FLOW_ATTR = FlowModelFlowBuilder.class.getSimpleName() + ".validator"; + + public static final String VALIDATION_HINT_RESOLVER_FLOW_ATTR = FlowModelFlowBuilder.class.getSimpleName() + ".validationHintResolver"; + private FlowModelHolder flowModelHolder; private FlowModel flowModel; @@ -155,6 +162,10 @@ public class FlowModelFlowBuilder extends AbstractFlowBuilder { String flowId = getContext().getFlowId(); AttributeMap flowAttributes = parseFlowMetaAttributes(flowModel); flowAttributes = getContext().getFlowAttributes().union(flowAttributes); + if (IS_SPRING_FACES_PRESENT) { + flowAttributes.asMap().put(VALIDATOR_FLOW_ATTR, getLocalContext().getValidator()); + flowAttributes.asMap().put(VALIDATION_HINT_RESOLVER_FLOW_ATTR, getLocalContext().getValidationHintResolver()); + } Flow flow = getLocalContext().getFlowArtifactFactory().createFlow(flowId, flowAttributes); flow.setApplicationContext(getLocalContext().getApplicationContext()); return flow; diff --git a/src/reference/views.xml b/src/reference/views.xml index 6f3a3cd4..386728a0 100644 --- a/src/reference/views.xml +++ b/src/reference/views.xml @@ -529,14 +529,6 @@ private String name; <webflow:flow-builder-services id="flowBuilderServices" validator=".." validationHintResolver=".." /> - - The above configuration example works in applications not using JSF. - For JSF applications that rely on Web Flow-based validation, - you will need to register a - custom extension of Web Flow's FlowActionListener - via faces-config.xml configured with the custom - ValidationHintResolver. -