diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/bind/PropertiesConfigurationFactory.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/bind/PropertiesConfigurationFactory.java index f477cc6312d..57ef3ec5eed 100644 --- a/spring-bootstrap/src/main/java/org/springframework/bootstrap/bind/PropertiesConfigurationFactory.java +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/bind/PropertiesConfigurationFactory.java @@ -38,8 +38,9 @@ import org.springframework.validation.ObjectError; import org.springframework.validation.Validator; /** - * Validate some {@link Properties} by binding them to an object of a specified type and - * then optionally running a {@link Validator} over it. + * Validate some {@link Properties} (or optionally {@link PropertySources}) by binding + * them to an object of a specified type and then optionally running a {@link Validator} + * over it. * * @author Dave Syer */ @@ -195,15 +196,13 @@ public class PropertiesConfigurationFactory implements FactoryBean, if (this.logger.isTraceEnabled()) { if (this.properties != null) { this.logger.trace("Properties:\n" + this.properties); - } - else { + } else { this.logger.trace("Property Sources: " + this.propertySources); } } this.hasBeenBound = true; doBindPropertiesToTarget(); - } - catch (BindException ex) { + } catch (BindException ex) { if (this.exceptionIfInvalid) { throw ex; } diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/DefaultProfileDocumentMatcher.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/DefaultProfileDocumentMatcher.java new file mode 100644 index 00000000000..3680190f13b --- /dev/null +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/DefaultProfileDocumentMatcher.java @@ -0,0 +1,40 @@ +/* + * Copyright 2012-2013 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.bootstrap.config; + +import java.util.Properties; + +import org.springframework.bootstrap.config.YamlProcessor.DocumentMatcher; +import org.springframework.bootstrap.config.YamlProcessor.MatchStatus; + +/** + * A {@link DocumentMatcher} that matches the default profile implicitly but not + * explicitly (i.e. matches if "spring.profiles" is not found and not otherwise). + * + * @author Dave Syer + * + */ +public final class DefaultProfileDocumentMatcher implements DocumentMatcher { + + @Override + public MatchStatus matches(Properties properties) { + if (!properties.containsKey("spring.profiles")) { + return MatchStatus.FOUND; + } else { + return MatchStatus.NOT_FOUND; + } + } +} \ No newline at end of file diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/PropertiesPropertySourceLoader.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/PropertiesPropertySourceLoader.java new file mode 100644 index 00000000000..3368b66a6ca --- /dev/null +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/PropertiesPropertySourceLoader.java @@ -0,0 +1,52 @@ +/* + * Copyright 2012-2013 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.bootstrap.config; + +import java.io.IOException; +import java.util.Properties; + +import org.springframework.core.env.Environment; +import org.springframework.core.env.PropertiesPropertySource; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PropertiesLoaderUtils; + +/** + * Strategy to load '.properties' files into a {@link PropertySource}. + */ +public class PropertiesPropertySourceLoader implements PropertySourceLoader { + + @Override + public boolean supports(Resource resource) { + return resource.getFilename().endsWith(".properties"); + } + + @Override + public PropertySource load(Resource resource, Environment environment) { + try { + Properties properties = loadProperties(resource, environment); + return new PropertiesPropertySource(resource.getDescription(), properties); + } catch (IOException ex) { + throw new IllegalStateException("Could not load properties from " + resource, + ex); + } + } + + protected Properties loadProperties(Resource resource, Environment environment) + throws IOException { + return PropertiesLoaderUtils.loadProperties(resource); + } +} \ No newline at end of file diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/PropertySourceLoader.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/PropertySourceLoader.java new file mode 100644 index 00000000000..8f6f1545724 --- /dev/null +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/PropertySourceLoader.java @@ -0,0 +1,38 @@ +/* + * Copyright 2012-2013 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.bootstrap.config; + +import org.springframework.core.env.Environment; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.Resource; + +/** + * Strategy interface used to load a {@link PropertySource}. + */ +public interface PropertySourceLoader { + + /** + * @return Is this resource supported? + */ + public boolean supports(Resource resource); + + /** + * Load the resource into a property source. + * @return a property source + */ + PropertySource load(Resource resource, Environment environment); + +} \ No newline at end of file diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/SpringProfileDocumentMatcher.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/SpringProfileDocumentMatcher.java new file mode 100644 index 00000000000..118951dd441 --- /dev/null +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/SpringProfileDocumentMatcher.java @@ -0,0 +1,49 @@ +/* + * Copyright 2012-2013 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.bootstrap.config; + +import java.util.Properties; + +import org.springframework.bootstrap.config.YamlProcessor.ArrayDocumentMatcher; +import org.springframework.bootstrap.config.YamlProcessor.DocumentMatcher; +import org.springframework.bootstrap.config.YamlProcessor.MatchStatus; +import org.springframework.core.env.Environment; + +/** + * @author Dave Syer + * + */ +public class SpringProfileDocumentMatcher implements DocumentMatcher { + + private final Environment environment; + + /** + * @param environment + */ + public SpringProfileDocumentMatcher(Environment environment) { + this.environment = environment; + } + + @Override + public MatchStatus matches(Properties properties) { + String[] profiles = this.environment.getActiveProfiles(); + if (profiles.length == 0) { + profiles = new String[] { "default" }; + } + return new ArrayDocumentMatcher("spring.profiles", profiles).matches(properties); + } + +} \ No newline at end of file diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/YamlPropertySourceLoader.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/YamlPropertySourceLoader.java new file mode 100644 index 00000000000..ad7012bfa53 --- /dev/null +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/config/YamlPropertySourceLoader.java @@ -0,0 +1,81 @@ +/* + * Copyright 2012-2013 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.bootstrap.config; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; + +import org.springframework.bootstrap.config.YamlProcessor.DocumentMatcher; +import org.springframework.core.env.Environment; +import org.springframework.core.env.PropertySource; +import org.springframework.core.io.Resource; + +/** + * Strategy to load '.yml' files into a {@link PropertySource}. + */ +public class YamlPropertySourceLoader extends PropertiesPropertySourceLoader { + + private List matchers; + + /** + * A property source loader that loads all properties and matches all documents. + * + * @return a property source loader + */ + public static YamlPropertySourceLoader matchAllLoader() { + return new YamlPropertySourceLoader(); + } + + /** + * A property source loader that matches documents that have no explicit profile or + * which have an explicit "spring.profiles.active" value in the current active + * profiles. + * + * @return a property source loader + */ + public static YamlPropertySourceLoader springProfileAwareLoader( + Environment environment) { + return new YamlPropertySourceLoader(new SpringProfileDocumentMatcher(environment), + new DefaultProfileDocumentMatcher()); + } + + /** + * @param matchers + */ + public YamlPropertySourceLoader(DocumentMatcher... matchers) { + this.matchers = Arrays.asList(matchers); + } + + @Override + public boolean supports(Resource resource) { + return resource.getFilename().endsWith(".yml"); + } + + @Override + protected Properties loadProperties(final Resource resource, + final Environment environment) throws IOException { + YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); + if (this.matchers != null && !this.matchers.isEmpty()) { + factory.setMatchDefault(false); + factory.setDocumentMatchers(this.matchers); + } + factory.setResources(new Resource[] { resource }); + return factory.getObject(); + } + +} \ No newline at end of file diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/initializer/ConfigFileApplicationContextInitializer.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/initializer/ConfigFileApplicationContextInitializer.java index 5133c086fe3..d01363ce437 100644 --- a/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/initializer/ConfigFileApplicationContextInitializer.java +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/initializer/ConfigFileApplicationContextInitializer.java @@ -16,26 +16,21 @@ package org.springframework.bootstrap.context.initializer; -import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import java.util.Properties; -import java.util.Set; -import org.springframework.bootstrap.config.YamlPropertiesFactoryBean; -import org.springframework.bootstrap.config.YamlProcessor.ArrayDocumentMatcher; -import org.springframework.bootstrap.config.YamlProcessor.DocumentMatcher; -import org.springframework.bootstrap.config.YamlProcessor.MatchStatus; +import org.springframework.bootstrap.config.SpringProfileDocumentMatcher; +import org.springframework.bootstrap.config.PropertiesPropertySourceLoader; +import org.springframework.bootstrap.config.PropertySourceLoader; +import org.springframework.bootstrap.config.YamlPropertySourceLoader; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.context.annotation.PropertySource; import org.springframework.core.Ordered; import org.springframework.core.env.CommandLinePropertySource; +import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MutablePropertySources; -import org.springframework.core.env.PropertiesPropertySource; +import org.springframework.core.env.PropertySource; import org.springframework.core.io.Resource; -import org.springframework.core.io.support.PropertiesLoaderUtils; import org.springframework.util.StringUtils; /** @@ -69,8 +64,6 @@ import org.springframework.util.StringUtils; public class ConfigFileApplicationContextInitializer implements ApplicationContextInitializer, Ordered { - private static final Loader[] LOADERS = { new PropertiesLoader(), new YamlLoader() }; - private static final String LOCATION_VARIABLE = "${spring.config.location}"; private String[] searchLocations = new String[] { "classpath:", "file:./", @@ -100,11 +93,9 @@ public class ConfigFileApplicationContextInitializer implements private List getCandidateLocations() { List candidates = new ArrayList(); for (String searchLocation : this.searchLocations) { - for (Loader loader : LOADERS) { - for (String extension : loader.getExtensions()) { - String location = searchLocation + this.name + extension; - candidates.add(location); - } + for (String extension : new String[] { ".properties", ".yml" }) { + String location = searchLocation + this.name + extension; + candidates.add(location); } } candidates.add(LOCATION_VARIABLE); @@ -113,16 +104,30 @@ public class ConfigFileApplicationContextInitializer implements private void load(ConfigurableApplicationContext applicationContext, String location, String profile) { - location = applicationContext.getEnvironment().resolvePlaceholders(location); + + ConfigurableEnvironment environment = applicationContext.getEnvironment(); + location = environment.resolvePlaceholders(location); String suffix = "." + StringUtils.getFilenameExtension(location); if (StringUtils.hasLength(profile)) { location = location.replace(suffix, "-" + profile + suffix); } - for (Loader loader : LOADERS) { - if (loader.getExtensions().contains(suffix.toLowerCase())) { - Resource resource = applicationContext.getResource(location); - if (resource != null && resource.exists()) { - loader.load(resource, applicationContext); + PropertySourceLoader[] loaders = { + new PropertiesPropertySourceLoader(), + new YamlPropertySourceLoader(new SpringProfileDocumentMatcher(environment), + new ProfileSettingDocumentMatcher(environment)) }; + for (PropertySourceLoader loader : loaders) { + Resource resource = applicationContext.getResource(location); + if (resource != null && resource.exists() && loader.supports(resource)) { + PropertySource propertySource = loader.load(resource, environment); + MutablePropertySources propertySources = environment.getPropertySources(); + if (propertySources + .contains(CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME)) { + propertySources.addAfter( + CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME, + propertySource); + + } else { + propertySources.addFirst(propertySource); } return; } @@ -152,118 +157,4 @@ public class ConfigFileApplicationContextInitializer implements this.searchLocations = (searchLocations == null ? null : searchLocations.clone()); } - /** - * Strategy interface used to load a {@link PropertySource}. - */ - private static interface Loader { - - /** - * @return The supported extensions (including '.' and in lowercase) - */ - public Set getExtensions(); - - /** - * Load the resource into the destination application context. - */ - void load(Resource resource, ConfigurableApplicationContext applicationContext); - - } - - /** - * Strategy to load '.properties' files. - */ - private static class PropertiesLoader implements Loader { - - @Override - public Set getExtensions() { - return Collections.singleton(".properties"); - } - - @Override - public void load(Resource resource, - ConfigurableApplicationContext applicationContext) { - try { - Properties properties = loadProperties(resource, applicationContext); - MutablePropertySources propertySources = applicationContext - .getEnvironment().getPropertySources(); - if (propertySources - .contains(CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME)) { - propertySources.addAfter( - CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME, - new PropertiesPropertySource(resource.getDescription(), - properties)); - - } - else { - propertySources.addFirst(new PropertiesPropertySource(resource - .getDescription(), properties)); - } - } - catch (IOException ex) { - throw new IllegalStateException("Could not load properties file from " - + resource, ex); - } - } - - protected Properties loadProperties(Resource resource, - ConfigurableApplicationContext applicationContext) throws IOException { - return PropertiesLoaderUtils.loadProperties(resource); - } - } - - /** - * Strategy to load '.yml' files. - */ - private static class YamlLoader extends PropertiesLoader { - - @Override - public Set getExtensions() { - return Collections.singleton(".yml"); - } - - @Override - protected Properties loadProperties(final Resource resource, - final ConfigurableApplicationContext applicationContext) - throws IOException { - YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean(); - List matchers = new ArrayList(); - matchers.add(new DocumentMatcher() { - @Override - public MatchStatus matches(Properties properties) { - String[] profiles = applicationContext.getEnvironment() - .getActiveProfiles(); - if (profiles.length == 0) { - profiles = new String[] { "default" }; - } - return new ArrayDocumentMatcher("spring.profiles", profiles) - .matches(properties); - - } - }); - matchers.add(new DocumentMatcher() { - @Override - public MatchStatus matches(Properties properties) { - if (!properties.containsKey("spring.profiles")) { - Set profiles = StringUtils - .commaDelimitedListToSet(properties.getProperty( - "spring.profiles.active", "")); - for (String profile : profiles) { - // allow document with no profile to set the active one - applicationContext.getEnvironment().addActiveProfile(profile); - } - // matches default profile - return MatchStatus.FOUND; - } - else { - return MatchStatus.NOT_FOUND; - } - } - }); - factory.setMatchDefault(false); - factory.setDocumentMatchers(matchers); - factory.setResources(new Resource[] { resource }); - return factory.getObject(); - } - } - } diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/initializer/ProfileSettingDocumentMatcher.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/initializer/ProfileSettingDocumentMatcher.java new file mode 100644 index 00000000000..ce35ce38629 --- /dev/null +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/initializer/ProfileSettingDocumentMatcher.java @@ -0,0 +1,60 @@ +/* + * Copyright 2012-2013 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.bootstrap.context.initializer; + +import java.util.Properties; +import java.util.Set; + +import org.springframework.bootstrap.config.YamlProcessor.DocumentMatcher; +import org.springframework.bootstrap.config.YamlProcessor.MatchStatus; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.Environment; +import org.springframework.util.StringUtils; + +/** + * A {@link DocumentMatcher} that sets the active profile if it finds a document with + * a key spring.profiles.active. + * + * @author Dave Syer + * + */ +public final class ProfileSettingDocumentMatcher implements DocumentMatcher { + + private final Environment environment; + + public ProfileSettingDocumentMatcher(Environment environment) { + this.environment = environment; + } + + @Override + public MatchStatus matches(Properties properties) { + if (!properties.containsKey("spring.profiles")) { + Set profiles = StringUtils.commaDelimitedListToSet(properties + .getProperty("spring.profiles.active", "")); + if (this.environment instanceof ConfigurableEnvironment) { + ConfigurableEnvironment configurable = (ConfigurableEnvironment) this.environment; + for (String profile : profiles) { + // allow document with no profile to set the active one + configurable.addActiveProfile(profile); + } + } + // matches default profile + return MatchStatus.FOUND; + } else { + return MatchStatus.NOT_FOUND; + } + } +} \ No newline at end of file diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/properties/ConfigurationProperties.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/properties/ConfigurationProperties.java index 936a79b91e5..bc145bf2bcc 100644 --- a/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/properties/ConfigurationProperties.java +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/properties/ConfigurationProperties.java @@ -70,4 +70,12 @@ public @interface ConfigurationProperties { */ boolean ignoreUnknownFields() default true; + /** + * Optionally provide an explicit resource path to bind to instead of using the + * default environment. + * + * @return the path (or paths) of resources to bind to + */ + String[] path() default {}; + } diff --git a/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/properties/ConfigurationPropertiesBindingPostProcessor.java b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/properties/ConfigurationPropertiesBindingPostProcessor.java index 6494d83424a..acd4b76a794 100644 --- a/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/properties/ConfigurationPropertiesBindingPostProcessor.java +++ b/spring-bootstrap/src/main/java/org/springframework/bootstrap/context/properties/ConfigurationPropertiesBindingPostProcessor.java @@ -23,11 +23,23 @@ import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.bootstrap.bind.PropertiesConfigurationFactory; +import org.springframework.bootstrap.config.PropertiesPropertySourceLoader; +import org.springframework.bootstrap.config.PropertySourceLoader; +import org.springframework.bootstrap.config.YamlPropertySourceLoader; +import org.springframework.context.EnvironmentAware; +import org.springframework.context.ResourceLoaderAware; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.support.DefaultConversionService; +import org.springframework.core.env.Environment; +import org.springframework.core.env.MutablePropertySources; +import org.springframework.core.env.PropertySource; import org.springframework.core.env.PropertySources; +import org.springframework.core.env.StandardEnvironment; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; import org.springframework.util.StringUtils; import org.springframework.validation.Validator; @@ -38,7 +50,7 @@ import org.springframework.validation.Validator; * @author Dave Syer */ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProcessor, - BeanFactoryAware { + BeanFactoryAware, ResourceLoaderAware, EnvironmentAware { private PropertySources propertySources; @@ -52,6 +64,10 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc private boolean initialized = false; + private ResourceLoader resourceLoader = new DefaultResourceLoader(); + + private Environment environment = new StandardEnvironment(); + /** * @param propertySources */ @@ -78,6 +94,16 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc this.beanFactory = beanFactory; } + @Override + public void setResourceLoader(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } + + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { @@ -101,7 +127,12 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc .getTarget() : bean); PropertiesConfigurationFactory factory = new PropertiesConfigurationFactory( target); - factory.setPropertySources(this.propertySources); + if (annotation != null && annotation.path().length != 0) { + + factory.setPropertySources(loadPropertySources(annotation.path())); + } else { + factory.setPropertySources(this.propertySources); + } factory.setValidator(this.validator); // If no explicit conversion service is provided we add one so that (at least) // comma-separated arrays of convertibles can be bound automatically @@ -118,12 +149,31 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc } try { factory.bindPropertiesToTarget(); - } - catch (Exception ex) { + } catch (Exception ex) { throw new BeanCreationException(beanName, "Could not bind properties", ex); } } + private PropertySources loadPropertySources(String[] path) { + MutablePropertySources propertySources = new MutablePropertySources(); + PropertySourceLoader[] loaders = { new PropertiesPropertySourceLoader(), + YamlPropertySourceLoader.springProfileAwareLoader(this.environment) }; + for (String location : path) { + location = this.environment.resolvePlaceholders(location); + Resource resource = this.resourceLoader.getResource(location); + if (resource != null && resource.exists()) { + for (PropertySourceLoader loader : loaders) { + if (loader.supports(resource)) { + PropertySource propertySource = loader.load(resource, + this.environment); + propertySources.addFirst(propertySource); + } + } + } + } + return propertySources; + } + private ConversionService getDefaultConversionService() { if (!this.initialized && this.beanFactory instanceof ListableBeanFactory) { for (Converter converter : ((ListableBeanFactory) this.beanFactory) diff --git a/spring-bootstrap/src/test/java/org/springframework/bootstrap/context/properties/EnableConfigurationPropertiesTests.java b/spring-bootstrap/src/test/java/org/springframework/bootstrap/context/properties/EnableConfigurationPropertiesTests.java index 49f29e10bee..b53a0e52a2f 100644 --- a/spring-bootstrap/src/test/java/org/springframework/bootstrap/context/properties/EnableConfigurationPropertiesTests.java +++ b/spring-bootstrap/src/test/java/org/springframework/bootstrap/context/properties/EnableConfigurationPropertiesTests.java @@ -120,6 +120,49 @@ public class EnableConfigurationPropertiesTests { assertEquals("bar", this.context.getBean(TestProperties.class).getName()); } + @Test + public void testBindingDirectlyToFile() { + this.context.register(ResourceBindingProperties.class, TestConfiguration.class); + this.context.refresh(); + assertEquals(1, + this.context.getBeanNamesForType(ResourceBindingProperties.class).length); + assertEquals("foo", this.context.getBean(ResourceBindingProperties.class) + .getName()); + } + + @Test + public void testBindingDirectlyToFileResolvedFromEnvironment() { + TestUtils.addEnviroment(this.context, "binding.location:classpath:other.yml"); + this.context.register(ResourceBindingProperties.class, TestConfiguration.class); + this.context.refresh(); + assertEquals(1, + this.context.getBeanNamesForType(ResourceBindingProperties.class).length); + assertEquals("other", this.context.getBean(ResourceBindingProperties.class) + .getName()); + } + + @Test + public void testBindingDirectlyToFileWithDefaultsWhenProfileNotFound() { + this.context.register(ResourceBindingProperties.class, TestConfiguration.class); + this.context.getEnvironment().addActiveProfile("nonexistent"); + this.context.refresh(); + assertEquals(1, + this.context.getBeanNamesForType(ResourceBindingProperties.class).length); + assertEquals("foo", this.context.getBean(ResourceBindingProperties.class) + .getName()); + } + + @Test + public void testBindingDirectlyToFileWithExplicitSpringProfile() { + this.context.register(ResourceBindingProperties.class, TestConfiguration.class); + this.context.getEnvironment().addActiveProfile("super"); + this.context.refresh(); + assertEquals(1, + this.context.getBeanNamesForType(ResourceBindingProperties.class).length); + assertEquals("bar", this.context.getBean(ResourceBindingProperties.class) + .getName()); + } + @Test public void testBindingWithTwoBeans() { this.context.register(MoreConfiguration.class, TestConfiguration.class); @@ -246,4 +289,16 @@ public class EnableConfigurationPropertiesTests { } } + @ConfigurationProperties(path = "${binding.location:classpath:name.yml}") + protected static class ResourceBindingProperties { + private String name; + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + } } diff --git a/spring-bootstrap/src/test/resources/name.yml b/spring-bootstrap/src/test/resources/name.yml new file mode 100644 index 00000000000..4a5bda92f0a --- /dev/null +++ b/spring-bootstrap/src/test/resources/name.yml @@ -0,0 +1,10 @@ +--- +name: foo + +--- +spring.profiles: super +name: bar + +--- +spring.profiles: other +name: spam \ No newline at end of file diff --git a/spring-bootstrap/src/test/resources/other.yml b/spring-bootstrap/src/test/resources/other.yml new file mode 100644 index 00000000000..b5bbd790764 --- /dev/null +++ b/spring-bootstrap/src/test/resources/other.yml @@ -0,0 +1,2 @@ +--- +name: other