Commit 3db5c70b authored by Stephane Nicoll's avatar Stephane Nicoll

Make sure binder properly resolve resources

This commit makes sure that `@ConfigurationProperties` binding resolves
resources properly. In particular, any `ProtocolResolver` registered on
the `ApplicationContext` is now honoured.

Closes gh-11569
parent 712e5625
/* /*
* Copyright 2012-2017 the original author or authors. * Copyright 2012-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -31,6 +31,9 @@ import org.springframework.beans.BeanUtils; ...@@ -31,6 +31,9 @@ import org.springframework.beans.BeanUtils;
import org.springframework.beans.PropertyValues; import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.support.ResourceEditorRegistrar;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.MessageSource; import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware; import org.springframework.context.MessageSourceAware;
import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.ConversionService;
...@@ -52,7 +55,7 @@ import org.springframework.validation.Validator; ...@@ -52,7 +55,7 @@ import org.springframework.validation.Validator;
* @author Dave Syer * @author Dave Syer
*/ */
public class PropertiesConfigurationFactory<T> public class PropertiesConfigurationFactory<T>
implements FactoryBean<T>, MessageSourceAware, InitializingBean { implements FactoryBean<T>, ApplicationContextAware, MessageSourceAware, InitializingBean {
private static final char[] EXACT_DELIMITERS = { '_', '.', '[' }; private static final char[] EXACT_DELIMITERS = { '_', '.', '[' };
...@@ -73,6 +76,8 @@ public class PropertiesConfigurationFactory<T> ...@@ -73,6 +76,8 @@ public class PropertiesConfigurationFactory<T>
private Validator validator; private Validator validator;
private ApplicationContext applicationContext;
private MessageSource messageSource; private MessageSource messageSource;
private boolean hasBeenBound = false; private boolean hasBeenBound = false;
...@@ -149,6 +154,11 @@ public class PropertiesConfigurationFactory<T> ...@@ -149,6 +154,11 @@ public class PropertiesConfigurationFactory<T>
this.targetName = targetName; this.targetName = targetName;
} }
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/** /**
* Set the message source. * Set the message source.
* @param messageSource the message source * @param messageSource the message source
...@@ -265,6 +275,11 @@ public class PropertiesConfigurationFactory<T> ...@@ -265,6 +275,11 @@ public class PropertiesConfigurationFactory<T>
dataBinder.setIgnoreInvalidFields(this.ignoreInvalidFields); dataBinder.setIgnoreInvalidFields(this.ignoreInvalidFields);
dataBinder.setIgnoreUnknownFields(this.ignoreUnknownFields); dataBinder.setIgnoreUnknownFields(this.ignoreUnknownFields);
customizeBinder(dataBinder); customizeBinder(dataBinder);
if (this.applicationContext != null) {
ResourceEditorRegistrar resourceEditorRegistrar = new ResourceEditorRegistrar(
this.applicationContext, this.applicationContext.getEnvironment());
resourceEditorRegistrar.registerCustomEditors(dataBinder);
}
Iterable<String> relaxedTargetNames = getRelaxedTargetNames(); Iterable<String> relaxedTargetNames = getRelaxedTargetNames();
Set<String> names = getNames(relaxedTargetNames); Set<String> names = getNames(relaxedTargetNames);
PropertyValues propertyValues = getPropertySourcesPropertyValues(names, PropertyValues propertyValues = getPropertySourcesPropertyValues(names,
......
/* /*
* Copyright 2012-2017 the original author or authors. * Copyright 2012-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -312,6 +312,7 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc ...@@ -312,6 +312,7 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>( PropertiesConfigurationFactory<Object> factory = new PropertiesConfigurationFactory<Object>(
target); target);
factory.setPropertySources(this.propertySources); factory.setPropertySources(this.propertySources);
factory.setApplicationContext(this.applicationContext);
factory.setValidator(determineValidator(bean)); factory.setValidator(determineValidator(bean));
// If no explicit conversion service is provided we add one so that (at least) // If no explicit conversion service is provided we add one so that (at least)
// comma-separated arrays of convertibles can be bound automatically // comma-separated arrays of convertibles can be bound automatically
......
/* /*
* Copyright 2012-2017 the original author or authors. * Copyright 2012-2018 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
...@@ -40,6 +40,10 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext ...@@ -40,6 +40,10 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.ProtocolResolver;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.mock.env.MockEnvironment; import org.springframework.mock.env.MockEnvironment;
import org.springframework.test.context.support.TestPropertySourceUtils; import org.springframework.test.context.support.TestPropertySourceUtils;
import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.util.ReflectionTestUtils;
...@@ -52,6 +56,12 @@ import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; ...@@ -52,6 +56,12 @@ import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.mockito.BDDMockito.given;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
/** /**
* Tests for {@link ConfigurationPropertiesBindingPostProcessor}. * Tests for {@link ConfigurationPropertiesBindingPostProcessor}.
...@@ -366,6 +376,38 @@ public class ConfigurationPropertiesBindingPostProcessorTests { ...@@ -366,6 +376,38 @@ public class ConfigurationPropertiesBindingPostProcessorTests {
} }
} }
@Test
public void customProtocolResolverIsInvoked() {
this.context = new AnnotationConfigApplicationContext();
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context,
"test.resource=application.properties");
ProtocolResolver protocolResolver = mock(ProtocolResolver.class);
given(protocolResolver.resolve(anyString(), any(ResourceLoader.class)))
.willReturn(null);
this.context.addProtocolResolver(protocolResolver);
this.context.register(PropertiesWithResource.class);
this.context.refresh();
verify(protocolResolver).resolve(eq("application.properties"),
any(ResourceLoader.class));
}
@Test
public void customProtocolResolver() {
this.context = new AnnotationConfigApplicationContext();
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(this.context,
"test.resource=test:/application.properties");
this.context.addProtocolResolver(new TestProtocolResolver());
this.context.register(PropertiesWithResource.class);
this.context.refresh();
Resource resource = this.context.getBean(PropertiesWithResource.class)
.getResource();
assertThat(resource).isNotNull();
assertThat(resource).isInstanceOf(ClassPathResource.class);
assertThat(resource.exists()).isTrue();
assertThat(((ClassPathResource) resource).getPath())
.isEqualTo("application.properties");
}
@Configuration @Configuration
@EnableConfigurationProperties @EnableConfigurationProperties
public static class TestConfigurationWithValidatingSetter { public static class TestConfigurationWithValidatingSetter {
...@@ -819,4 +861,35 @@ public class ConfigurationPropertiesBindingPostProcessorTests { ...@@ -819,4 +861,35 @@ public class ConfigurationPropertiesBindingPostProcessorTests {
} }
@Configuration
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "test")
public static class PropertiesWithResource {
private Resource resource;
public Resource getResource() {
return this.resource;
}
public void setResource(Resource resource) {
this.resource = resource;
}
}
private static class TestProtocolResolver implements ProtocolResolver {
public static final String PREFIX = "test:/";
@Override
public Resource resolve(String location, ResourceLoader resourceLoader) {
if (location.startsWith(PREFIX)) {
String path = location.substring(PREFIX.length(), location.length());
return new ClassPathResource(path);
}
return null;
}
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment