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;