diff --git a/spring-boot/src/main/java/org/springframework/boot/cloudfoundry/VcapApplicationListener.java b/spring-boot/src/main/java/org/springframework/boot/cloudfoundry/VcapApplicationListener.java
index e586963dc7..cf35a079e7 100644
--- a/spring-boot/src/main/java/org/springframework/boot/cloudfoundry/VcapApplicationListener.java
+++ b/spring-boot/src/main/java/org/springframework/boot/cloudfoundry/VcapApplicationListener.java
@@ -98,7 +98,7 @@ public class VcapApplicationListener implements
private static final String VCAP_SERVICES = "VCAP_SERVICES";
// Before ConfigFileApplicationListener so values there can use these ones
- private int order = ConfigFileApplicationListener.DEFAULT_CONFIG_LISTENER_ORDER - 1;;
+ private int order = ConfigFileApplicationListener.DEFAULT_ORDER - 1;;
private final JsonParser parser = JsonParserFactory.getJsonParser();
diff --git a/spring-boot/src/main/java/org/springframework/boot/config/ConfigFileApplicationListener.java b/spring-boot/src/main/java/org/springframework/boot/config/ConfigFileApplicationListener.java
index 2a25d86846..68a2662625 100644
--- a/spring-boot/src/main/java/org/springframework/boot/config/ConfigFileApplicationListener.java
+++ b/spring-boot/src/main/java/org/springframework/boot/config/ConfigFileApplicationListener.java
@@ -16,40 +16,41 @@
package org.springframework.boot.config;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
import java.util.LinkedHashSet;
+import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
-import java.util.Random;
+import java.util.Queue;
import java.util.Set;
-import org.springframework.beans.PropertyValues;
-import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.bind.PropertySourcesPropertyValues;
import org.springframework.boot.bind.RelaxedDataBinder;
+import org.springframework.boot.env.PropertySourcesLoader;
import org.springframework.boot.event.ApplicationEnvironmentPreparedEvent;
+import org.springframework.boot.event.ApplicationPreparedEvent;
+import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
-import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
-import org.springframework.context.annotation.PropertySources;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.core.Ordered;
-import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
-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.DigestUtils;
+import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
@@ -63,254 +64,99 @@ import org.springframework.util.StringUtils;
*
file:./config/:
*
*
- * Alternative locations and names can be specified using
- * {@link #setSearchLocations(String[])} and {@link #setNames(String)}.
+ * Alternative search locations and names can be specified using
+ * {@link #setSearchLocations(String)} and {@link #setSearchNames(String)}.
*
* Additional files will also be loaded based on active profiles. For example if a 'web'
* profile is active 'application-web.properties' and 'application-web.yml' will be
* considered.
*
- * The 'spring.config.name' property can be used to specify an alternative name to load or
- * alternatively the 'spring.config.location' property can be used to specify an exact
- * resource location.
+ * The 'spring.config.name' property can be used to specify an alternative name to load
+ * and the 'spring.config.location' property can be used to specify alternative search
+ * locations or specific files.
+ *
+ * Configuration properties are also bound to the {@link SpringApplication}. This makes it
+ * possible to set {@link SpringApplication} properties dynamically, like the sources
+ * ("spring.main.sources" - a CSV list) the flag to indicate a web environment
+ * ("spring.main.web_environment=true") or the flag to switch off the banner
+ * ("spring.main.show_banner=false").
*
* @author Dave Syer
* @author Phillip Webb
*/
public class ConfigFileApplicationListener implements
- ApplicationListener, Ordered {
+ ApplicationListener, Ordered {
+
+ private static final String DEFAULT_PROPERTIES = "defaultProperties";
private static final String ACTIVE_PROFILES_PROPERTY = "spring.profiles.active";
- private static final String LOCATION_VARIABLE = "${spring.config.location}";
+ private static final String CONFIG_NAME_PROPERTY = "spring.config.name";
- public static final int DEFAULT_CONFIG_LISTENER_ORDER = Ordered.HIGHEST_PRECEDENCE + 10;
+ private static final String CONFIG_LOCATION_PROPERTY = "spring.config.location";
- private String[] searchLocations = new String[] { "classpath:/", "file:./",
- "classpath:/config/", "file:./config/" };
+ private static final String DEFAULT_SEARCH_LOCATIONS = "classpath:/,file:./,"
+ + "classpath:/config/,file:./config/";
- private String names = "${spring.config.name:application}";
+ private static final String DEFAULT_NAMES = "application";
- private int order = DEFAULT_CONFIG_LISTENER_ORDER;
+ public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 10;
+
+ private String searchLocations;
+
+ private String names;
+
+ private int order = DEFAULT_ORDER;
private final ConversionService conversionService = new DefaultConversionService();
- private final Map> cache = new HashMap>();
-
- private final PropertySourceAnnotations annotations = new PropertySourceAnnotations();
-
- private PropertySourceLoadersFactory propertySourceLoadersFactory = new DefaultPropertySourceLoadersFactory();
-
- /**
- * Binds the early {@link Environment} to the {@link SpringApplication}. This makes it
- * possible to set {@link SpringApplication} properties dynamically, like the sources
- * ("spring.main.sources" - a CSV list) the flag to indicate a web environment
- * ("spring.main.web_environment=true") or the flag to switch off the banner
- * ("spring.main.show_banner=false").
- */
@Override
- public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
+ public void onApplicationEvent(ApplicationEvent event) {
+ if (event instanceof ApplicationEnvironmentPreparedEvent) {
+ onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
+ }
+ if (event instanceof ApplicationPreparedEvent) {
+ onApplicationPreparedEvent((ApplicationPreparedEvent) event);
+ }
+ };
+
+ private void onApplicationEnvironmentPreparedEvent(
+ ApplicationEnvironmentPreparedEvent event) {
Environment environment = event.getEnvironment();
if (environment instanceof ConfigurableEnvironment) {
- configure((ConfigurableEnvironment) environment, event.getSpringApplication());
+ onApplicationEnvironmentPreparedEvent((ConfigurableEnvironment) environment,
+ event.getSpringApplication());
}
}
- private void configure(ConfigurableEnvironment environment,
- SpringApplication springApplication) {
- for (Object source : springApplication.getSources()) {
- this.annotations.addFromSource(source);
+ private void onApplicationEnvironmentPreparedEvent(
+ ConfigurableEnvironment environment, SpringApplication application) {
+ RandomValuePropertySource.addToEnvironment(environment);
+ try {
+ PropertySource> defaultProperties = environment.getPropertySources()
+ .remove(DEFAULT_PROPERTIES);
+ new Loader(environment).load();
+ if (defaultProperties != null) {
+ environment.getPropertySources().addLast(defaultProperties);
+ }
}
- load(environment, new DefaultResourceLoader());
- environment.getPropertySources().addAfter(
- StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
- new RandomValuePropertySource("random"));
+ catch (IOException ex) {
+ throw new IllegalStateException("Unable to load configuration files", ex);
+ }
+ bindToSpringApplication(application, environment);
+ }
- int sourcesSizeBefore = springApplication.getSources().size();
- // Set bean properties from the early environment
- PropertyValues propertyValues = new PropertySourcesPropertyValues(
- environment.getPropertySources());
- RelaxedDataBinder binder = new RelaxedDataBinder(springApplication, "spring.main");
+ private void bindToSpringApplication(SpringApplication application,
+ ConfigurableEnvironment environment) {
+ RelaxedDataBinder binder = new RelaxedDataBinder(application, "spring.main");
binder.setConversionService(this.conversionService);
- binder.bind(propertyValues);
-
- if (springApplication.getSources().size() > sourcesSizeBefore) {
- // Configure again in case there are new @PropertySources
- configure(environment, springApplication);
- }
+ binder.bind(new PropertySourcesPropertyValues(environment.getPropertySources()));
}
- private void load(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
-
- LoadCandidates candidates = new LoadCandidates(environment, resourceLoader);
- PropertySource> defaultProperties = environment.getPropertySources().remove(
- "defaultProperties");
-
- // Load to allow a file that defines active profiles to be considered
- String firstPropertySourceName = loadInitial(environment, resourceLoader,
- candidates);
-
- // Apply the active profiles (if any) from the first property source
- if (environment.containsProperty(ACTIVE_PROFILES_PROPERTY)) {
- activeProfilesFromProperty(environment,
- environment.getProperty(ACTIVE_PROFILES_PROPERTY), true);
- }
-
- // Apply any profile additions from any source
- activeProfileAdditionsFromAnySource(environment);
-
- // Repeatedly load property sources in case additional profiles are activated
- int numberOfPropertySources;
- do {
- numberOfPropertySources = environment.getPropertySources().size();
- activeProfileAdditionsFromAnySource(environment);
- loadAgain(environment, resourceLoader, candidates, firstPropertySourceName);
- }
- while (environment.getPropertySources().size() > numberOfPropertySources);
-
- if (defaultProperties != null) {
- environment.getPropertySources().addLast(defaultProperties);
- }
- }
-
- private void activeProfileAdditionsFromAnySource(ConfigurableEnvironment environment) {
- for (PropertySource> propertySource : environment.getPropertySources()) {
- if (propertySource.containsProperty(ACTIVE_PROFILES_PROPERTY)) {
- activeProfilesFromProperty(environment,
- propertySource.getProperty(ACTIVE_PROFILES_PROPERTY), false);
- }
- }
- }
-
- private void activeProfilesFromProperty(ConfigurableEnvironment environment,
- Object property, boolean addAll) {
- for (String profile : StringUtils.commaDelimitedListToSet(property.toString())) {
- boolean addition = profile.startsWith("+");
- profile = (addition ? profile.substring(1) : profile);
- if (addAll || addition) {
- environment.addActiveProfile(profile);
- }
- }
- }
-
- private String loadInitial(ConfigurableEnvironment environment,
- ResourceLoader resourceLoader, LoadCandidates candidates) {
- String firstSourceName = null;
- // Initial load allows profiles to be activated
- for (String candidate : candidates) {
- for (String path : StringUtils.commaDelimitedListToStringArray(environment
- .resolvePlaceholders(candidate))) {
-
- if (LOCATION_VARIABLE.equals(candidate) && !path.contains("$")) {
- if (!path.contains(":")) {
- path = "file:" + path;
- }
- path = StringUtils.cleanPath(path);
- }
-
- PropertySource> source = loadPropertySource(environment,
- resourceLoader, path, null);
- if (source != null) {
- if (firstSourceName == null) {
- firstSourceName = source.getName();
- }
- environment.getPropertySources().addLast(source);
- }
- }
- }
- return firstSourceName;
- }
-
- private void loadAgain(ConfigurableEnvironment environment,
- ResourceLoader resourceLoader, LoadCandidates candidates,
- String firstPropertySourceName) {
- for (String profile : environment.getActiveProfiles()) {
- for (String candidate : candidates) {
- PropertySource> source = loadPropertySource(environment,
- resourceLoader, candidate, profile);
- addBeforeOrLast(environment, firstPropertySourceName, source);
- }
- }
- }
-
- private void addBeforeOrLast(ConfigurableEnvironment environment,
- String relativePropertySourceName, PropertySource> source) {
- if (source != null) {
- MutablePropertySources propertySources = environment.getPropertySources();
- // Originals go at the end so they don't override the specific profiles
- if (relativePropertySourceName != null) {
- propertySources.addBefore(relativePropertySourceName, source);
- }
- else {
- propertySources.addLast(source);
- }
- }
- }
-
- private PropertySource> loadPropertySource(ConfigurableEnvironment environment,
- ResourceLoader resourceLoader, String location, String profile) {
-
- Class> type = this.annotations.configuration(location);
-
- String suffix = "." + StringUtils.getFilenameExtension(location);
- if (StringUtils.hasLength(profile)) {
- location = location.replace(suffix, "-" + profile + suffix);
- }
-
- if (isPropertySourceAnnotationOnExcludedType(environment, profile, type, location)) {
- return null;
- }
-
- Resource resource = resourceLoader.getResource(location);
- String name = this.annotations.name(location);
- name = (name != null ? name : location);
- return getPropertySource(environment, name, resource, profile);
- }
-
- private boolean isPropertySourceAnnotationOnExcludedType(Environment environment,
- String profile, Class> type, String location) {
-
- if (type == null) {
- // No configuration class to worry about, just a vanilla properties location
- return false;
- }
-
- if (StringUtils.hasText(profile)
- && !this.annotations.getLocations().contains(location)) {
- // We are looking for profile specific properties and this one isn't
- // explicitly asked for in propertySourceAnnotations
- return true;
- }
-
- AnnotatedBeanDefinitionReader reader = new AnnotatedBeanDefinitionReader(
- new DefaultListableBeanFactory(), environment);
- int before = reader.getRegistry().getBeanDefinitionCount();
- reader.register(type);
- int after = reader.getRegistry().getBeanDefinitionCount();
-
- // Return if the configuration class was @Conditional and excluded
- return (after == before);
- }
-
- private PropertySource> getPropertySource(Environment environment, String name,
- Resource resource, String profile) {
- if (resource == null || !resource.exists()) {
- return null;
- }
- String key = resource.getDescription() + (profile == null ? "" : "#" + profile);
- if (this.cache.containsKey(key)) {
- return this.cache.get(key);
- }
- for (PropertySourceLoader loader : this.propertySourceLoadersFactory
- .getLoaders(environment)) {
- if (loader.supports(resource)) {
- PropertySource> propertySource = loader.load(name, resource);
- this.cache.put(key, propertySource);
- return propertySource;
- }
- }
- throw new IllegalStateException("No supported loader found for "
- + "configuration resource: " + resource);
+ private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
+ ConfigurableApplicationContext context = event.getApplicationContext();
+ context.addBeanFactoryPostProcessor(new PropertySourceOrderingPostProcessor(
+ context));
}
public void setOrder(int order) {
@@ -323,181 +169,233 @@ public class ConfigFileApplicationListener implements
}
/**
- * Sets the names of the files that should be loaded (excluding file extension) as a
- * comma separated list. Defaults to "application".
+ * Set the search locations that will be considered as a comma-separated list.
*/
- public void setNames(String names) {
+ public void setSearchLocations(String locations) {
+ Assert.hasLength(locations, "Locations must not be empty");
+ this.searchLocations = locations;
+ }
+
+ /**
+ * Sets the names of the files that should be loaded (excluding file extension) as a
+ * comma-separated list.
+ */
+ public void setSearchNames(String names) {
+ Assert.hasLength(names, "Names must not be empty");
this.names = names;
}
/**
- * Set the search locations that will be considered.
+ * {@link BeanFactoryPostProcessor} to re-order our property sources below any
+ * {@code @ProperySource} items added by the {@link ConfigurationClassPostProcessor}.
*/
- public void setSearchLocations(String[] searchLocations) {
- this.searchLocations = (searchLocations == null ? null : searchLocations.clone());
- }
+ private class PropertySourceOrderingPostProcessor implements
+ BeanFactoryPostProcessor, Ordered {
- /**
- * Set the {@link PropertySourceLoadersFactory} that will be used to create
- * {@link PropertySourceLoader}s.
- */
- public void setPropertySourceLoadersFactory(
- PropertySourceLoadersFactory propertySourceLoaderFactory) {
- this.propertySourceLoadersFactory = propertySourceLoaderFactory;
- }
+ private ConfigurableApplicationContext context;
- /**
- * Provides {@link Iterable} access to candidate property sources.
- */
- private class LoadCandidates implements Iterable {
-
- private final List candidates;
-
- public LoadCandidates(ConfigurableEnvironment environment,
- ResourceLoader resourceLoader) {
- Set candidates = new LinkedHashSet();
- addLoadCandidatesFromSearchLocations(environment, candidates);
- candidates.add(LOCATION_VARIABLE);
- // @PropertySource annotation locations go last here (eventually highest
- // priority). This unfortunately isn't the same semantics as @PropertySource
- // in Spring and it's hard to change that (so the property source gets added
- // again in last position by Spring later in the cycle).
- addLoadCandidatesFromAnnotations(environment, resourceLoader, candidates);
- this.candidates = new ArrayList(candidates);
- Collections.reverse(this.candidates);
- }
-
- private void addLoadCandidatesFromSearchLocations(
- ConfigurableEnvironment environment, Set candidates) {
- String[] names = StringUtils.commaDelimitedListToStringArray(environment
- .resolvePlaceholders(ConfigFileApplicationListener.this.names));
- for (String location : ConfigFileApplicationListener.this.searchLocations) {
- for (String extension : new String[] { ".properties", ".yml" }) {
- for (int i = names.length - 1; i >= 0; i--) {
- candidates.add(location + names[i] + extension);
- }
- }
- }
- }
-
- private void addLoadCandidatesFromAnnotations(
- ConfigurableEnvironment environment, ResourceLoader resourceLoader,
- Set candidates) {
- for (String location : ConfigFileApplicationListener.this.annotations
- .getLocations()) {
- Resource resource = resourceLoader.getResource(environment
- .resolvePlaceholders(location));
- if (!ConfigFileApplicationListener.this.annotations
- .ignoreResourceNotFound(location) && !resource.exists()) {
- throw new IllegalStateException("Resource not found: " + location);
- }
- candidates.add(location);
- }
+ public PropertySourceOrderingPostProcessor(ConfigurableApplicationContext context) {
+ this.context = context;
}
@Override
- public Iterator iterator() {
- return this.candidates.iterator();
+ public int getOrder() {
+ return Ordered.HIGHEST_PRECEDENCE;
+ }
+
+ @Override
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
+ throws BeansException {
+ reorderSources(this.context.getEnvironment());
+ }
+
+ private void reorderSources(ConfigurableEnvironment environment) {
+ ConfigurationPropertySources.finishAndRelocate(environment
+ .getPropertySources());
+ PropertySource> defaultProperties = environment.getPropertySources()
+ .remove(DEFAULT_PROPERTIES);
+ if (defaultProperties != null) {
+ environment.getPropertySources().addLast(defaultProperties);
+ }
}
}
/**
- * {@link PropertySource} that returns a random value for any property that starts
- * with {@literal "random."}. Return a {@code byte[]} unless the property name ends
- * with {@literal ".int} or {@literal ".long"}.
+ * Loads candidate property sources and configures the active profiles.
*/
- private static class RandomValuePropertySource extends PropertySource {
+ private class Loader {
- public RandomValuePropertySource(String name) {
- super(name, new Random());
+ private final ConfigurableEnvironment environment;
+
+ private final ResourceLoader resourceLoader = new DefaultResourceLoader();
+
+ private PropertySourcesLoader propertiesLoader;
+
+ private Queue profiles;
+
+ private boolean activatedProfiles;
+
+ public Loader(ConfigurableEnvironment environment) {
+ this.environment = environment;
+ }
+
+ public void load() throws IOException {
+ this.propertiesLoader = new PropertySourcesLoader();
+ this.profiles = new LinkedList();
+ this.profiles.add(null);
+ this.profiles.addAll(Arrays.asList(this.environment.getActiveProfiles()));
+ this.activatedProfiles = false;
+ addActiveProfiles(this.environment.getProperty(ACTIVE_PROFILES_PROPERTY));
+
+ while (!this.profiles.isEmpty()) {
+ String profile = this.profiles.poll();
+ for (String location : getSearchLocations()) {
+ for (String name : getSearchNames()) {
+ load(location, name, profile);
+ }
+ }
+ }
+
+ addConfigurationProperties(this.propertiesLoader.getPropertySources());
+ }
+
+ private void load(String location, String name, String profile)
+ throws IOException {
+
+ // Try to load directly from the location
+ PropertySource> locationPropertySource = load(location, profile);
+
+ // If that fails, try a search
+ if (locationPropertySource == null) {
+ for (String ext : this.propertiesLoader.getAllFileExtensions()) {
+ if (profile != null) {
+ // Try the profile specific file (with a null profile section)
+ load(location + name + "-" + profile + "." + ext, null);
+ }
+ // Try the profile (if any) specific section of the normal file
+ load(location + name + "." + ext, profile);
+ }
+ }
+ }
+
+ private PropertySource> load(String resourceLocation, String profile)
+ throws IOException {
+ Resource resource = this.resourceLoader.getResource(resourceLocation);
+ if (resource != null && resource.exists()) {
+ String name = "applicationConfig: " + resource.getDescription();
+ if (StringUtils.hasLength(profile)) {
+ name += " " + profile;
+ }
+ PropertySource> propertySource = this.propertiesLoader.load(resource,
+ name, profile);
+ if (propertySource != null) {
+ addActiveProfiles(propertySource
+ .getProperty(ACTIVE_PROFILES_PROPERTY));
+ }
+ return propertySource;
+ }
+ return null;
+ }
+
+ private void addActiveProfiles(Object property) {
+ String profiles = (property == null ? null : property.toString());
+ boolean profilesNotActivatedWhenCalled = !this.activatedProfiles;
+ for (String profile : asResolvedSet(profiles, null)) {
+ boolean addition = profile.startsWith("+");
+ profile = (addition ? profile.substring(1) : profile);
+ if (profilesNotActivatedWhenCalled || addition) {
+ this.profiles.add(profile);
+ this.environment.addActiveProfile(profile);
+ this.activatedProfiles = true;
+ }
+ }
+ }
+
+ public Set getSearchLocations() {
+ Set locations = new LinkedHashSet();
+ locations.addAll(asResolvedSet(
+ ConfigFileApplicationListener.this.searchLocations,
+ DEFAULT_SEARCH_LOCATIONS));
+ if (this.environment.containsProperty(CONFIG_LOCATION_PROPERTY)) {
+ for (String path : asResolvedSet(
+ this.environment.getProperty(CONFIG_LOCATION_PROPERTY), null)) {
+ if (!path.contains("$")) {
+ if (!path.contains(":")) {
+ path = "file:" + path;
+ }
+ path = StringUtils.cleanPath(path);
+ }
+ locations.add(path);
+ }
+ }
+ return locations;
+ }
+
+ public Set getSearchNames() {
+ if (this.environment.containsProperty(CONFIG_NAME_PROPERTY)) {
+ return asResolvedSet(this.environment.getProperty(CONFIG_NAME_PROPERTY),
+ null);
+ }
+ return asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES);
+ }
+
+ private Set asResolvedSet(String value, String fallback) {
+ List list = Arrays.asList(StringUtils
+ .commaDelimitedListToStringArray(value != null ? this.environment
+ .resolvePlaceholders(value) : fallback));
+ Collections.reverse(list);
+ return new LinkedHashSet(list);
+ }
+
+ private void addConfigurationProperties(MutablePropertySources sources) {
+ List> reorderedSources = new ArrayList>();
+ for (PropertySource> item : sources) {
+ reorderedSources.add(item);
+ }
+ Collections.reverse(reorderedSources);
+ this.environment.getPropertySources().addLast(
+ new ConfigurationPropertySources(reorderedSources));
+ }
+
+ }
+
+ /**
+ * Holds the configuration {@link PropertySource}s as they are loaded can relocate
+ * them once configuration classes have been processed.
+ */
+ static class ConfigurationPropertySources extends PropertySource {
+
+ private static final String NAME = "applicationConfigurationProperties";
+
+ private final Collection> sources;
+
+ public ConfigurationPropertySources(Collection> sources) {
+ super(NAME);
+ this.sources = sources;
}
@Override
public Object getProperty(String name) {
- if (!name.startsWith("random.")) {
- return null;
- }
- if (name.endsWith("int")) {
- return getSource().nextInt();
- }
- if (name.endsWith("long")) {
- return getSource().nextLong();
- }
- byte[] bytes = new byte[32];
- getSource().nextBytes(bytes);
- return DigestUtils.md5DigestAsHex(bytes);
- }
-
- }
-
- /**
- * Holds details collected from
- * {@link org.springframework.context.annotation.PropertySource} annotations.
- */
- private static class PropertySourceAnnotations {
-
- private final Collection locations = new LinkedHashSet();
-
- private final Map names = new HashMap();
-
- private final Map> configs = new HashMap>();
-
- private final Map ignores = new HashMap();
-
- public void addFromSource(Object source) {
- if (source instanceof Class>) {
- addFromSource((Class>) source);
- }
- }
-
- private void addFromSource(Class> source) {
- for (org.springframework.context.annotation.PropertySource propertySource : AnnotationUtils
- .getRepeatableAnnotation(source, PropertySources.class,
- org.springframework.context.annotation.PropertySource.class)) {
- add(source, propertySource);
- }
- }
-
- private void add(Class> source,
- org.springframework.context.annotation.PropertySource annotation) {
- this.locations.addAll(Arrays.asList(annotation.value()));
- if (StringUtils.hasText(annotation.name())) {
- for (String location : annotation.value()) {
- this.names.put(location, annotation.name());
+ for (PropertySource> propertySource : this.sources) {
+ Object value = propertySource.getProperty(name);
+ if (value != null) {
+ return value;
}
}
- for (String location : annotation.value()) {
- boolean reallyIgnore = annotation.ignoreResourceNotFound();
- if (this.ignores.containsKey(location)) {
- // Only if they all ignore this location will it be ignored
- reallyIgnore &= this.ignores.get(location);
+ return null;
+ }
+
+ public static void finishAndRelocate(MutablePropertySources propertySources) {
+ ConfigurationPropertySources removed = (ConfigurationPropertySources) propertySources
+ .remove(ConfigurationPropertySources.NAME);
+ if (removed != null) {
+ for (PropertySource> propertySource : removed.sources) {
+ propertySources.addLast(propertySource);
}
- this.ignores.put(location, reallyIgnore);
- this.configs.put(location, source);
}
}
- public Class> configuration(String location) {
- return this.configs.get(location);
- }
-
- public boolean ignoreResourceNotFound(String location) {
- return Boolean.TRUE.equals(this.ignores.get(location));
- }
-
- public String name(String location) {
- String name = this.names.get(location);
- if (name == null || Collections.frequency(this.names.values(), name) > 1) {
- return null;
- }
- // Only if there is a unique name for this location
- return "boot." + name;
- }
-
- public Collection getLocations() {
- return this.locations;
- }
}
}
diff --git a/spring-boot/src/main/java/org/springframework/boot/config/DefaultPropertySourceLoadersFactory.java b/spring-boot/src/main/java/org/springframework/boot/config/DefaultPropertySourceLoadersFactory.java
deleted file mode 100644
index 4dc7b97e55..0000000000
--- a/spring-boot/src/main/java/org/springframework/boot/config/DefaultPropertySourceLoadersFactory.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2012-2014 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.boot.config;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.springframework.core.env.Environment;
-import org.springframework.util.ClassUtils;
-
-/**
- * Default implementation of {@link PropertySourceLoadersFactory}. Provides a
- * {@link PropertiesPropertySourceLoader} and when possible a
- * {@link YamlPropertySourceLoader}.
- *
- * @author Dave Syer
- */
-public class DefaultPropertySourceLoadersFactory implements PropertySourceLoadersFactory {
-
- @Override
- public List getLoaders(Environment environment) {
- ArrayList loaders = new ArrayList();
- loaders.add(new PropertiesPropertySourceLoader());
- if (ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) {
- loaders.add(YamlPropertySourceLoader.springProfileAwareLoader(environment
- .getActiveProfiles()));
- }
- return loaders;
- }
-
-}
diff --git a/spring-boot/src/main/java/org/springframework/boot/config/PropertiesPropertySourceLoader.java b/spring-boot/src/main/java/org/springframework/boot/config/PropertiesPropertySourceLoader.java
deleted file mode 100644
index ef56f0f277..0000000000
--- a/spring-boot/src/main/java/org/springframework/boot/config/PropertiesPropertySourceLoader.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.boot.config;
-
-import java.io.IOException;
-import java.util.Properties;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-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}.
- *
- * @author Dave Syer
- */
-public class PropertiesPropertySourceLoader implements PropertySourceLoader {
-
- private static Log logger = LogFactory.getLog(PropertiesPropertySourceLoader.class);
-
- @Override
- public boolean supports(Resource resource) {
- return resource.getFilename().endsWith(".properties");
- }
-
- @Override
- public PropertySource> load(String name, Resource resource) {
- try {
- Properties properties = loadProperties(resource);
- // N.B. this is off by default unless user has supplied logback config in
- // standard location
- if (logger.isDebugEnabled()) {
- logger.debug("Properties loaded from " + resource + ": " + properties);
- }
- return new PropertiesPropertySource(name, properties);
- }
- catch (IOException ex) {
- throw new IllegalStateException("Could not load properties from " + resource,
- ex);
- }
- }
-
- protected Properties loadProperties(Resource resource) throws IOException {
- return PropertiesLoaderUtils.loadProperties(resource);
- }
-}
\ No newline at end of file
diff --git a/spring-boot/src/main/java/org/springframework/boot/config/PropertySourceLoadersFactory.java b/spring-boot/src/main/java/org/springframework/boot/config/PropertySourceLoadersFactory.java
deleted file mode 100644
index 456d8f97f1..0000000000
--- a/spring-boot/src/main/java/org/springframework/boot/config/PropertySourceLoadersFactory.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2012-2014 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.boot.config;
-
-import java.util.List;
-
-import org.springframework.core.env.Environment;
-
-/**
- * Factory to return {@link PropertySourceLoader}s.
- *
- * @author Dave Syer
- * @see DefaultPropertySourceLoadersFactory
- */
-public interface PropertySourceLoadersFactory {
-
- /**
- * Return a list of {@link PropertySourceLoader}s in the order that they should be
- * tried.
- * @param environment the source environment
- * @return a list of loaders
- */
- List getLoaders(Environment environment);
-
-}
diff --git a/spring-boot/src/main/java/org/springframework/boot/config/RandomValuePropertySource.java b/spring-boot/src/main/java/org/springframework/boot/config/RandomValuePropertySource.java
new file mode 100644
index 0000000000..7a33b023c4
--- /dev/null
+++ b/spring-boot/src/main/java/org/springframework/boot/config/RandomValuePropertySource.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2012-2014 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.boot.config;
+
+import java.util.Random;
+
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.PropertySource;
+import org.springframework.core.env.StandardEnvironment;
+import org.springframework.util.DigestUtils;
+
+/**
+ * {@link PropertySource} that returns a random value for any property that starts with
+ * {@literal "random."}. Return a {@code byte[]} unless the property name ends with
+ * {@literal ".int} or {@literal ".long"}.
+ *
+ * @author Dave Syer
+ */
+public class RandomValuePropertySource extends PropertySource {
+
+ public RandomValuePropertySource(String name) {
+ super(name, new Random());
+ }
+
+ @Override
+ public Object getProperty(String name) {
+ if (!name.startsWith("random.")) {
+ return null;
+ }
+ if (name.endsWith("int")) {
+ return getSource().nextInt();
+ }
+ if (name.endsWith("long")) {
+ return getSource().nextLong();
+ }
+ byte[] bytes = new byte[32];
+ getSource().nextBytes(bytes);
+ return DigestUtils.md5DigestAsHex(bytes);
+ }
+
+ public static void addToEnvironment(ConfigurableEnvironment environment) {
+ environment.getPropertySources().addAfter(
+ StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME,
+ new RandomValuePropertySource("random"));
+ }
+
+}
diff --git a/spring-boot/src/main/java/org/springframework/boot/config/YamlPropertySourceLoader.java b/spring-boot/src/main/java/org/springframework/boot/config/YamlPropertySourceLoader.java
deleted file mode 100644
index 7158497d39..0000000000
--- a/spring-boot/src/main/java/org/springframework/boot/config/YamlPropertySourceLoader.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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.boot.config;
-
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Properties;
-import java.util.Set;
-
-import org.springframework.boot.yaml.DefaultProfileDocumentMatcher;
-import org.springframework.boot.yaml.SpringProfileDocumentMatcher;
-import org.springframework.boot.yaml.YamlProcessor.DocumentMatcher;
-import org.springframework.boot.yaml.YamlProcessor.MatchStatus;
-import org.springframework.boot.yaml.YamlPropertiesFactoryBean;
-import org.springframework.core.env.PropertySource;
-import org.springframework.core.io.Resource;
-import org.springframework.util.StringUtils;
-
-/**
- * Strategy to load '.yml' files into a {@link PropertySource}.
- *
- * @author Dave Syer
- */
-public class YamlPropertySourceLoader extends PropertiesPropertySourceLoader {
-
- private final List matchers;
-
- /**
- * Create a {@link YamlPropertySourceLoader} instance with the specified matchers.
- * @param matchers the document 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) 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();
- }
-
- /**
- * 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.
- * @param activeProfiles the active profiles to match independent of file contents
- * @return a property source loader
- */
- public static YamlPropertySourceLoader springProfileAwareLoader(
- String[] activeProfiles) {
- final SpringProfileDocumentMatcher matcher = new SpringProfileDocumentMatcher();
- for (String profile : activeProfiles) {
- matcher.addActiveProfiles(profile);
- }
- return new YamlPropertySourceLoader(matcher, new DefaultProfileDocumentMatcher() {
- @Override
- public MatchStatus matches(Properties properties) {
- MatchStatus result = super.matches(properties);
- if (result == MatchStatus.FOUND) {
- Set profiles = StringUtils.commaDelimitedListToSet(properties
- .getProperty("spring.profiles.active", ""));
- for (String profile : profiles) {
- // allow document with no profile to set the active one
- matcher.addActiveProfiles(profile);
- }
- }
- return result;
- }
- });
- }
-
-}
diff --git a/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java b/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java
index 059aa83bdc..a7d9f2ea35 100644
--- a/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java
+++ b/spring-boot/src/main/java/org/springframework/boot/context/properties/ConfigurationPropertiesBindingPostProcessor.java
@@ -16,6 +16,8 @@
package org.springframework.boot.context.properties;
+import java.io.IOException;
+
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
@@ -26,9 +28,7 @@ import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.bind.PropertiesConfigurationFactory;
-import org.springframework.boot.config.PropertiesPropertySourceLoader;
-import org.springframework.boot.config.PropertySourceLoader;
-import org.springframework.boot.config.YamlPropertySourceLoader;
+import org.springframework.boot.env.PropertySourcesLoader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
@@ -332,26 +332,22 @@ public class ConfigurationPropertiesBindingPostProcessor implements BeanPostProc
return this.validator;
}
- private PropertySources loadPropertySources(String[] path) {
- MutablePropertySources propertySources = new MutablePropertySources();
- PropertySourceLoader[] loaders = {
- new PropertiesPropertySourceLoader(),
- YamlPropertySourceLoader.springProfileAwareLoader(this.environment
- .getActiveProfiles()) };
- 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.getDescription(), resource);
- propertySources.addFirst(propertySource);
- }
+ private PropertySources loadPropertySources(String[] locations) {
+ try {
+ PropertySourcesLoader loader = new PropertySourcesLoader();
+ for (String location : locations) {
+ Resource resource = this.resourceLoader.getResource(this.environment
+ .resolvePlaceholders(location));
+ for (String profile : this.environment.getActiveProfiles()) {
+ loader.load(resource, null, profile);
}
+ loader.load(resource, null, null);
}
+ return loader.getPropertySources();
+ }
+ catch (IOException ex) {
+ throw new IllegalStateException(ex);
}
- return propertySources;
}
private ConversionService getDefaultConversionService() {
diff --git a/spring-boot/src/main/java/org/springframework/boot/env/PropertiesPropertySourceLoader.java b/spring-boot/src/main/java/org/springframework/boot/env/PropertiesPropertySourceLoader.java
new file mode 100644
index 0000000000..dbebdab085
--- /dev/null
+++ b/spring-boot/src/main/java/org/springframework/boot/env/PropertiesPropertySourceLoader.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2012-2014 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.boot.env;
+
+import java.io.IOException;
+
+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}.
+ *
+ * @author Dave Syer
+ * @author Phillip Webb
+ */
+public class PropertiesPropertySourceLoader implements PropertySourceLoader {
+
+ @Override
+ public String[] getFileExtensions() {
+ return new String[] { "properties" };
+ }
+
+ @Override
+ public PropertySource> load(String name, Resource resource, String profile)
+ throws IOException {
+ if (profile != null) {
+ return null;
+ }
+ return new PropertiesPropertySource(name,
+ PropertiesLoaderUtils.loadProperties(resource));
+ }
+}
diff --git a/spring-boot/src/main/java/org/springframework/boot/config/PropertySourceLoader.java b/spring-boot/src/main/java/org/springframework/boot/env/PropertySourceLoader.java
similarity index 53%
rename from spring-boot/src/main/java/org/springframework/boot/config/PropertySourceLoader.java
rename to spring-boot/src/main/java/org/springframework/boot/env/PropertySourceLoader.java
index 39f0be1bec..a967fcd4a7 100644
--- a/spring-boot/src/main/java/org/springframework/boot/config/PropertySourceLoader.java
+++ b/spring-boot/src/main/java/org/springframework/boot/env/PropertySourceLoader.java
@@ -14,29 +14,39 @@
* limitations under the License.
*/
-package org.springframework.boot.config;
+package org.springframework.boot.env;
+
+import java.io.IOException;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.SpringFactoriesLoader;
/**
- * Strategy interface used to load a {@link PropertySource}.
+ * Strategy interface located via {@link SpringFactoriesLoader} and used to load a
+ * {@link PropertySource}.
*
* @author Dave Syer
+ * @author Phillip Webb
*/
public interface PropertySourceLoader {
/**
- * Returns {@code true} if the {@link Resource} is supported.
- * @return if the resource is supported
+ * Returns the file extensions that the loader supports (excluding the '.').
*/
- boolean supports(Resource resource);
+ String[] getFileExtensions();
/**
* Load the resource into a property source.
* @param name the name of the property source
- * @return a property source
+ * @param resource the resource to load
+ * @param profile the name of the profile to load or {@code null}. The profile can be
+ * used to load multi-document files (such as YAML). Simple property formats should
+ * {@code null} when asked to load a profile.
+ * @return a property source or {@code null}
+ * @throws IOException
*/
- PropertySource> load(String name, Resource resource);
+ PropertySource> load(String name, Resource resource, String profile)
+ throws IOException;
}
diff --git a/spring-boot/src/main/java/org/springframework/boot/env/PropertySourcesLoader.java b/spring-boot/src/main/java/org/springframework/boot/env/PropertySourcesLoader.java
new file mode 100644
index 0000000000..742f8245c5
--- /dev/null
+++ b/spring-boot/src/main/java/org/springframework/boot/env/PropertySourcesLoader.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2012-2014 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.boot.env;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.springframework.core.env.MutablePropertySources;
+import org.springframework.core.env.PropertySource;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.support.SpringFactoriesLoader;
+import org.springframework.util.Assert;
+
+/**
+ * Utiltiy that can be used to {@link MutablePropertySources} using
+ * {@link PropertySourceLoader}s.
+ *
+ * @author Phillip Webb
+ */
+public class PropertySourcesLoader {
+
+ private final MutablePropertySources propertySources;
+
+ private final List loaders;
+
+ /**
+ * Create a new {@link PropertySourceLoader} instance backed by a new
+ * {@link MutablePropertySources}.
+ */
+ public PropertySourcesLoader() {
+ this(new MutablePropertySources());
+ }
+
+ /**
+ * Create a new {@link PropertySourceLoader} instance backed by the specified
+ * {@link MutablePropertySources}.
+ * @param propertySources the destination property sources
+ */
+ public PropertySourcesLoader(MutablePropertySources propertySources) {
+ Assert.notNull(propertySources, "PropertySources must not be null");
+ this.propertySources = propertySources;
+ this.loaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
+ null);
+ }
+
+ /**
+ * Load the specified resource (if possible) and add it as the first source.
+ * @param resource the source resource (may be {@code null}).
+ * @param name the root property name (may be {@code null}).
+ * @param profile a specific profile to load or {@code null} to load the default.
+ * @return the loaded property source or {@code null}
+ * @throws IOException
+ */
+ public PropertySource> load(Resource resource, String name, String profile)
+ throws IOException {
+ if (resource != null && resource.exists()) {
+ name = generatePropertySourceName(resource, name, profile);
+ for (PropertySourceLoader loader : this.loaders) {
+ if (canLoadFileExtension(loader, resource)) {
+ PropertySource> source = loader.load(name, resource, profile);
+ addPropertySource(source);
+ return source;
+ }
+ }
+ }
+ return null;
+ }
+
+ private String generatePropertySourceName(Resource resource, String name,
+ String profile) {
+ if (name == null) {
+ name = resource.getDescription();
+ }
+ return (profile == null ? name : name + "#" + profile);
+ }
+
+ private boolean canLoadFileExtension(PropertySourceLoader loader, Resource resource) {
+ String filename = resource.getFilename().toLowerCase();
+ for (String extension : loader.getFileExtensions()) {
+ if (filename.endsWith("." + extension.toLowerCase())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void addPropertySource(PropertySource> propertySource) {
+ if (propertySource != null) {
+ this.propertySources.addLast(propertySource);
+ }
+ }
+
+ /**
+ * Return the {@link MutablePropertySources} being loaded.
+ */
+ public MutablePropertySources getPropertySources() {
+ return this.propertySources;
+ }
+
+ /**
+ * Returns all file extensions that could be loaded.
+ */
+ public Set getAllFileExtensions() {
+ Set fileExtensions = new HashSet();
+ for (PropertySourceLoader loader : this.loaders) {
+ fileExtensions.addAll(Arrays.asList(loader.getFileExtensions()));
+ }
+ return fileExtensions;
+ }
+
+}
diff --git a/spring-boot/src/main/java/org/springframework/boot/env/YamlPropertySourceLoader.java b/spring-boot/src/main/java/org/springframework/boot/env/YamlPropertySourceLoader.java
new file mode 100644
index 0000000000..2fa2a1a039
--- /dev/null
+++ b/spring-boot/src/main/java/org/springframework/boot/env/YamlPropertySourceLoader.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2012-2014 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.boot.env;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import org.springframework.boot.yaml.SpringProfileDocumentMatcher;
+import org.springframework.boot.yaml.YamlPropertiesFactoryBean;
+import org.springframework.core.env.PropertiesPropertySource;
+import org.springframework.core.env.PropertySource;
+import org.springframework.core.io.Resource;
+import org.springframework.util.ClassUtils;
+
+/**
+ * Strategy to load '.yml' files into a {@link PropertySource}.
+ *
+ * @author Dave Syer
+ * @author Phillip Webb
+ */
+public class YamlPropertySourceLoader implements PropertySourceLoader {
+
+ @Override
+ public String[] getFileExtensions() {
+ return new String[] { "yml" };
+ }
+
+ @Override
+ public PropertySource> load(String name, Resource resource, String profile)
+ throws IOException {
+ if (ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) {
+ YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
+ if (profile == null) {
+ factory.setMatchDefault(true);
+ factory.setDocumentMatchers(new SpringProfileDocumentMatcher());
+ }
+ else {
+ factory.setMatchDefault(false);
+ factory.setDocumentMatchers(new SpringProfileDocumentMatcher(profile));
+ }
+ factory.setResources(new Resource[] { resource });
+ Properties properties = factory.getObject();
+ if (profile == null || !properties.isEmpty()) {
+ return new PropertiesPropertySource(name, properties);
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/spring-boot/src/main/java/org/springframework/boot/yaml/SpringProfileDocumentMatcher.java b/spring-boot/src/main/java/org/springframework/boot/yaml/SpringProfileDocumentMatcher.java
index 15724a9ab9..7568493274 100644
--- a/spring-boot/src/main/java/org/springframework/boot/yaml/SpringProfileDocumentMatcher.java
+++ b/spring-boot/src/main/java/org/springframework/boot/yaml/SpringProfileDocumentMatcher.java
@@ -37,6 +37,13 @@ public class SpringProfileDocumentMatcher implements DocumentMatcher {
private String[] activeProfiles = new String[0];
+ public SpringProfileDocumentMatcher() {
+ }
+
+ public SpringProfileDocumentMatcher(String... profiles) {
+ addActiveProfiles(profiles);
+ }
+
public void addActiveProfiles(String... profiles) {
LinkedHashSet set = new LinkedHashSet(
Arrays.asList(this.activeProfiles));
@@ -55,4 +62,4 @@ public class SpringProfileDocumentMatcher implements DocumentMatcher {
return new ArrayDocumentMatcher("spring.profiles", profiles).matches(properties);
}
-}
\ No newline at end of file
+}
diff --git a/spring-boot/src/main/java/org/springframework/boot/yaml/YamlProcessor.java b/spring-boot/src/main/java/org/springframework/boot/yaml/YamlProcessor.java
index 77db233c5e..73b91408bf 100644
--- a/spring-boot/src/main/java/org/springframework/boot/yaml/YamlProcessor.java
+++ b/spring-boot/src/main/java/org/springframework/boot/yaml/YamlProcessor.java
@@ -17,6 +17,8 @@
package org.springframework.boot.yaml;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
@@ -37,7 +39,7 @@ import org.yaml.snakeyaml.Yaml;
*
* @author Dave Syer
*/
-public class YamlProcessor {
+public abstract class YamlProcessor {
private final Log logger = LogFactory.getLog(getClass());
@@ -75,14 +77,15 @@ public class YamlProcessor {
*
* @param matchers a map of keys to value patterns (regular expressions)
*/
- public void setDocumentMatchers(List extends DocumentMatcher> matchers) {
- this.documentMatchers = Collections.unmodifiableList(matchers);
+ public void setDocumentMatchers(DocumentMatcher... matchers) {
+ this.documentMatchers = Collections
+ .unmodifiableList(new ArrayList(Arrays.asList(matchers)));
}
/**
* Flag indicating that a document for which all the
- * {@link #setDocumentMatchers(List) document matchers} abstain will nevertheless
- * match.
+ * {@link #setDocumentMatchers(DocumentMatcher...) document matchers} abstain will
+ * nevertheless match.
* @param matchDefault the flag to set (default true)
*/
public void setMatchDefault(boolean matchDefault) {
@@ -111,10 +114,10 @@ public class YamlProcessor {
/**
* Provides an opportunity for subclasses to process the Yaml parsed from the supplied
* resources. Each resource is parsed in turn and the documents inside checked against
- * the {@link #setDocumentMatchers(List) matchers}. If a document matches it is passed
- * into the callback, along with its representation as Properties. Depending on the
- * {@link #setResolutionMethod(ResolutionMethod)} not all of the documents will be
- * parsed.
+ * the {@link #setDocumentMatchers(DocumentMatcher...) matchers}. If a document
+ * matches it is passed into the callback, along with its representation as
+ * Properties. Depending on the {@link #setResolutionMethod(ResolutionMethod)} not all
+ * of the documents will be parsed.
* @param callback a callback to delegate to once matching documents are found
*/
protected void process(MatchCallback callback) {
@@ -172,6 +175,7 @@ public class YamlProcessor {
result.put("document", object);
return result;
}
+
Map map = (Map) object;
for (Entry entry : map.entrySet()) {
Object value = entry.getValue();
@@ -191,43 +195,42 @@ public class YamlProcessor {
}
private boolean process(Map map, MatchCallback callback) {
+
Properties properties = new Properties();
assignProperties(properties, map, null);
+
if (this.documentMatchers.isEmpty()) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Merging document (no matchers set)" + map);
}
callback.process(properties, map);
+ return true;
}
- else {
- boolean valueFound = false;
- MatchStatus result = MatchStatus.ABSTAIN;
- for (DocumentMatcher matcher : this.documentMatchers) {
- MatchStatus match = matcher.matches(properties);
- result = MatchStatus.getMostSpecific(match, result);
- if (match == MatchStatus.FOUND) {
- if (this.logger.isDebugEnabled()) {
- this.logger.debug("Matched document with document matcher: "
- + properties);
- }
- callback.process(properties, map);
- valueFound = true;
- // No need to check for more matches
- break;
- }
- }
- if (result == MatchStatus.ABSTAIN && this.matchDefault) {
+
+ MatchStatus result = MatchStatus.ABSTAIN;
+ for (DocumentMatcher matcher : this.documentMatchers) {
+ MatchStatus match = matcher.matches(properties);
+ result = MatchStatus.getMostSpecific(match, result);
+ if (match == MatchStatus.FOUND) {
if (this.logger.isDebugEnabled()) {
- this.logger.debug("Matched document with default matcher: " + map);
+ this.logger.debug("Matched document with document matcher: "
+ + properties);
}
callback.process(properties, map);
- }
- else if (!valueFound) {
- this.logger.debug("Unmatched document");
- return false;
+ return true;
}
}
- return true;
+
+ if (result == MatchStatus.ABSTAIN && this.matchDefault) {
+ if (this.logger.isDebugEnabled()) {
+ this.logger.debug("Matched document with default matcher: " + map);
+ }
+ callback.process(properties, map);
+ return true;
+ }
+
+ this.logger.debug("Unmatched document");
+ return false;
}
private void assignProperties(Properties properties, Map input,
@@ -300,7 +303,21 @@ public class YamlProcessor {
* Status returned from {@link DocumentMatcher#matches(Properties)}
*/
public static enum MatchStatus {
- FOUND, NOT_FOUND, ABSTAIN;
+
+ /**
+ * A match was found.
+ */
+ FOUND,
+
+ /**
+ * No match was found.
+ */
+ NOT_FOUND,
+
+ /**
+ * The matcher should not be considered.
+ */
+ ABSTAIN;
/**
* Compare two {@link MatchStatus} items, returning the most specific status.
diff --git a/spring-boot/src/main/resources/META-INF/spring.factories b/spring-boot/src/main/resources/META-INF/spring.factories
index 924e4d76f0..6477d90210 100644
--- a/spring-boot/src/main/resources/META-INF/spring.factories
+++ b/spring-boot/src/main/resources/META-INF/spring.factories
@@ -1,3 +1,8 @@
+# ProperySource Loaders
+org.springframework.boot.env.PropertySourceLoader=\
+org.springframework.boot.env.PropertiesPropertySourceLoader,\
+org.springframework.boot.env.YamlPropertySourceLoader
+
# Run Participants
org.springframework.boot.SpringApplicationRunParticipant=\
org.springframework.boot.event.EventPublishingRunParticipant
diff --git a/spring-boot/src/test/java/org/springframework/boot/ReproTests.java b/spring-boot/src/test/java/org/springframework/boot/ReproTests.java
index c347c0d3ff..1323723b44 100644
--- a/spring-boot/src/test/java/org/springframework/boot/ReproTests.java
+++ b/spring-boot/src/test/java/org/springframework/boot/ReproTests.java
@@ -43,6 +43,36 @@ public class ReproTests {
assertThat(context.getEnvironment().acceptsProfiles("a"), equalTo(true));
}
+ @Test
+ public void activeProfilesWithYaml() throws Exception {
+ // gh-322
+ SpringApplication application = new SpringApplication(Config.class);
+ application.setWebEnvironment(false);
+ String configName = "--spring.config.name=activeprofilerepro";
+ assertVersionProperty(application.run(configName, "--spring.profiles.active=B"),
+ "B", "B");
+ assertVersionProperty(application.run(configName), "B", "B");
+ assertVersionProperty(application.run(configName, "--spring.profiles.active=A"),
+ "A", "A");
+ assertVersionProperty(application.run(configName, "--spring.profiles.active=C"),
+ "C", "C");
+ assertVersionProperty(
+ application.run(configName, "--spring.profiles.active=A,C"), "A", "A",
+ "C");
+ assertVersionProperty(
+ application.run(configName, "--spring.profiles.active=C,A"), "C", "C",
+ "A");
+ }
+
+ private void assertVersionProperty(ConfigurableApplicationContext context,
+ String expectedVersion, String... expectedActiveProfiles) {
+ assertThat(context.getEnvironment().getActiveProfiles(),
+ equalTo(expectedActiveProfiles));
+ assertThat("version mismatch", context.getEnvironment().getProperty("version"),
+ equalTo(expectedVersion));
+ context.close();
+ }
+
@Configuration
public static class Config {
diff --git a/spring-boot/src/test/java/org/springframework/boot/config/ConfigFileApplicationListenerTests.java b/spring-boot/src/test/java/org/springframework/boot/config/ConfigFileApplicationListenerTests.java
index 5a6bd80657..d6e69f3ad9 100644
--- a/spring-boot/src/test/java/org/springframework/boot/config/ConfigFileApplicationListenerTests.java
+++ b/spring-boot/src/test/java/org/springframework/boot/config/ConfigFileApplicationListenerTests.java
@@ -17,7 +17,6 @@
package org.springframework.boot.config;
import java.util.Arrays;
-import java.util.List;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
@@ -27,6 +26,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.boot.SpringApplication;
+import org.springframework.boot.config.ConfigFileApplicationListener.ConfigurationPropertySources;
import org.springframework.boot.event.ApplicationEnvironmentPreparedEvent;
import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.ConfigurableApplicationContext;
@@ -34,10 +34,9 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
-import org.springframework.core.env.Environment;
+import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.SimpleCommandLinePropertySource;
import org.springframework.core.env.StandardEnvironment;
-import org.springframework.core.io.Resource;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.equalTo;
@@ -72,7 +71,7 @@ public class ConfigFileApplicationListenerTests {
@Test
public void loadPropertiesFile() throws Exception {
- this.initializer.setNames("testproperties");
+ this.initializer.setSearchNames("testproperties");
this.initializer.onApplicationEvent(this.event);
String property = this.environment.getProperty("my.property");
assertThat(property, equalTo("frompropertiesfile"));
@@ -108,7 +107,7 @@ public class ConfigFileApplicationListenerTests {
@Test
public void loadTwoPropertiesFiles() throws Exception {
- this.initializer.setNames("moreproperties,testproperties");
+ this.initializer.setSearchNames("moreproperties,testproperties");
this.initializer.onApplicationEvent(this.event);
String property = this.environment.getProperty("my.property");
assertThat(property, equalTo("frommorepropertiesfile"));
@@ -116,7 +115,7 @@ public class ConfigFileApplicationListenerTests {
@Test
public void loadYamlFile() throws Exception {
- this.initializer.setNames("testyaml");
+ this.initializer.setSearchNames("testyaml");
this.initializer.onApplicationEvent(this.event);
String property = this.environment.getProperty("my.property");
assertThat(property, equalTo("fromyamlfile"));
@@ -128,7 +127,7 @@ public class ConfigFileApplicationListenerTests {
public void commandLineWins() throws Exception {
this.environment.getPropertySources().addFirst(
new SimpleCommandLinePropertySource("--my.property=fromcommandline"));
- this.initializer.setNames("testproperties");
+ this.initializer.setSearchNames("testproperties");
this.initializer.onApplicationEvent(this.event);
String property = this.environment.getProperty("my.property");
assertThat(property, equalTo("fromcommandline"));
@@ -137,7 +136,7 @@ public class ConfigFileApplicationListenerTests {
@Test
public void systemPropertyWins() throws Exception {
System.setProperty("my.property", "fromsystem");
- this.initializer.setNames("testproperties");
+ this.initializer.setSearchNames("testproperties");
this.initializer.onApplicationEvent(this.event);
String property = this.environment.getProperty("my.property");
assertThat(property, equalTo("fromsystem"));
@@ -145,7 +144,7 @@ public class ConfigFileApplicationListenerTests {
@Test
public void loadPropertiesThenProfileProperties() throws Exception {
- this.initializer.setNames("enableprofile");
+ this.initializer.setSearchNames("enableprofile");
this.initializer.onApplicationEvent(this.event);
String property = this.environment.getProperty("my.property");
assertThat(property, equalTo("fromprofilepropertiesfile"));
@@ -153,7 +152,7 @@ public class ConfigFileApplicationListenerTests {
@Test
public void profilePropertiesUsedInPlaceholders() throws Exception {
- this.initializer.setNames("enableprofile");
+ this.initializer.setSearchNames("enableprofile");
this.initializer.onApplicationEvent(this.event);
String property = this.environment.getProperty("one.more");
assertThat(property, equalTo("fromprofilepropertiesfile"));
@@ -161,7 +160,7 @@ public class ConfigFileApplicationListenerTests {
@Test
public void yamlProfiles() throws Exception {
- this.initializer.setNames("testprofiles");
+ this.initializer.setSearchNames("testprofiles");
this.environment.setActiveProfiles("dev");
this.initializer.onApplicationEvent(this.event);
String property = this.environment.getProperty("my.property");
@@ -172,7 +171,7 @@ public class ConfigFileApplicationListenerTests {
@Test
public void yamlSetsProfiles() throws Exception {
- this.initializer.setNames("testsetprofiles");
+ this.initializer.setSearchNames("testsetprofiles");
this.initializer.onApplicationEvent(this.event);
String property = this.environment.getProperty("my.property");
assertThat(Arrays.asList(this.environment.getActiveProfiles()), contains("dev"));
@@ -183,7 +182,7 @@ public class ConfigFileApplicationListenerTests {
public void yamlProfileCanBeChanged() throws Exception {
EnvironmentTestUtils.addEnvironment(this.environment,
"spring.profiles.active:prod");
- this.initializer.setNames("testsetprofiles");
+ this.initializer.setSearchNames("testsetprofiles");
this.initializer.onApplicationEvent(this.event);
assertThat(this.environment.getActiveProfiles(), equalTo(new String[] { "prod" }));
}
@@ -206,10 +205,11 @@ public class ConfigFileApplicationListenerTests {
this.initializer.onApplicationEvent(this.event);
String property = this.environment.getProperty("my.property");
assertThat(property, equalTo("fromspecificlocation"));
- assertThat(this.environment, containsProperySource(location));
+ assertThat(this.environment, containsProperySource("applicationConfig: "
+ + "class path resource [specificlocation.properties]"));
// The default property source is still there
- assertThat(this.environment, containsProperySource("classpath:"
- + "/application.properties"));
+ assertThat(this.environment, containsProperySource("applicationConfig: "
+ + "class path resource [application.properties]"));
assertThat(this.environment.getProperty("foo"), equalTo("bucket"));
}
@@ -219,35 +219,8 @@ public class ConfigFileApplicationListenerTests {
EnvironmentTestUtils.addEnvironment(this.environment, "spring.config.location:"
+ location);
this.initializer.onApplicationEvent(this.event);
- assertThat(this.environment, containsProperySource(location));
- }
-
- @Test
- public void unsupportedResource() throws Exception {
- this.initializer
- .setPropertySourceLoadersFactory(new PropertySourceLoadersFactory() {
- @Override
- public List getLoaders(Environment environment) {
- return Arrays
- . asList(new PropertySourceLoader() {
-
- @Override
- public boolean supports(Resource resource) {
- return false;
- }
-
- @Override
- public org.springframework.core.env.PropertySource> load(
- String name, Resource resource) {
- return null;
- }
-
- });
- }
- });
- this.expected.expect(IllegalStateException.class);
- this.expected.expectMessage("No supported loader");
- this.initializer.onApplicationEvent(this.event);
+ assertThat(this.environment, containsProperySource("applicationConfig: "
+ + "URL [" + location + "]"));
}
@Test
@@ -256,7 +229,8 @@ public class ConfigFileApplicationListenerTests {
EnvironmentTestUtils.addEnvironment(this.environment, "spring.config.location:"
+ location);
this.initializer.onApplicationEvent(this.event);
- assertThat(this.environment, containsProperySource("file:" + location));
+ assertThat(this.environment, containsProperySource("applicationConfig: "
+ + "URL [file:" + location + "]"));
}
@Test
@@ -266,8 +240,8 @@ public class ConfigFileApplicationListenerTests {
ConfigurableApplicationContext context = application.run();
String property = context.getEnvironment().getProperty("my.property");
assertThat(property, equalTo("fromspecificlocation"));
- assertThat(context.getEnvironment(), containsProperySource("classpath:"
- + "/specificlocation.properties"));
+ assertThat(context.getEnvironment(), containsProperySource("class path resource "
+ + "[specificlocation.properties]"));
context.close();
}
@@ -282,8 +256,8 @@ public class ConfigFileApplicationListenerTests {
ConfigurableApplicationContext context = application.run();
String property = context.getEnvironment().getProperty("my.property");
assertThat(property, equalTo("fromspecificlocation"));
- assertThat(context.getEnvironment(), containsProperySource("classpath:"
- + "/specificlocation.properties"));
+ assertThat(context.getEnvironment(), containsProperySource("class path resource "
+ + "[specificlocation.properties]"));
context.close();
}
@@ -295,10 +269,7 @@ public class ConfigFileApplicationListenerTests {
ConfigurableApplicationContext context = application.run();
String property = context.getEnvironment().getProperty("my.property");
assertThat(property, equalTo("fromspecificlocation"));
- // In this case "foo" should be the specificlocation.properties source, but Spring
- // will have shifted it to the back of the line.
- assertThat(context.getEnvironment().getPropertySources().get("boot.foo"),
- notNullValue());
+ assertThat(context.getEnvironment(), containsProperySource("foo"));
context.close();
}
@@ -311,10 +282,10 @@ public class ConfigFileApplicationListenerTests {
.run("--spring.profiles.active=myprofile");
String property = context.getEnvironment().getProperty("my.property");
assertThat(property, equalTo("frompropertiesfile"));
- assertThat(context.getEnvironment(), containsProperySource("classpath:"
- + "/enableprofile.properties"));
- assertThat(context.getEnvironment(), not(containsProperySource("classpath:"
- + "/enableprofile-myprofile.properties")));
+ assertThat(context.getEnvironment(), containsProperySource("class path resource "
+ + "[enableprofile.properties]"));
+ assertThat(context.getEnvironment(), not(containsProperySource("classpath:/"
+ + "enableprofile-myprofile.properties")));
context.close();
}
@@ -339,8 +310,8 @@ public class ConfigFileApplicationListenerTests {
ConfigurableApplicationContext context = application.run();
String property = context.getEnvironment().getProperty("my.property");
assertThat(property, equalTo("frommorepropertiesfile"));
- assertThat(context.getEnvironment(),
- containsProperySource("classpath:/specificlocation.properties"));
+ assertThat(context.getEnvironment(), containsProperySource("class path resource "
+ + "[specificlocation.properties]"));
context.close();
}
@@ -352,12 +323,7 @@ public class ConfigFileApplicationListenerTests {
ConfigurableApplicationContext context = application.run();
String property = context.getEnvironment().getProperty("my.property");
assertThat(property, equalTo("frommorepropertiesfile"));
- // foo is there but it is a dead rubber because the individual sources get higher
- // priority (and are named after the resource locations)
- assertThat(context.getEnvironment().getPropertySources().get("foo"),
- notNullValue());
- assertThat(context.getEnvironment(),
- containsProperySource("classpath:/specificlocation.properties"));
+ assertThat(context.getEnvironment(), containsProperySource("foo"));
context.close();
}
@@ -377,7 +343,6 @@ public class ConfigFileApplicationListenerTests {
private static Matcher super ConfigurableEnvironment> containsProperySource(
final String sourceName) {
return new TypeSafeDiagnosingMatcher() {
-
@Override
public void describeTo(Description description) {
description.appendText("environment containing property source ")
@@ -387,9 +352,12 @@ public class ConfigFileApplicationListenerTests {
@Override
protected boolean matchesSafely(ConfigurableEnvironment item,
Description mismatchDescription) {
- mismatchDescription.appendText("Not matched against: ").appendValue(
+ MutablePropertySources sources = new MutablePropertySources(
item.getPropertySources());
- return item.getPropertySources().contains(sourceName);
+ ConfigurationPropertySources.finishAndRelocate(sources);
+ mismatchDescription.appendText("Not matched against: ").appendValue(
+ sources);
+ return sources.contains(sourceName);
}
};
}
diff --git a/spring-boot/src/test/java/org/springframework/boot/yaml/YamlProcessorTests.java b/spring-boot/src/test/java/org/springframework/boot/yaml/YamlProcessorTests.java
index a22fe97181..14332917bc 100644
--- a/spring-boot/src/test/java/org/springframework/boot/yaml/YamlProcessorTests.java
+++ b/spring-boot/src/test/java/org/springframework/boot/yaml/YamlProcessorTests.java
@@ -36,7 +36,8 @@ import static org.junit.Assert.assertEquals;
*/
public class YamlProcessorTests {
- private final YamlProcessor processor = new YamlProcessor();
+ private final YamlProcessor processor = new YamlProcessor() {
+ };
@Rule
public ExpectedException exception = ExpectedException.none();
diff --git a/spring-boot/src/test/java/org/springframework/boot/yaml/YamlPropertiesFactoryBeanTests.java b/spring-boot/src/test/java/org/springframework/boot/yaml/YamlPropertiesFactoryBeanTests.java
index 2cb6d1b9fb..22393a0693 100644
--- a/spring-boot/src/test/java/org/springframework/boot/yaml/YamlPropertiesFactoryBeanTests.java
+++ b/spring-boot/src/test/java/org/springframework/boot/yaml/YamlPropertiesFactoryBeanTests.java
@@ -16,7 +16,6 @@
package org.springframework.boot.yaml;
-import java.util.Arrays;
import java.util.Map;
import java.util.Properties;
@@ -33,7 +32,9 @@ import org.springframework.core.io.Resource;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.scanner.ScannerException;
-import static org.junit.Assert.assertEquals;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
/**
* Tests for {@link YamlPropertiesFactoryBean}.
@@ -51,8 +52,8 @@ public class YamlPropertiesFactoryBeanTests {
factory.setResources(new Resource[] { new ByteArrayResource(
"foo: bar\nspam:\n foo: baz".getBytes()) });
Properties properties = factory.getObject();
- assertEquals("bar", properties.get("foo"));
- assertEquals("baz", properties.get("spam.foo"));
+ assertThat(properties.getProperty("foo"), equalTo("bar"));
+ assertThat(properties.getProperty("spam.foo"), equalTo("baz"));
}
@Test
@@ -72,31 +73,29 @@ public class YamlPropertiesFactoryBeanTests {
new ByteArrayResource("foo: bar\nspam:\n foo: baz".getBytes()),
new ByteArrayResource("foo:\n bar: spam".getBytes()) });
Properties properties = factory.getObject();
- assertEquals("bar", properties.get("foo"));
- assertEquals("baz", properties.get("spam.foo"));
- assertEquals("spam", properties.get("foo.bar"));
+ assertThat(properties.getProperty("foo"), equalTo("bar"));
+ assertThat(properties.getProperty("spam.foo"), equalTo("baz"));
+ assertThat(properties.getProperty("foo.bar"), equalTo("spam"));
}
@Test
- @Ignore
- // We can't fail on duplicate keys because the Map is created by the YAML library
+ @Ignore("We can't fail on duplicate keys because the Map is created by the YAML library")
public void testLoadResourcesWithInternalOverride() throws Exception {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(new Resource[] { new ByteArrayResource(
"foo: bar\nspam:\n foo: baz\nfoo: bucket".getBytes()) });
Properties properties = factory.getObject();
- assertEquals("bar", properties.get("foo"));
+ assertThat(properties.getProperty("foo"), equalTo("bar"));
}
@Test
- @Ignore
- // We can't fail on duplicate keys because the Map is created by the YAML library
+ @Ignore("We can't fail on duplicate keys because the Map is created by the YAML library")
public void testLoadResourcesWithNestedInternalOverride() throws Exception {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(new Resource[] { new ByteArrayResource(
"foo:\n bar: spam\n foo: baz\nbreak: it\nfoo: bucket".getBytes()) });
Properties properties = factory.getObject();
- assertEquals("spam", properties.get("foo.bar"));
+ assertThat(properties.getProperty("foo.bar"), equalTo("spam"));
}
@Test
@@ -105,8 +104,8 @@ public class YamlPropertiesFactoryBeanTests {
factory.setResources(new Resource[] { new ByteArrayResource(
"foo: bar\nspam: baz\n---\nfoo: bag".getBytes()) });
Properties properties = factory.getObject();
- assertEquals("bag", properties.get("foo"));
- assertEquals("baz", properties.get("spam"));
+ assertThat(properties.getProperty("foo"), equalTo("bag"));
+ assertThat(properties.getProperty("spam"), equalTo("baz"));
}
@Test
@@ -114,17 +113,16 @@ public class YamlPropertiesFactoryBeanTests {
YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
factory.setResources(new Resource[] { new ByteArrayResource(
"foo: bar\nspam: baz\n---\nfoo: bag\nspam: bad".getBytes()) });
- factory.setDocumentMatchers(Arrays
- . asList(new DocumentMatcher() {
- @Override
- public MatchStatus matches(Properties properties) {
- return "bag".equals(properties.getProperty("foo")) ? MatchStatus.FOUND
- : MatchStatus.NOT_FOUND;
- }
- }));
+ factory.setDocumentMatchers(new DocumentMatcher() {
+ @Override
+ public MatchStatus matches(Properties properties) {
+ return "bag".equals(properties.getProperty("foo")) ? MatchStatus.FOUND
+ : MatchStatus.NOT_FOUND;
+ }
+ });
Properties properties = factory.getObject();
- assertEquals("bag", properties.get("foo"));
- assertEquals("bad", properties.get("spam"));
+ assertThat(properties.getProperty("foo"), equalTo("bag"));
+ assertThat(properties.getProperty("spam"), equalTo("bad"));
}
@Test
@@ -133,21 +131,42 @@ public class YamlPropertiesFactoryBeanTests {
factory.setMatchDefault(true);
factory.setResources(new Resource[] { new ByteArrayResource(
"one: two\n---\nfoo: bar\nspam: baz\n---\nfoo: bag\nspam: bad".getBytes()) });
- factory.setDocumentMatchers(Arrays
- . asList(new DocumentMatcher() {
- @Override
- public MatchStatus matches(Properties properties) {
- if (!properties.containsKey("foo")) {
- return MatchStatus.ABSTAIN;
- }
- return "bag".equals(properties.getProperty("foo")) ? MatchStatus.FOUND
- : MatchStatus.NOT_FOUND;
- }
- }));
+ factory.setDocumentMatchers(new DocumentMatcher() {
+ @Override
+ public MatchStatus matches(Properties properties) {
+ if (!properties.containsKey("foo")) {
+ return MatchStatus.ABSTAIN;
+ }
+ return "bag".equals(properties.getProperty("foo")) ? MatchStatus.FOUND
+ : MatchStatus.NOT_FOUND;
+ }
+ });
Properties properties = factory.getObject();
- assertEquals("bag", properties.get("foo"));
- assertEquals("bad", properties.get("spam"));
- assertEquals("two", properties.get("one"));
+ assertThat(properties.getProperty("foo"), equalTo("bag"));
+ assertThat(properties.getProperty("spam"), equalTo("bad"));
+ assertThat(properties.getProperty("one"), equalTo("two"));
+ }
+
+ @Test
+ public void testLoadResourceWithoutDefaultMatch() throws Exception {
+ YamlPropertiesFactoryBean factory = new YamlPropertiesFactoryBean();
+ factory.setMatchDefault(false);
+ factory.setResources(new Resource[] { new ByteArrayResource(
+ "one: two\n---\nfoo: bar\nspam: baz\n---\nfoo: bag\nspam: bad".getBytes()) });
+ factory.setDocumentMatchers(new DocumentMatcher() {
+ @Override
+ public MatchStatus matches(Properties properties) {
+ if (!properties.containsKey("foo")) {
+ return MatchStatus.ABSTAIN;
+ }
+ return "bag".equals(properties.getProperty("foo")) ? MatchStatus.FOUND
+ : MatchStatus.NOT_FOUND;
+ }
+ });
+ Properties properties = factory.getObject();
+ assertThat(properties.getProperty("foo"), equalTo("bag"));
+ assertThat(properties.getProperty("spam"), equalTo("bad"));
+ assertThat(properties.getProperty("one"), nullValue());
}
@Test
@@ -156,21 +175,20 @@ public class YamlPropertiesFactoryBeanTests {
factory.setMatchDefault(true);
factory.setResources(new Resource[] { new ByteArrayResource(
"one: two\n---\nfoo: bag\nspam: bad\n---\nfoo: bar\nspam: baz".getBytes()) });
- factory.setDocumentMatchers(Arrays
- . asList(new DocumentMatcher() {
- @Override
- public MatchStatus matches(Properties properties) {
- if (!properties.containsKey("foo")) {
- return MatchStatus.ABSTAIN;
- }
- return "bag".equals(properties.getProperty("foo")) ? MatchStatus.FOUND
- : MatchStatus.NOT_FOUND;
- }
- }));
+ factory.setDocumentMatchers(new DocumentMatcher() {
+ @Override
+ public MatchStatus matches(Properties properties) {
+ if (!properties.containsKey("foo")) {
+ return MatchStatus.ABSTAIN;
+ }
+ return "bag".equals(properties.getProperty("foo")) ? MatchStatus.FOUND
+ : MatchStatus.NOT_FOUND;
+ }
+ });
Properties properties = factory.getObject();
- assertEquals("bag", properties.get("foo"));
- assertEquals("bad", properties.get("spam"));
- assertEquals("two", properties.get("one"));
+ assertThat(properties.getProperty("foo"), equalTo("bag"));
+ assertThat(properties.getProperty("spam"), equalTo("bad"));
+ assertThat(properties.getProperty("one"), equalTo("two"));
}
@Test
@@ -179,7 +197,7 @@ public class YamlPropertiesFactoryBeanTests {
factory.setResolutionMethod(ResolutionMethod.OVERRIDE_AND_IGNORE);
factory.setResources(new Resource[] { new ClassPathResource("no-such-file.yml") });
Properties properties = factory.getObject();
- assertEquals(0, properties.size());
+ assertThat(properties.size(), equalTo(0));
}
@Test
@@ -188,8 +206,8 @@ public class YamlPropertiesFactoryBeanTests {
factory.setResources(new Resource[] { new ByteArrayResource("foo: bar\nspam:"
.getBytes()) });
Properties properties = factory.getObject();
- assertEquals("bar", properties.get("foo"));
- assertEquals("", properties.get("spam"));
+ assertThat(properties.getProperty("foo"), equalTo("bar"));
+ assertThat(properties.getProperty("spam"), equalTo(""));
}
@Test
@@ -198,8 +216,8 @@ public class YamlPropertiesFactoryBeanTests {
factory.setResources(new Resource[] { new ByteArrayResource("foo:\n- bar\n- baz"
.getBytes()) });
Properties properties = factory.getObject();
- assertEquals("bar", properties.get("foo[0]"));
- assertEquals("baz", properties.get("foo[1]"));
+ assertThat(properties.getProperty("foo[0]"), equalTo("bar"));
+ assertThat(properties.getProperty("foo[1]"), equalTo("baz"));
}
@Test
@@ -209,10 +227,10 @@ public class YamlPropertiesFactoryBeanTests {
"foo:\n- bar:\n spam: crap\n- baz\n- one: two\n three: four"
.getBytes()) });
Properties properties = factory.getObject();
- assertEquals("crap", properties.get("foo[0].bar.spam"));
- assertEquals("baz", properties.get("foo[1]"));
- assertEquals("two", properties.get("foo[2].one"));
- assertEquals("four", properties.get("foo[2].three"));
+ assertThat(properties.getProperty("foo[0].bar.spam"), equalTo("crap"));
+ assertThat(properties.getProperty("foo[1]"), equalTo("baz"));
+ assertThat(properties.getProperty("foo[2].one"), equalTo("two"));
+ assertThat(properties.getProperty("foo[2].three"), equalTo("four"));
}
@SuppressWarnings("unchecked")
@@ -220,8 +238,9 @@ public class YamlPropertiesFactoryBeanTests {
public void testYaml() {
Yaml yaml = new Yaml();
Map map = yaml.loadAs("foo: bar\nspam:\n foo: baz", Map.class);
- assertEquals("bar", map.get("foo"));
- assertEquals("baz", ((Map) map.get("spam")).get("foo"));
+ assertThat(map.get("foo"), equalTo((Object) "bar"));
+ assertThat(((Map) map.get("spam")).get("foo"),
+ equalTo((Object) "baz"));
}
}
diff --git a/spring-boot/src/test/resources/activeprofilerepro.yml b/spring-boot/src/test/resources/activeprofilerepro.yml
new file mode 100644
index 0000000000..3871896a3b
--- /dev/null
+++ b/spring-boot/src/test/resources/activeprofilerepro.yml
@@ -0,0 +1,11 @@
+spring.profiles.active: B
+---
+spring.profiles: A
+version: A
+---
+spring.profiles: B
+version: B
+---
+spring.profiles: C
+version: C
+---