Support providing validator in faces builder serivces

Issue: SWF-1494
This commit is contained in:
Rossen Stoyanchev
2014-03-31 17:28:31 -04:00
parent 33bd09801b
commit cce1515a17
11 changed files with 138 additions and 27 deletions

View File

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

View File

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

View File

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

View File

@@ -45,6 +45,26 @@ The custom ExpressionParser implementation to use to compile expression strings
<xsd:documentation>
<![CDATA[
The custom ViewFactoryCreator implementation to use produce ViewFactories capable of rendering Views.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="validator">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.validation.Validator">
<![CDATA[
The bean name of the Validator that is to be used for validating a model declared on a view state.
This is an optional attribute. By default it is not set.
]]>
</xsd:documentation>
</xsd:annotation>
</xsd:attribute>
<xsd:attribute name="validation-hint-resolver">
<xsd:annotation>
<xsd:documentation source="java:org.springframework.webflow.validation.ValidationHintResolver">
<![CDATA[
The bean name of a ValidationHintResolver used to resolve String-based validation hints.
This is an optional attribute. The default an instance of BeanValidationHintResolver is used.
]]>
</xsd:documentation>
</xsd:annotation>

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,7 @@
package org.springframework.faces.config;
import org.springframework.webflow.validation.BeanValidationHintResolver;
public class MyBeanValidationHintResolver extends BeanValidationHintResolver {
}

View File

@@ -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">
<faces:flow-builder-services id="flowBuilderServicesDefault"/>
@@ -15,7 +15,10 @@
<faces:flow-builder-services id="flowBuilderServicesAllCustom"
expression-parser="customExpressionParser"
view-factory-creator="customViewFactoryCreator"
conversion-service="customConversionService" development="true" />
conversion-service="customConversionService"
validator="customValidator"
validation-hint-resolver="customValidationHintResolver"
development="true" />
<faces:flow-builder-services id="flowBuilderServicesConversionServiceCustom"
conversion-service="customConversionService"/>
@@ -32,4 +35,8 @@
<bean id="customConversionService"
class="org.springframework.faces.config.AbstractFacesFlowBuilderServicesConfigurationTests$TestConversionService"/>
<bean id="customValidator" class="org.springframework.faces.config.EmptySpringValidator" />
<bean id="customValidationHintResolver" class="org.springframework.faces.config.MyBeanValidationHintResolver" />
</beans>

View File

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

View File

@@ -529,14 +529,6 @@ private String name;
&lt;webflow:flow-builder-services id="flowBuilderServices" validator=".." validationHintResolver=".." /&gt;
</programlisting>
</para>
<para>
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 <emphasis>FlowActionListener</emphasis>
via faces-config.xml configured with the custom
<classname>ValidationHintResolver</classname>.
</para>
</sect3>
</sect2>