Skip to content

Commit 5d87612

Browse files
committed
Refine validation auto-configuration
Checking for the presence of a `ValidationProvider` resource is unfortunately not enough to trigger the validation auto-configuration. If `hibernate-validator` is added on the classpath without a `javax.el` implementation, the former will blow up on startup. So far this was a pilot error so we didn't have to care about it. Now that we have an auto-configuration that may lead to this error, we need to be extra careful and check that scenario ourselves. This commit adds an extra condition that runs as late as possible and attempt to actually initialize a `Validator`. If that fails for whatever reason, the auto-configuration will now back off. Several additional tests have been added to exercise this scenario. Closes gh-6228
1 parent 50c3966 commit 5d87612

File tree

3 files changed

+142
-0
lines changed

3 files changed

+142
-0
lines changed

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/validation/ValidationAutoConfiguration.java

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,22 @@
1616

1717
package org.springframework.boot.autoconfigure.validation;
1818

19+
import javax.validation.Validation;
1920
import javax.validation.Validator;
2021

2122
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
23+
import org.springframework.boot.autoconfigure.condition.ConditionMessage;
24+
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
2225
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2326
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
2427
import org.springframework.boot.autoconfigure.condition.ConditionalOnResource;
28+
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
2529
import org.springframework.context.annotation.Bean;
30+
import org.springframework.context.annotation.ConditionContext;
31+
import org.springframework.context.annotation.Conditional;
32+
import org.springframework.core.Ordered;
33+
import org.springframework.core.annotation.Order;
34+
import org.springframework.core.type.AnnotatedTypeMetadata;
2635
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
2736

2837
/**
@@ -37,9 +46,29 @@ public class ValidationAutoConfiguration {
3746

3847
@Bean
3948
@ConditionalOnResource(resources = "META-INF/services/javax.validation.spi.ValidationProvider")
49+
@Conditional(OnValidatorAvailableCondition.class)
4050
@ConditionalOnMissingBean
4151
public MethodValidationPostProcessor methodValidationPostProcessor() {
4252
return new MethodValidationPostProcessor();
4353
}
4454

55+
@Order(Ordered.LOWEST_PRECEDENCE)
56+
static class OnValidatorAvailableCondition extends SpringBootCondition {
57+
58+
@Override
59+
public ConditionOutcome getMatchOutcome(ConditionContext context,
60+
AnnotatedTypeMetadata metadata) {
61+
ConditionMessage.Builder message = ConditionMessage.forCondition(
62+
getClass().getName());
63+
try {
64+
Validation.buildDefaultValidatorFactory().getValidator();
65+
return ConditionOutcome.match(message.available("JSR-303 provider"));
66+
}
67+
catch (Exception ex) {
68+
return ConditionOutcome.noMatch(message.notAvailable("JSR-303 provider"));
69+
}
70+
}
71+
72+
}
73+
4574
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2012-2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.validation;
18+
19+
import org.junit.After;
20+
import org.junit.Test;
21+
import org.junit.runner.RunWith;
22+
23+
import org.springframework.boot.junit.runner.classpath.ClassPathExclusions;
24+
import org.springframework.boot.junit.runner.classpath.ModifiedClassPathRunner;
25+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
26+
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
27+
28+
import static org.assertj.core.api.Assertions.assertThat;
29+
30+
/**
31+
* Test for {{@link ValidationAutoConfiguration} when Hibernate validator is present
32+
* but no EL implementation is available.
33+
*
34+
* @author Stephane Nicoll
35+
*/
36+
@RunWith(ModifiedClassPathRunner.class)
37+
@ClassPathExclusions("tomcat-embed-el-*.jar")
38+
public class ValidationAutoConfigurationWithHibernateValidatorMissingElImplTests {
39+
40+
private AnnotationConfigApplicationContext context;
41+
42+
@After
43+
public void close() {
44+
if (this.context != null) {
45+
this.context.close();
46+
}
47+
}
48+
49+
@Test
50+
public void validationIsDisabled() {
51+
this.context = new AnnotationConfigApplicationContext(
52+
ValidationAutoConfiguration.class);
53+
assertThat(this.context.getBeansOfType(MethodValidationPostProcessor.class))
54+
.isEmpty();
55+
}
56+
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2012-2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.validation;
18+
19+
import org.junit.After;
20+
import org.junit.Test;
21+
import org.junit.runner.RunWith;
22+
23+
import org.springframework.boot.junit.runner.classpath.ClassPathExclusions;
24+
import org.springframework.boot.junit.runner.classpath.ModifiedClassPathRunner;
25+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
26+
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
27+
28+
import static org.assertj.core.api.Assertions.assertThat;
29+
30+
/**
31+
* Test for {{@link ValidationAutoConfiguration} when no JSR-303 provider is available.
32+
*
33+
* @author Stephane Nicoll
34+
*/
35+
@RunWith(ModifiedClassPathRunner.class)
36+
@ClassPathExclusions("hibernate-validator-*.jar")
37+
public class ValidationAutoConfigurationWithoutValidatorTests {
38+
39+
private AnnotationConfigApplicationContext context;
40+
41+
@After
42+
public void close() {
43+
if (this.context != null) {
44+
this.context.close();
45+
}
46+
}
47+
48+
@Test
49+
public void validationIsDisabled() {
50+
this.context = new AnnotationConfigApplicationContext(
51+
ValidationAutoConfiguration.class);
52+
assertThat(this.context.getBeansOfType(MethodValidationPostProcessor.class))
53+
.isEmpty();
54+
}
55+
56+
}

0 commit comments

Comments
 (0)