Commit f42998f5 authored by Stephane Nicoll's avatar Stephane Nicoll

Revert "Refine validator and MVC validator configuration"

This commit reverts c9561f03 and 69a8c0d8 and effectivly fixes gh-8979 but
reintroduces the issue reported in gh-8495.
parent 4d8584eb
...@@ -41,7 +41,6 @@ import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMappingCusto ...@@ -41,7 +41,6 @@ import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMappingCusto
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration; import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration; import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration; import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration;
import org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration; import org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration;
...@@ -157,9 +156,9 @@ public class EndpointMvcIntegrationTests { ...@@ -157,9 +156,9 @@ public class EndpointMvcIntegrationTests {
@Documented @Documented
@Import({ EmbeddedServletContainerAutoConfiguration.class, @Import({ EmbeddedServletContainerAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class, ServerPropertiesAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class, DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class,
WebMvcAutoConfiguration.class, JacksonAutoConfiguration.class, JacksonAutoConfiguration.class, ErrorMvcAutoConfiguration.class,
ErrorMvcAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class }) PropertyPlaceholderAutoConfiguration.class })
protected @interface MinimalWebConfiguration { protected @interface MinimalWebConfiguration {
} }
......
/*
* Copyright 2012-2017 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.boot.autoconfigure.validation;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.validation.MessageInterpolatorFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
/**
* Default validator configuration imported by {@link ValidationAutoConfiguration}.
*
* @author Stephane Nicoll
* @author Phillip Webb
*/
@Configuration
class DefaultValidatorConfiguration {
@Bean
@ConditionalOnMissingBean(type = { "javax.validation.Validator",
"org.springframework.validation.Validator" })
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public static LocalValidatorFactoryBean defaultValidator() {
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory();
factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
return factoryBean;
}
}
/*
* Copyright 2012-2017 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.boot.autoconfigure.validation;
import org.springframework.util.Assert;
import org.springframework.validation.Errors;
import org.springframework.validation.SmartValidator;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
/**
* {@link Validator} implementation that delegates calls to another {@link Validator}.
* This {@link Validator} implements Spring's {@link SmartValidator} interface but does
* not implement the JSR-303 {@code javax.validator.Validator} interface.
*
* @author Phillip Webb
* @since 1.5.3
*/
public class DelegatingValidator implements SmartValidator {
private final Validator delegate;
/**
* Create a new {@link DelegatingValidator} instance.
* @param targetValidator the target JSR validator
*/
public DelegatingValidator(javax.validation.Validator targetValidator) {
this.delegate = new SpringValidatorAdapter(targetValidator);
}
/**
* Create a new {@link DelegatingValidator} instance.
* @param targetValidator the target validator
*/
public DelegatingValidator(Validator targetValidator) {
Assert.notNull(targetValidator, "Target Validator must not be null");
this.delegate = targetValidator;
}
@Override
public boolean supports(Class<?> clazz) {
return this.delegate.supports(clazz);
}
@Override
public void validate(Object target, Errors errors) {
this.delegate.validate(target, errors);
}
@Override
public void validate(Object target, Errors errors, Object... validationHints) {
if (this.delegate instanceof SmartValidator) {
((SmartValidator) this.delegate).validate(target, errors, validationHints);
}
else {
this.delegate.validate(target, errors);
}
}
/**
* Return the delegate validator.
* @return the delegate validator
*/
protected final Validator getDelegate() {
return this.delegate;
}
}
/*
* Copyright 2012-2017 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.boot.autoconfigure.validation;
import javax.validation.Validator;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Role;
import org.springframework.validation.SmartValidator;
/**
* JSR 303 adapter configration imported by {@link ValidationAutoConfiguration}.
*
* @author Stephane Nicoll
* @author Phillip Webb
*/
@Configuration
class Jsr303ValidatorAdapterConfiguration {
@Bean
@ConditionalOnSingleCandidate(Validator.class)
@ConditionalOnMissingBean(org.springframework.validation.Validator.class)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public SmartValidator jsr303ValidatorAdapter(Validator validator) {
return new DelegatingValidator(validator);
}
}
...@@ -19,16 +19,18 @@ package org.springframework.boot.autoconfigure.validation; ...@@ -19,16 +19,18 @@ package org.springframework.boot.autoconfigure.validation;
import javax.validation.Validator; import javax.validation.Validator;
import javax.validation.executable.ExecutableValidator; import javax.validation.executable.ExecutableValidator;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnResource; import org.springframework.boot.autoconfigure.condition.ConditionalOnResource;
import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.boot.validation.MessageInterpolatorFactory;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Role;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
/** /**
...@@ -41,12 +43,19 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess ...@@ -41,12 +43,19 @@ import org.springframework.validation.beanvalidation.MethodValidationPostProcess
@Configuration @Configuration
@ConditionalOnClass(ExecutableValidator.class) @ConditionalOnClass(ExecutableValidator.class)
@ConditionalOnResource(resources = "classpath:META-INF/services/javax.validation.spi.ValidationProvider") @ConditionalOnResource(resources = "classpath:META-INF/services/javax.validation.spi.ValidationProvider")
@Import({ DefaultValidatorConfiguration.class,
Jsr303ValidatorAdapterConfiguration.class })
public class ValidationAutoConfiguration { public class ValidationAutoConfiguration {
@Bean @Bean
@ConditionalOnBean(Validator.class) @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@ConditionalOnMissingBean
public static Validator jsr303Validator() {
LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
MessageInterpolatorFactory interpolatorFactory = new MessageInterpolatorFactory();
factoryBean.setMessageInterpolator(interpolatorFactory.getObject());
return factoryBean;
}
@Bean
@ConditionalOnMissingBean @ConditionalOnMissingBean
public static MethodValidationPostProcessor methodValidationPostProcessor( public static MethodValidationPostProcessor methodValidationPostProcessor(
Environment environment, Validator validator) { Environment environment, Validator validator) {
......
/*
* Copyright 2012-2017 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.boot.autoconfigure.web;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.validation.MessageInterpolatorFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.validation.Errors;
import org.springframework.validation.SmartValidator;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;
/**
* A {@link SmartValidator} exposed as a bean for WebMvc use. Wraps existing
* {@link SpringValidatorAdapter} instances so that only the Spring's {@link Validator}
* type is exposed. This prevents such a bean to expose both the Spring and JSR-303
* validator contract at the same time.
*
* @author Stephane Nicoll
* @author Phillip Webb
*/
class WebMvcValidator implements SmartValidator, ApplicationContextAware,
InitializingBean, DisposableBean {
private final SpringValidatorAdapter target;
private final boolean existingBean;
WebMvcValidator(SpringValidatorAdapter target, boolean existingBean) {
this.target = target;
this.existingBean = existingBean;
}
SpringValidatorAdapter getTarget() {
return this.target;
}
@Override
public boolean supports(Class<?> clazz) {
return this.target.supports(clazz);
}
@Override
public void validate(Object target, Errors errors) {
this.target.validate(target, errors);
}
@Override
public void validate(Object target, Errors errors, Object... validationHints) {
this.target.validate(target, errors, validationHints);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
if (!this.existingBean && this.target instanceof ApplicationContextAware) {
((ApplicationContextAware) this.target)
.setApplicationContext(applicationContext);
}
}
@Override
public void afterPropertiesSet() throws Exception {
if (!this.existingBean && this.target instanceof InitializingBean) {
((InitializingBean) this.target).afterPropertiesSet();
}
}
@Override
public void destroy() throws Exception {
if (!this.existingBean && this.target instanceof DisposableBean) {
((DisposableBean) this.target).destroy();
}
}
public static Validator get(ApplicationContext applicationContext,
Validator validator) {
if (validator != null) {
return wrap(validator, false);
}
return getExistingOrCreate(applicationContext);
}
private static Validator getExistingOrCreate(ApplicationContext applicationContext) {
Validator existing = getExisting(applicationContext);
if (existing != null) {
return wrap(existing, true);
}
return create();
}
private static Validator getExisting(ApplicationContext applicationContext) {
try {
javax.validation.Validator validator = applicationContext
.getBean(javax.validation.Validator.class);
if (validator instanceof Validator) {
return (Validator) validator;
}
return new SpringValidatorAdapter(validator);
}
catch (NoSuchBeanDefinitionException ex) {
return null;
}
}
private static Validator create() {
OptionalValidatorFactoryBean validator = new OptionalValidatorFactoryBean();
validator.setMessageInterpolator(new MessageInterpolatorFactory().getObject());
return wrap(validator, false);
}
private static Validator wrap(Validator validator, boolean existingBean) {
if (validator instanceof javax.validation.Validator) {
if (validator instanceof SpringValidatorAdapter) {
return new WebMvcValidator((SpringValidatorAdapter) validator,
existingBean);
}
return new WebMvcValidator(
new SpringValidatorAdapter((javax.validation.Validator) validator),
existingBean);
}
return validator;
}
}
...@@ -31,7 +31,6 @@ import org.junit.Test; ...@@ -31,7 +31,6 @@ import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration; import org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration;
import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration; import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration;
import org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration; import org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration;
...@@ -325,9 +324,9 @@ public class SpringBootWebSecurityConfigurationTests { ...@@ -325,9 +324,9 @@ public class SpringBootWebSecurityConfigurationTests {
@Documented @Documented
@Import({ EmbeddedServletContainerAutoConfiguration.class, @Import({ EmbeddedServletContainerAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class, ServerPropertiesAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class, DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class,
WebMvcAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, ErrorMvcAutoConfiguration.class,
ErrorMvcAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class }) PropertyPlaceholderAutoConfiguration.class })
protected @interface MinimalWebConfiguration { protected @interface MinimalWebConfiguration {
} }
......
/*
* Copyright 2012-2017 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.boot.autoconfigure.validation;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.Errors;
import org.springframework.validation.SmartValidator;
import org.springframework.validation.Validator;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link DelegatingValidator}.
*
* @author Phillip Webb
*/
public class DelegatingValidatorTests {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Mock
private SmartValidator delegate;
private DelegatingValidator delegating;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.delegating = new DelegatingValidator(this.delegate);
}
@Test
public void createWhenJsrValidatorIsNullShouldThrowException() throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Target Validator must not be null");
new DelegatingValidator((javax.validation.Validator) null);
}
@Test
public void createWithJsrValidatorShouldAdapt() throws Exception {
javax.validation.Validator delegate = mock(javax.validation.Validator.class);
Validator delegating = new DelegatingValidator(delegate);
Object target = new Object();
Errors errors = new BeanPropertyBindingResult(target, "foo");
delegating.validate(target, errors);
verify(delegate).validate(any());
}
@Test
public void createWithSpringValidatorWhenValidatorIsNullShouldThrowException()
throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Target Validator must not be null");
new DelegatingValidator((Validator) null);
}
@Test
public void supportsShouldDelegateToValidator() throws Exception {
this.delegating.supports(Object.class);
verify(this.delegate).supports(Object.class);
}
@Test
public void validateShouldDelegateToValidator() throws Exception {
Object target = new Object();
Errors errors = new BeanPropertyBindingResult(target, "foo");
this.delegating.validate(target, errors);
verify(this.delegate).validate(target, errors);
}
@Test
public void validateWithHintsShouldDelegateToValidator() throws Exception {
Object target = new Object();
Errors errors = new BeanPropertyBindingResult(target, "foo");
Object[] hints = { "foo", "bar" };
this.delegating.validate(target, errors, hints);
verify(this.delegate).validate(target, errors, hints);
}
@Test
public void validateWithHintsWhenDelegateIsNotSmartShouldDelegateToSimpleValidator()
throws Exception {
Validator delegate = mock(Validator.class);
DelegatingValidator delegating = new DelegatingValidator(delegate);
Object target = new Object();
Errors errors = new BeanPropertyBindingResult(target, "foo");
Object[] hints = { "foo", "bar" };
delegating.validate(target, errors, hints);
verify(delegate).validate(target, errors);
}
}
...@@ -32,12 +32,9 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext ...@@ -32,12 +32,9 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
import org.springframework.validation.beanvalidation.OptionalValidatorFactoryBean;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
/** /**
* Tests for {@link ValidationAutoConfiguration}. * Tests for {@link ValidationAutoConfiguration}.
...@@ -59,94 +56,45 @@ public class ValidationAutoConfigurationTests { ...@@ -59,94 +56,45 @@ public class ValidationAutoConfigurationTests {
} }
@Test @Test
public void validationAutoConfigurationShouldConfigureJsrAndSpringValidator() public void validationIsEnabled() {
throws Exception { load(SampleService.class);
load(Config.class);
Validator jsrValidator = this.context.getBean(Validator.class);
String[] jsrValidatorNames = this.context.getBeanNamesForType(Validator.class);
org.springframework.validation.Validator springValidator = this.context
.getBean(org.springframework.validation.Validator.class);
String[] springValidatorNames = this.context
.getBeanNamesForType(org.springframework.validation.Validator.class);
assertThat(jsrValidator).isInstanceOf(LocalValidatorFactoryBean.class);
assertThat(jsrValidator).isEqualTo(springValidator);
assertThat(jsrValidatorNames).containsExactly("defaultValidator");
assertThat(springValidatorNames).containsExactly("defaultValidator");
}
@Test
public void validationAutoConfigurationWhenUserProvidesValidatorShouldBackOff()
throws Exception {
load(UserDefinedValidatorConfig.class);
Validator jsrValidator = this.context.getBean(Validator.class);
String[] jsrValidatorNames = this.context.getBeanNamesForType(Validator.class);
org.springframework.validation.Validator springValidator = this.context
.getBean(org.springframework.validation.Validator.class);
String[] springValidatorNames = this.context
.getBeanNamesForType(org.springframework.validation.Validator.class);
assertThat(jsrValidator).isInstanceOf(OptionalValidatorFactoryBean.class);
assertThat(jsrValidator).isEqualTo(springValidator);
assertThat(jsrValidatorNames).containsExactly("customValidator");
assertThat(springValidatorNames).containsExactly("customValidator");
}
@Test
public void validationAutoConfigurationWhenUserProvidesJsrOnlyShouldAdaptIt()
throws Exception {
load(UserDefinedJsrValidatorConfig.class);
Validator jsrValidator = this.context.getBean(Validator.class);
String[] jsrValidatorNames = this.context.getBeanNamesForType(Validator.class);
org.springframework.validation.Validator springValidator = this.context
.getBean(org.springframework.validation.Validator.class);
String[] springValidatorNames = this.context
.getBeanNamesForType(org.springframework.validation.Validator.class);
assertThat(jsrValidator).isNotEqualTo(springValidator);
assertThat(springValidator).isInstanceOf(DelegatingValidator.class);
assertThat(jsrValidatorNames).containsExactly("customValidator");
assertThat(springValidatorNames).containsExactly("jsr303ValidatorAdapter");
}
@Test
public void validationAutoConfigurationShouldBeEnabled() {
load(ClassWithConstraint.class);
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1); assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1);
ClassWithConstraint service = this.context.getBean(ClassWithConstraint.class); SampleService service = this.context.getBean(SampleService.class);
service.call("Valid"); service.doSomething("Valid");
this.thrown.expect(ConstraintViolationException.class); this.thrown.expect(ConstraintViolationException.class);
service.call("KO"); service.doSomething("KO");
} }
@Test @Test
public void validationAutoConfigurationShouldUseCglibProxy() { public void validationUsesCglibProxy() {
load(ImplementationOfInterfaceWithConstraint.class); load(DefaultAnotherSampleService.class);
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1); assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1);
ImplementationOfInterfaceWithConstraint service = this.context DefaultAnotherSampleService service = this.context
.getBean(ImplementationOfInterfaceWithConstraint.class); .getBean(DefaultAnotherSampleService.class);
service.call(42); service.doSomething(42);
this.thrown.expect(ConstraintViolationException.class); this.thrown.expect(ConstraintViolationException.class);
service.call(2); service.doSomething(2);
} }
@Test @Test
public void validationAutoConfigurationWhenProxyTargetClassIsFalseShouldUseJdkProxy() { public void validationCanBeConfiguredToUseJdkProxy() {
load(AnotherSampleServiceConfiguration.class, load(AnotherSampleServiceConfiguration.class,
"spring.aop.proxy-target-class=false"); "spring.aop.proxy-target-class=false");
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1); assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1);
assertThat(this.context assertThat(this.context.getBeansOfType(DefaultAnotherSampleService.class))
.getBeansOfType(ImplementationOfInterfaceWithConstraint.class)).isEmpty(); .isEmpty();
InterfaceWithConstraint service = this.context AnotherSampleService service = this.context.getBean(AnotherSampleService.class);
.getBean(InterfaceWithConstraint.class); service.doSomething(42);
service.call(42);
this.thrown.expect(ConstraintViolationException.class); this.thrown.expect(ConstraintViolationException.class);
service.call(2); service.doSomething(2);
} }
@Test @Test
public void validationAutoConfigurationWhenUserDefinesMethodValidationPostProcessorShouldBackOff() { public void userDefinedMethodValidationPostProcessorTakesPrecedence() {
load(UserDefinedMethodValidationConfig.class); load(SampleConfiguration.class);
assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1); assertThat(this.context.getBeansOfType(Validator.class)).hasSize(1);
Object userMethodValidationPostProcessor = this.context Object userMethodValidationPostProcessor = this.context
.getBean("customMethodValidationPostProcessor"); .getBean("testMethodValidationPostProcessor");
assertThat(this.context.getBean(MethodValidationPostProcessor.class)) assertThat(this.context.getBean(MethodValidationPostProcessor.class))
.isSameAs(userMethodValidationPostProcessor); .isSameAs(userMethodValidationPostProcessor);
assertThat(this.context.getBeansOfType(MethodValidationPostProcessor.class)) assertThat(this.context.getBeansOfType(MethodValidationPostProcessor.class))
...@@ -167,73 +115,47 @@ public class ValidationAutoConfigurationTests { ...@@ -167,73 +115,47 @@ public class ValidationAutoConfigurationTests {
this.context = ctx; this.context = ctx;
} }
@Configuration @Validated
static class Config { static class SampleService {
}
@Configuration public void doSomething(@Size(min = 3, max = 10) String name) {
static class UserDefinedValidatorConfig {
@Bean
public OptionalValidatorFactoryBean customValidator() {
return new OptionalValidatorFactoryBean();
} }
} }
@Configuration interface AnotherSampleService {
static class UserDefinedJsrValidatorConfig {
@Bean
public Validator customValidator() {
return mock(Validator.class);
}
void doSomething(@Min(42) Integer counter);
} }
@Configuration @Validated
static class UserDefinedMethodValidationConfig { static class DefaultAnotherSampleService implements AnotherSampleService {
@Bean @Override
public MethodValidationPostProcessor customMethodValidationPostProcessor() { public void doSomething(Integer counter) {
return new MethodValidationPostProcessor();
}
}
} }
@Configuration @Configuration
static class AnotherSampleServiceConfiguration { static class AnotherSampleServiceConfiguration {
@Bean @Bean
public InterfaceWithConstraint implementationOfInterfaceWithConstraint() { public AnotherSampleService anotherSampleService() {
return new ImplementationOfInterfaceWithConstraint(); return new DefaultAnotherSampleService();
} }
} }
@Validated @Configuration
static class ClassWithConstraint { static class SampleConfiguration {
public void call(@Size(min = 3, max = 10) String name) {
@Bean
public MethodValidationPostProcessor testMethodValidationPostProcessor() {
return new MethodValidationPostProcessor();
} }
} }
interface InterfaceWithConstraint {
void call(@Min(42) Integer counter);
}
@Validated
static class ImplementationOfInterfaceWithConstraint
implements InterfaceWithConstraint {
@Override
public void call(Integer counter) {
}
}
} }
...@@ -35,7 +35,6 @@ import org.junit.rules.ExpectedException; ...@@ -35,7 +35,6 @@ import org.junit.rules.ExpectedException;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.util.ApplicationContextTestUtils; import org.springframework.boot.test.util.ApplicationContextTestUtils;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
...@@ -127,9 +126,9 @@ public class BasicErrorControllerDirectMockMvcTests { ...@@ -127,9 +126,9 @@ public class BasicErrorControllerDirectMockMvcTests {
@Documented @Documented
@Import({ EmbeddedServletContainerAutoConfiguration.class, @Import({ EmbeddedServletContainerAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class, ServerPropertiesAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class, DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class,
WebMvcAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, ErrorMvcAutoConfiguration.class,
ErrorMvcAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class }) PropertyPlaceholderAutoConfiguration.class })
protected @interface MinimalWebConfiguration { protected @interface MinimalWebConfiguration {
} }
......
...@@ -35,7 +35,6 @@ import org.junit.runner.RunWith; ...@@ -35,7 +35,6 @@ import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
...@@ -129,9 +128,9 @@ public class BasicErrorControllerMockMvcTests { ...@@ -129,9 +128,9 @@ public class BasicErrorControllerMockMvcTests {
@Import({ EmbeddedServletContainerAutoConfiguration.EmbeddedTomcat.class, @Import({ EmbeddedServletContainerAutoConfiguration.EmbeddedTomcat.class,
EmbeddedServletContainerAutoConfiguration.class, EmbeddedServletContainerAutoConfiguration.class,
ServerPropertiesAutoConfiguration.class, ServerPropertiesAutoConfiguration.class,
DispatcherServletAutoConfiguration.class, ValidationAutoConfiguration.class, DispatcherServletAutoConfiguration.class, WebMvcAutoConfiguration.class,
WebMvcAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, ErrorMvcAutoConfiguration.class,
ErrorMvcAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class }) PropertyPlaceholderAutoConfiguration.class })
protected @interface MinimalWebConfiguration { protected @interface MinimalWebConfiguration {
} }
......
/*
* Copyright 2012-2017 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.boot.autoconfigure.web;
import java.util.HashMap;
import javax.validation.constraints.Min;
import org.junit.After;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.MapBindingResult;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
/**
* Tests for {@link WebMvcValidator}.
*
* @author Stephane Nicoll
*/
public class WebMvcValidatorTests {
private AnnotationConfigApplicationContext context;
@After
public void close() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void wrapLocalValidatorFactoryBean() {
WebMvcValidator wrapper = load(
LocalValidatorFactoryBeanConfig.class);
assertThat(wrapper.supports(SampleData.class)).isTrue();
MapBindingResult errors = new MapBindingResult(new HashMap<String, Object>(),
"test");
wrapper.validate(new SampleData(40), errors);
assertThat(errors.getErrorCount()).isEqualTo(1);
}
@Test
public void wrapperInvokesCallbackOnNonManagedBean() {
load(NonManagedBeanConfig.class);
LocalValidatorFactoryBean validator = this.context
.getBean(NonManagedBeanConfig.class).validator;
verify(validator, times(1)).setApplicationContext(any(ApplicationContext.class));
verify(validator, times(1)).afterPropertiesSet();
verify(validator, times(0)).destroy();
this.context.close();
this.context = null;
verify(validator, times(1)).destroy();
}
@Test
public void wrapperDoesNotInvokeCallbackOnManagedBean() {
load(ManagedBeanConfig.class);
LocalValidatorFactoryBean validator = this.context
.getBean(ManagedBeanConfig.class).validator;
verify(validator, times(0)).setApplicationContext(any(ApplicationContext.class));
verify(validator, times(0)).afterPropertiesSet();
verify(validator, times(0)).destroy();
this.context.close();
this.context = null;
verify(validator, times(0)).destroy();
}
private WebMvcValidator load(Class<?> config) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(config);
ctx.refresh();
this.context = ctx;
return this.context.getBean(WebMvcValidator.class);
}
@Configuration
static class LocalValidatorFactoryBeanConfig {
@Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
@Bean
public WebMvcValidator wrapper() {
return new WebMvcValidator(validator(), true);
}
}
@Configuration
static class NonManagedBeanConfig {
private final LocalValidatorFactoryBean validator = mock(
LocalValidatorFactoryBean.class);
@Bean
public WebMvcValidator wrapper() {
return new WebMvcValidator(this.validator, false);
}
}
@Configuration
static class ManagedBeanConfig {
private final LocalValidatorFactoryBean validator = mock(
LocalValidatorFactoryBean.class);
@Bean
public WebMvcValidator wrapper() {
return new WebMvcValidator(this.validator, true);
}
}
static class SampleData {
@Min(42)
private int counter;
SampleData(int counter) {
this.counter = counter;
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment