diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/validation/ValidatorAdapter.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/validation/ValidatorAdapter.java index ed84cc2ecea..da1c3cf73b3 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/validation/ValidatorAdapter.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/validation/ValidatorAdapter.java @@ -16,6 +16,8 @@ package org.springframework.boot.autoconfigure.validation; +import javax.validation.ValidationException; + import org.springframework.beans.BeansException; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.InitializingBean; @@ -135,7 +137,13 @@ public class ValidatorAdapter implements SmartValidator, ApplicationContextAware private static Validator create() { OptionalValidatorFactoryBean validator = new OptionalValidatorFactoryBean(); - validator.setMessageInterpolator(new MessageInterpolatorFactory().getObject()); + try { + validator + .setMessageInterpolator(new MessageInterpolatorFactory().getObject()); + } + catch (ValidationException ex) { + // Ignore + } return wrap(validator, false); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/validation/ValidatorAdapterTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/validation/ValidatorAdapterTests.java index f95d42b5939..5674b8b8798 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/validation/ValidatorAdapterTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/validation/ValidatorAdapterTests.java @@ -20,13 +20,14 @@ import java.util.HashMap; import javax.validation.constraints.Min; -import org.junit.After; import org.junit.Test; +import org.springframework.boot.test.context.FilteredClassLoader; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; 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.core.io.ClassPathResource; import org.springframework.validation.MapBindingResult; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; @@ -41,60 +42,65 @@ import static org.mockito.Mockito.verify; * Tests for {@link ValidatorAdapter}. * * @author Stephane Nicoll + * @author Madhura Bhave */ public class ValidatorAdapterTests { - private AnnotationConfigApplicationContext context; - - @After - public void close() { - if (this.context != null) { - this.context.close(); - } - } + private ApplicationContextRunner contextRunner = new ApplicationContextRunner(); @Test public void wrapLocalValidatorFactoryBean() { - ValidatorAdapter wrapper = load(LocalValidatorFactoryBeanConfig.class); - assertThat(wrapper.supports(SampleData.class)).isTrue(); - MapBindingResult errors = new MapBindingResult(new HashMap(), - "test"); - wrapper.validate(new SampleData(40), errors); - assertThat(errors.getErrorCount()).isEqualTo(1); + this.contextRunner.withUserConfiguration(LocalValidatorFactoryBeanConfig.class) + .run((context) -> { + ValidatorAdapter wrapper = context.getBean(ValidatorAdapter.class); + assertThat(wrapper.supports(SampleData.class)).isTrue(); + MapBindingResult errors = new MapBindingResult( + new HashMap(), "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, never()).destroy(); - this.context.close(); - this.context = null; - verify(validator, times(1)).destroy(); + this.contextRunner.withUserConfiguration(NonManagedBeanConfig.class) + .run((context) -> { + LocalValidatorFactoryBean validator = context + .getBean(NonManagedBeanConfig.class).validator; + verify(validator, times(1)) + .setApplicationContext(any(ApplicationContext.class)); + verify(validator, times(1)).afterPropertiesSet(); + verify(validator, never()).destroy(); + context.close(); + verify(validator, times(1)).destroy(); + }); } @Test public void wrapperDoesNotInvokeCallbackOnManagedBean() { - load(ManagedBeanConfig.class); - LocalValidatorFactoryBean validator = this.context - .getBean(ManagedBeanConfig.class).validator; - verify(validator, never()).setApplicationContext(any(ApplicationContext.class)); - verify(validator, never()).afterPropertiesSet(); - verify(validator, never()).destroy(); - this.context.close(); - this.context = null; - verify(validator, never()).destroy(); + this.contextRunner.withUserConfiguration(ManagedBeanConfig.class) + .run((context) -> { + LocalValidatorFactoryBean validator = context + .getBean(ManagedBeanConfig.class).validator; + verify(validator, never()) + .setApplicationContext(any(ApplicationContext.class)); + verify(validator, never()).afterPropertiesSet(); + verify(validator, never()).destroy(); + context.close(); + verify(validator, never()).destroy(); + }); } - private ValidatorAdapter load(Class config) { - AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); - ctx.register(config); - ctx.refresh(); - this.context = ctx; - return this.context.getBean(ValidatorAdapter.class); + @Test + public void wrapperWhenValidationProviderNotPresentShouldNotThrowException() { + ClassPathResource hibernateValidator = new ClassPathResource( + "META-INF/services/javax.validation.spi.ValidationProvider"); + this.contextRunner + .withClassLoader(new FilteredClassLoader( + FilteredClassLoader.ClassPathResourceFilter + .of(hibernateValidator), + FilteredClassLoader.PackageFilter.of("org.hibernate.validator"))) + .run((context) -> ValidatorAdapter.get(context, null)); } @Configuration(proxyBeanMethods = false) diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/FilteredClassLoader.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/FilteredClassLoader.java index e6f77ce3f8c..efbd146c5f5 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/FilteredClassLoader.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/FilteredClassLoader.java @@ -16,11 +16,14 @@ package org.springframework.boot.test.context; +import java.io.IOException; +import java.io.InputStream; import java.net.URL; import java.net.URLClassLoader; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Enumeration; import java.util.function.Predicate; import org.springframework.core.io.ClassPathResource; @@ -109,6 +112,26 @@ public class FilteredClassLoader extends URLClassLoader { return super.getResource(name); } + @Override + public Enumeration getResources(String name) throws IOException { + for (Predicate filter : this.resourcesFilters) { + if (filter.test(name)) { + return Collections.emptyEnumeration(); + } + } + return super.getResources(name); + } + + @Override + public InputStream getResourceAsStream(String name) { + for (Predicate filter : this.resourcesFilters) { + if (filter.test(name)) { + return null; + } + } + return super.getResourceAsStream(name); + } + /** * Filter to restrict the classes that can be loaded. */ diff --git a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/FilteredClassLoaderTests.java b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/FilteredClassLoaderTests.java index 6ebf612fe63..99367fea71b 100644 --- a/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/FilteredClassLoaderTests.java +++ b/spring-boot-project/spring-boot-test/src/test/java/org/springframework/boot/test/context/FilteredClassLoaderTests.java @@ -16,7 +16,9 @@ package org.springframework.boot.test.context; +import java.io.InputStream; import java.net.URL; +import java.util.Enumeration; import org.junit.Test; @@ -81,4 +83,44 @@ public class FilteredClassLoaderTests { } } + @Test + public void loadResourcesWhenFilteredOnResourceShouldReturnNotFound() + throws Exception { + try (FilteredClassLoader classLoader = new FilteredClassLoader(TEST_RESOURCE)) { + final Enumeration loaded = classLoader + .getResources(TEST_RESOURCE.getPath()); + assertThat(loaded.hasMoreElements()).isFalse(); + } + } + + @Test + public void loadResourcesWhenNotFilteredShouldLoadResource() throws Exception { + try (FilteredClassLoader classLoader = new FilteredClassLoader( + (resourceName) -> false)) { + final Enumeration loaded = classLoader + .getResources(TEST_RESOURCE.getPath()); + assertThat(loaded.hasMoreElements()).isTrue(); + } + } + + @Test + public void loadResourceAsStreamWhenFilteredOnResourceShouldReturnNotFound() + throws Exception { + try (FilteredClassLoader classLoader = new FilteredClassLoader(TEST_RESOURCE)) { + final InputStream loaded = classLoader + .getResourceAsStream(TEST_RESOURCE.getPath()); + assertThat(loaded).isNull(); + } + } + + @Test + public void loadResourceAsStreamWhenNotFilteredShouldLoadResource() throws Exception { + try (FilteredClassLoader classLoader = new FilteredClassLoader( + (resourceName) -> false)) { + final InputStream loaded = classLoader + .getResourceAsStream(TEST_RESOURCE.getPath()); + assertThat(loaded).isNotNull(); + } + } + }