diff --git a/spring-context/src/main/java/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.java b/spring-context/src/main/java/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.java
index 33d53bf922..97006cf4a5 100644
--- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.java
+++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 the original author or authors.
+ * Copyright 2002-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.
@@ -31,6 +31,7 @@ import javax.validation.ConstraintValidatorFactory;
import javax.validation.MessageInterpolator;
import javax.validation.TraversableResolver;
import javax.validation.Validation;
+import javax.validation.ValidationException;
import javax.validation.ValidationProviderResolver;
import javax.validation.Validator;
import javax.validation.ValidatorContext;
@@ -74,6 +75,7 @@ import org.springframework.util.ReflectionUtils;
* instead. If you really need programmatic {@code #forExecutables} access, inject this class as
* a {@link ValidatorFactory} and call {@link #getValidator()} on it, then {@code #forExecutables}
* on the returned native {@link Validator} reference instead of directly on this class.
+ * Alternatively, call {@code #unwrap(Validator.class) which will also provide the native object.
*
*
This class is also being used by Spring's MVC configuration namespace, in case of the
* {@code javax.validation} API being present but no explicit Validator having been configured.
@@ -397,6 +399,30 @@ public class LocalValidatorFactoryBean extends SpringValidatorAdapter
return this.validatorFactory.getConstraintValidatorFactory();
}
+ @Override
+ @SuppressWarnings("unchecked")
+ public T unwrap(Class type) {
+ if (type == null || !ValidatorFactory.class.isAssignableFrom(type)) {
+ try {
+ return super.unwrap(type);
+ }
+ catch (ValidationException ex) {
+ // ignore - we'll try ValidatorFactory unwrapping next
+ }
+ }
+ try {
+ return this.validatorFactory.unwrap(type);
+ }
+ catch (ValidationException ex) {
+ // ignore if just being asked for ValidatorFactory
+ if (ValidatorFactory.class == type) {
+ return (T) this.validatorFactory;
+ }
+ throw ex;
+ }
+ }
+
+
public void close() {
if (closeMethod != null && this.validatorFactory != null) {
ReflectionUtils.invokeMethod(closeMethod, this.validatorFactory);
diff --git a/spring-context/src/test/java/org/springframework/validation/beanvalidation/SpringValidatorAdapterTests.java b/spring-context/src/test/java/org/springframework/validation/beanvalidation/SpringValidatorAdapterTests.java
index de7bc833a1..307acc597e 100644
--- a/spring-context/src/test/java/org/springframework/validation/beanvalidation/SpringValidatorAdapterTests.java
+++ b/spring-context/src/test/java/org/springframework/validation/beanvalidation/SpringValidatorAdapterTests.java
@@ -27,6 +27,7 @@ import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;
import javax.validation.Validation;
+import javax.validation.Validator;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
@@ -51,8 +52,9 @@ import static org.junit.Assert.*;
*/
public class SpringValidatorAdapterTests {
- private final SpringValidatorAdapter validatorAdapter = new SpringValidatorAdapter(
- Validation.buildDefaultValidatorFactory().getValidator());
+ private final Validator nativeValidator = Validation.buildDefaultValidatorFactory().getValidator();
+
+ private final SpringValidatorAdapter validatorAdapter = new SpringValidatorAdapter(nativeValidator);
private final StaticMessageSource messageSource = new StaticMessageSource();
@@ -66,6 +68,12 @@ public class SpringValidatorAdapterTests {
}
+ @Test
+ public void testUnwrap() {
+ Validator nativeValidator = validatorAdapter.unwrap(Validator.class);
+ assertSame(this.nativeValidator, nativeValidator);
+ }
+
@Test // SPR-13406
public void testNoStringArgumentValue() {
TestBean testBean = new TestBean();
diff --git a/spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java b/spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java
index 26138af537..d5a4fb79c6 100644
--- a/spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java
+++ b/spring-context/src/test/java/org/springframework/validation/beanvalidation/ValidatorFactoryTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2014 the original author or authors.
+ * Copyright 2002-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.
@@ -33,11 +33,18 @@ import javax.validation.ConstraintValidatorContext;
import javax.validation.ConstraintViolation;
import javax.validation.Payload;
import javax.validation.Valid;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.HibernateValidator;
+import org.hibernate.validator.HibernateValidatorFactory;
import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.AnnotationConfigApplicationContext;
+import org.springframework.core.env.Environment;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
@@ -47,10 +54,9 @@ import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
/**
- * Tested against Hibernate Validator 4.3, as of Spring 4.0.
+ * Tests against Hibernate Validator 5.x.
*
* @author Juergen Hoeller
- * @since 3.0
*/
public class ValidatorFactoryTests {
@@ -58,6 +64,7 @@ public class ValidatorFactoryTests {
public void testSimpleValidation() throws Exception {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
+
ValidPerson person = new ValidPerson();
Set> result = validator.validate(person);
assertEquals(2, result.size());
@@ -70,6 +77,12 @@ public class ValidatorFactoryTests {
fail("Invalid constraint violation with path '" + path + "'");
}
}
+
+ Validator nativeValidator = validator.unwrap(Validator.class);
+ assertTrue(nativeValidator.getClass().getName().startsWith("org.hibernate"));
+ assertTrue(validator.unwrap(ValidatorFactory.class) instanceof HibernateValidatorFactory);
+ assertTrue(validator.unwrap(HibernateValidatorFactory.class) instanceof HibernateValidatorFactory);
+
validator.destroy();
}
@@ -78,6 +91,7 @@ public class ValidatorFactoryTests {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.setProviderClass(HibernateValidator.class);
validator.afterPropertiesSet();
+
ValidPerson person = new ValidPerson();
Set> result = validator.validate(person);
assertEquals(2, result.size());
@@ -90,6 +104,12 @@ public class ValidatorFactoryTests {
fail("Invalid constraint violation with path '" + path + "'");
}
}
+
+ Validator nativeValidator = validator.unwrap(Validator.class);
+ assertTrue(nativeValidator.getClass().getName().startsWith("org.hibernate"));
+ assertTrue(validator.unwrap(ValidatorFactory.class) instanceof HibernateValidatorFactory);
+ assertTrue(validator.unwrap(HibernateValidatorFactory.class) instanceof HibernateValidatorFactory);
+
validator.destroy();
}
@@ -112,6 +132,7 @@ public class ValidatorFactoryTests {
public void testSpringValidationFieldType() throws Exception {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
+
ValidPerson person = new ValidPerson();
person.setName("Phil");
person.getAddress().setStreet("Phil's Street");
@@ -126,6 +147,7 @@ public class ValidatorFactoryTests {
public void testSpringValidation() throws Exception {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
+
ValidPerson person = new ValidPerson();
BeanPropertyBindingResult result = new BeanPropertyBindingResult(person, "person");
validator.validate(person, result);
@@ -153,6 +175,7 @@ public class ValidatorFactoryTests {
public void testSpringValidationWithClassLevel() throws Exception {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
+
ValidPerson person = new ValidPerson();
person.setName("Juergen");
person.getAddress().setStreet("Juergen's Street");
@@ -166,10 +189,32 @@ public class ValidatorFactoryTests {
assertTrue(errorCodes.contains("NameAddressValid"));
}
+ @Test
+ public void testSpringValidationWithAutowiredValidator() throws Exception {
+ ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(
+ LocalValidatorFactoryBean.class);
+ LocalValidatorFactoryBean validator = ctx.getBean(LocalValidatorFactoryBean.class);
+
+ ValidPerson person = new ValidPerson();
+ person.expectsAutowiredValidator = true;
+ person.setName("Juergen");
+ person.getAddress().setStreet("Juergen's Street");
+ BeanPropertyBindingResult result = new BeanPropertyBindingResult(person, "person");
+ validator.validate(person, result);
+ assertEquals(1, result.getErrorCount());
+ ObjectError globalError = result.getGlobalError();
+ List errorCodes = Arrays.asList(globalError.getCodes());
+ assertEquals(2, errorCodes.size());
+ assertTrue(errorCodes.contains("NameAddressValid.person"));
+ assertTrue(errorCodes.contains("NameAddressValid"));
+ ctx.close();
+ }
+
@Test
public void testSpringValidationWithErrorInListElement() throws Exception {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
+
ValidPerson person = new ValidPerson();
person.getAddressList().add(new ValidAddress());
BeanPropertyBindingResult result = new BeanPropertyBindingResult(person, "person");
@@ -187,6 +232,7 @@ public class ValidatorFactoryTests {
public void testSpringValidationWithErrorInSetElement() throws Exception {
LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
validator.afterPropertiesSet();
+
ValidPerson person = new ValidPerson();
person.getAddressSet().add(new ValidAddress());
BeanPropertyBindingResult result = new BeanPropertyBindingResult(person, "person");
@@ -235,10 +281,12 @@ public class ValidatorFactoryTests {
private ValidAddress address = new ValidAddress();
@Valid
- private List addressList = new LinkedList();
+ private List addressList = new LinkedList<>();
@Valid
- private Set addressSet = new LinkedHashSet();
+ private Set addressSet = new LinkedHashSet<>();
+
+ public boolean expectsAutowiredValidator = false;
public String getName() {
return name;
@@ -304,12 +352,18 @@ public class ValidatorFactoryTests {
public static class NameAddressValidator implements ConstraintValidator {
+ @Autowired
+ private Environment environment;
+
@Override
public void initialize(NameAddressValid constraintAnnotation) {
}
@Override
public boolean isValid(ValidPerson value, ConstraintValidatorContext context) {
+ if (value.expectsAutowiredValidator) {
+ assertNotNull(this.environment);
+ }
boolean valid = (value.name == null || !value.address.street.contains(value.name));
if (!valid && "Phil".equals(value.name)) {
context.buildConstraintViolationWithTemplate(
@@ -378,7 +432,7 @@ public class ValidatorFactoryTests {
public boolean isValid(InnerBean bean, ConstraintValidatorContext context) {
context.disableDefaultConstraintViolation();
if (bean.getValue() == null) {
- context.buildConstraintViolationWithTemplate("NULL"). addNode("value").addConstraintViolation();
+ context.buildConstraintViolationWithTemplate("NULL").addNode("value").addConstraintViolation();
return false;
}
return true;
diff --git a/spring-orm-hibernate4/src/test/java/org/springframework/validation/hibernatevalidator5/ValidatorFactoryTests.java b/spring-orm-hibernate4/src/test/java/org/springframework/validation/hibernatevalidator5/ValidatorFactoryTests.java
index 4165836065..5258dd2165 100644
--- a/spring-orm-hibernate4/src/test/java/org/springframework/validation/hibernatevalidator5/ValidatorFactoryTests.java
+++ b/spring-orm-hibernate4/src/test/java/org/springframework/validation/hibernatevalidator5/ValidatorFactoryTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 the original author or authors.
+ * Copyright 2002-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.
@@ -33,9 +33,12 @@ import javax.validation.ConstraintValidatorContext;
import javax.validation.ConstraintViolation;
import javax.validation.Payload;
import javax.validation.Valid;
+import javax.validation.Validator;
+import javax.validation.ValidatorFactory;
import javax.validation.constraints.NotNull;
import org.hibernate.validator.HibernateValidator;
+import org.hibernate.validator.HibernateValidatorFactory;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
@@ -77,6 +80,12 @@ public class ValidatorFactoryTests {
fail("Invalid constraint violation with path '" + path + "'");
}
}
+
+ Validator nativeValidator = validator.unwrap(Validator.class);
+ assertTrue(nativeValidator.getClass().getName().startsWith("org.hibernate"));
+ assertTrue(validator.unwrap(ValidatorFactory.class) instanceof HibernateValidatorFactory);
+ assertTrue(validator.unwrap(HibernateValidatorFactory.class) instanceof HibernateValidatorFactory);
+
validator.destroy();
}
@@ -98,6 +107,12 @@ public class ValidatorFactoryTests {
fail("Invalid constraint violation with path '" + path + "'");
}
}
+
+ Validator nativeValidator = validator.unwrap(Validator.class);
+ assertTrue(nativeValidator.getClass().getName().startsWith("org.hibernate"));
+ assertTrue(validator.unwrap(ValidatorFactory.class) instanceof HibernateValidatorFactory);
+ assertTrue(validator.unwrap(HibernateValidatorFactory.class) instanceof HibernateValidatorFactory);
+
validator.destroy();
}
@@ -269,10 +284,10 @@ public class ValidatorFactoryTests {
private ValidAddress address = new ValidAddress();
@Valid
- private List addressList = new LinkedList();
+ private List addressList = new LinkedList<>();
@Valid
- private Set addressSet = new LinkedHashSet();
+ private Set addressSet = new LinkedHashSet<>();
public boolean expectsAutowiredValidator = false;