Skip to content

Commit ac25db6

Browse files
committed
LocalValidatorFactoryBean properly supports unwrap at ValidatorFactory level
Issue: SPR-15561 (cherry picked from commit cb3d1be)
1 parent 22bf9fe commit ac25db6

File tree

4 files changed

+115
-12
lines changed

4 files changed

+115
-12
lines changed

spring-context/src/main/java/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.java

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2016 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@
3131
import javax.validation.MessageInterpolator;
3232
import javax.validation.TraversableResolver;
3333
import javax.validation.Validation;
34+
import javax.validation.ValidationException;
3435
import javax.validation.ValidationProviderResolver;
3536
import javax.validation.Validator;
3637
import javax.validation.ValidatorContext;
@@ -74,6 +75,7 @@
7475
* instead. If you really need programmatic {@code #forExecutables} access, inject this class as
7576
* a {@link ValidatorFactory} and call {@link #getValidator()} on it, then {@code #forExecutables}
7677
* on the returned native {@link Validator} reference instead of directly on this class.
78+
* Alternatively, call {@code #unwrap(Validator.class) which will also provide the native object.
7779
*
7880
* <p>This class is also being used by Spring's MVC configuration namespace, in case of the
7981
* {@code javax.validation} API being present but no explicit Validator having been configured.
@@ -397,6 +399,30 @@ public ConstraintValidatorFactory getConstraintValidatorFactory() {
397399
return this.validatorFactory.getConstraintValidatorFactory();
398400
}
399401

402+
@Override
403+
@SuppressWarnings("unchecked")
404+
public <T> T unwrap(Class<T> type) {
405+
if (type == null || !ValidatorFactory.class.isAssignableFrom(type)) {
406+
try {
407+
return super.unwrap(type);
408+
}
409+
catch (ValidationException ex) {
410+
// ignore - we'll try ValidatorFactory unwrapping next
411+
}
412+
}
413+
try {
414+
return this.validatorFactory.unwrap(type);
415+
}
416+
catch (ValidationException ex) {
417+
// ignore if just being asked for ValidatorFactory
418+
if (ValidatorFactory.class == type) {
419+
return (T) this.validatorFactory;
420+
}
421+
throw ex;
422+
}
423+
}
424+
425+
400426
public void close() {
401427
if (closeMethod != null && this.validatorFactory != null) {
402428
ReflectionUtils.invokeMethod(closeMethod, this.validatorFactory);

spring-context/src/test/java/org/springframework/validation/beanvalidation/SpringValidatorAdapterTests.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import javax.validation.ConstraintValidatorContext;
2828
import javax.validation.Payload;
2929
import javax.validation.Validation;
30+
import javax.validation.Validator;
3031
import javax.validation.constraints.Pattern;
3132
import javax.validation.constraints.Size;
3233

@@ -51,8 +52,9 @@
5152
*/
5253
public class SpringValidatorAdapterTests {
5354

54-
private final SpringValidatorAdapter validatorAdapter = new SpringValidatorAdapter(
55-
Validation.buildDefaultValidatorFactory().getValidator());
55+
private final Validator nativeValidator = Validation.buildDefaultValidatorFactory().getValidator();
56+
57+
private final SpringValidatorAdapter validatorAdapter = new SpringValidatorAdapter(nativeValidator);
5658

5759
private final StaticMessageSource messageSource = new StaticMessageSource();
5860

@@ -66,6 +68,12 @@ public void setupSpringValidatorAdapter() {
6668
}
6769

6870

71+
@Test
72+
public void testUnwrap() {
73+
Validator nativeValidator = validatorAdapter.unwrap(Validator.class);
74+
assertSame(this.nativeValidator, nativeValidator);
75+
}
76+
6977
@Test // SPR-13406
7078
public void testNoStringArgumentValue() {
7179
TestBean testBean = new TestBean();

spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java

Lines changed: 60 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2014 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -33,11 +33,18 @@
3333
import javax.validation.ConstraintViolation;
3434
import javax.validation.Payload;
3535
import javax.validation.Valid;
36+
import javax.validation.Validator;
37+
import javax.validation.ValidatorFactory;
3638
import javax.validation.constraints.NotNull;
3739

3840
import org.hibernate.validator.HibernateValidator;
41+
import org.hibernate.validator.HibernateValidatorFactory;
3942
import org.junit.Test;
4043

44+
import org.springframework.beans.factory.annotation.Autowired;
45+
import org.springframework.context.ConfigurableApplicationContext;
46+
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
47+
import org.springframework.core.env.Environment;
4148
import org.springframework.validation.BeanPropertyBindingResult;
4249
import org.springframework.validation.Errors;
4350
import org.springframework.validation.FieldError;
@@ -47,17 +54,17 @@
4754
import static org.junit.Assert.*;
4855

4956
/**
50-
* Tested against Hibernate Validator 4.3, as of Spring 4.0.
57+
* Tests against Hibernate Validator 5.x.
5158
*
5259
* @author Juergen Hoeller
53-
* @since 3.0
5460
*/
5561
public class ValidatorFactoryTests {
5662

5763
@Test
5864
public void testSimpleValidation() throws Exception {
5965
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
6066
validator.afterPropertiesSet();
67+
6168
ValidPerson person = new ValidPerson();
6269
Set<ConstraintViolation<ValidPerson>> result = validator.validate(person);
6370
assertEquals(2, result.size());
@@ -70,6 +77,12 @@ public void testSimpleValidation() throws Exception {
7077
fail("Invalid constraint violation with path '" + path + "'");
7178
}
7279
}
80+
81+
Validator nativeValidator = validator.unwrap(Validator.class);
82+
assertTrue(nativeValidator.getClass().getName().startsWith("org.hibernate"));
83+
assertTrue(validator.unwrap(ValidatorFactory.class) instanceof HibernateValidatorFactory);
84+
assertTrue(validator.unwrap(HibernateValidatorFactory.class) instanceof HibernateValidatorFactory);
85+
7386
validator.destroy();
7487
}
7588

@@ -78,6 +91,7 @@ public void testSimpleValidationWithCustomProvider() throws Exception {
7891
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
7992
validator.setProviderClass(HibernateValidator.class);
8093
validator.afterPropertiesSet();
94+
8195
ValidPerson person = new ValidPerson();
8296
Set<ConstraintViolation<ValidPerson>> result = validator.validate(person);
8397
assertEquals(2, result.size());
@@ -90,6 +104,12 @@ public void testSimpleValidationWithCustomProvider() throws Exception {
90104
fail("Invalid constraint violation with path '" + path + "'");
91105
}
92106
}
107+
108+
Validator nativeValidator = validator.unwrap(Validator.class);
109+
assertTrue(nativeValidator.getClass().getName().startsWith("org.hibernate"));
110+
assertTrue(validator.unwrap(ValidatorFactory.class) instanceof HibernateValidatorFactory);
111+
assertTrue(validator.unwrap(HibernateValidatorFactory.class) instanceof HibernateValidatorFactory);
112+
93113
validator.destroy();
94114
}
95115

@@ -112,6 +132,7 @@ public void testSimpleValidationWithClassLevel() throws Exception {
112132
public void testSpringValidationFieldType() throws Exception {
113133
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
114134
validator.afterPropertiesSet();
135+
115136
ValidPerson person = new ValidPerson();
116137
person.setName("Phil");
117138
person.getAddress().setStreet("Phil's Street");
@@ -126,6 +147,7 @@ public void testSpringValidationFieldType() throws Exception {
126147
public void testSpringValidation() throws Exception {
127148
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
128149
validator.afterPropertiesSet();
150+
129151
ValidPerson person = new ValidPerson();
130152
BeanPropertyBindingResult result = new BeanPropertyBindingResult(person, "person");
131153
validator.validate(person, result);
@@ -153,6 +175,7 @@ public void testSpringValidation() throws Exception {
153175
public void testSpringValidationWithClassLevel() throws Exception {
154176
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
155177
validator.afterPropertiesSet();
178+
156179
ValidPerson person = new ValidPerson();
157180
person.setName("Juergen");
158181
person.getAddress().setStreet("Juergen's Street");
@@ -166,10 +189,32 @@ public void testSpringValidationWithClassLevel() throws Exception {
166189
assertTrue(errorCodes.contains("NameAddressValid"));
167190
}
168191

192+
@Test
193+
public void testSpringValidationWithAutowiredValidator() throws Exception {
194+
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(
195+
LocalValidatorFactoryBean.class);
196+
LocalValidatorFactoryBean validator = ctx.getBean(LocalValidatorFactoryBean.class);
197+
198+
ValidPerson person = new ValidPerson();
199+
person.expectsAutowiredValidator = true;
200+
person.setName("Juergen");
201+
person.getAddress().setStreet("Juergen's Street");
202+
BeanPropertyBindingResult result = new BeanPropertyBindingResult(person, "person");
203+
validator.validate(person, result);
204+
assertEquals(1, result.getErrorCount());
205+
ObjectError globalError = result.getGlobalError();
206+
List<String> errorCodes = Arrays.asList(globalError.getCodes());
207+
assertEquals(2, errorCodes.size());
208+
assertTrue(errorCodes.contains("NameAddressValid.person"));
209+
assertTrue(errorCodes.contains("NameAddressValid"));
210+
ctx.close();
211+
}
212+
169213
@Test
170214
public void testSpringValidationWithErrorInListElement() throws Exception {
171215
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
172216
validator.afterPropertiesSet();
217+
173218
ValidPerson person = new ValidPerson();
174219
person.getAddressList().add(new ValidAddress());
175220
BeanPropertyBindingResult result = new BeanPropertyBindingResult(person, "person");
@@ -187,6 +232,7 @@ public void testSpringValidationWithErrorInListElement() throws Exception {
187232
public void testSpringValidationWithErrorInSetElement() throws Exception {
188233
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
189234
validator.afterPropertiesSet();
235+
190236
ValidPerson person = new ValidPerson();
191237
person.getAddressSet().add(new ValidAddress());
192238
BeanPropertyBindingResult result = new BeanPropertyBindingResult(person, "person");
@@ -235,10 +281,12 @@ public static class ValidPerson {
235281
private ValidAddress address = new ValidAddress();
236282

237283
@Valid
238-
private List<ValidAddress> addressList = new LinkedList<ValidAddress>();
284+
private List<ValidAddress> addressList = new LinkedList<>();
239285

240286
@Valid
241-
private Set<ValidAddress> addressSet = new LinkedHashSet<ValidAddress>();
287+
private Set<ValidAddress> addressSet = new LinkedHashSet<>();
288+
289+
public boolean expectsAutowiredValidator = false;
242290

243291
public String getName() {
244292
return name;
@@ -304,12 +352,18 @@ public void setStreet(String street) {
304352

305353
public static class NameAddressValidator implements ConstraintValidator<NameAddressValid, ValidPerson> {
306354

355+
@Autowired
356+
private Environment environment;
357+
307358
@Override
308359
public void initialize(NameAddressValid constraintAnnotation) {
309360
}
310361

311362
@Override
312363
public boolean isValid(ValidPerson value, ConstraintValidatorContext context) {
364+
if (value.expectsAutowiredValidator) {
365+
assertNotNull(this.environment);
366+
}
313367
boolean valid = (value.name == null || !value.address.street.contains(value.name));
314368
if (!valid && "Phil".equals(value.name)) {
315369
context.buildConstraintViolationWithTemplate(
@@ -378,7 +432,7 @@ public void initialize(InnerValid constraintAnnotation) {
378432
public boolean isValid(InnerBean bean, ConstraintValidatorContext context) {
379433
context.disableDefaultConstraintViolation();
380434
if (bean.getValue() == null) {
381-
context.buildConstraintViolationWithTemplate("NULL"). addNode("value").addConstraintViolation();
435+
context.buildConstraintViolationWithTemplate("NULL").addNode("value").addConstraintViolation();
382436
return false;
383437
}
384438
return true;

spring-orm-hibernate4/src/test/java/org/springframework/validation/hibernatevalidator5/ValidatorFactoryTests.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2017 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -33,9 +33,12 @@
3333
import javax.validation.ConstraintViolation;
3434
import javax.validation.Payload;
3535
import javax.validation.Valid;
36+
import javax.validation.Validator;
37+
import javax.validation.ValidatorFactory;
3638
import javax.validation.constraints.NotNull;
3739

3840
import org.hibernate.validator.HibernateValidator;
41+
import org.hibernate.validator.HibernateValidatorFactory;
3942
import org.junit.Test;
4043

4144
import org.springframework.beans.factory.annotation.Autowired;
@@ -77,6 +80,12 @@ public void testSimpleValidation() throws Exception {
7780
fail("Invalid constraint violation with path '" + path + "'");
7881
}
7982
}
83+
84+
Validator nativeValidator = validator.unwrap(Validator.class);
85+
assertTrue(nativeValidator.getClass().getName().startsWith("org.hibernate"));
86+
assertTrue(validator.unwrap(ValidatorFactory.class) instanceof HibernateValidatorFactory);
87+
assertTrue(validator.unwrap(HibernateValidatorFactory.class) instanceof HibernateValidatorFactory);
88+
8089
validator.destroy();
8190
}
8291

@@ -98,6 +107,12 @@ public void testSimpleValidationWithCustomProvider() throws Exception {
98107
fail("Invalid constraint violation with path '" + path + "'");
99108
}
100109
}
110+
111+
Validator nativeValidator = validator.unwrap(Validator.class);
112+
assertTrue(nativeValidator.getClass().getName().startsWith("org.hibernate"));
113+
assertTrue(validator.unwrap(ValidatorFactory.class) instanceof HibernateValidatorFactory);
114+
assertTrue(validator.unwrap(HibernateValidatorFactory.class) instanceof HibernateValidatorFactory);
115+
101116
validator.destroy();
102117
}
103118

@@ -269,10 +284,10 @@ public static class ValidPerson {
269284
private ValidAddress address = new ValidAddress();
270285

271286
@Valid
272-
private List<ValidAddress> addressList = new LinkedList<ValidAddress>();
287+
private List<ValidAddress> addressList = new LinkedList<>();
273288

274289
@Valid
275-
private Set<ValidAddress> addressSet = new LinkedHashSet<ValidAddress>();
290+
private Set<ValidAddress> addressSet = new LinkedHashSet<>();
276291

277292
public boolean expectsAutowiredValidator = false;
278293

0 commit comments

Comments
 (0)