From 133f11df2fa25096aa3ee139daa67e945462177d Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 4 May 2017 21:58:58 -0700 Subject: [PATCH] Refactor ConfigFileApplicationListener Refactor `ConfigFileApplicationListener` to use a `MultiValueMap` when loading sources. The helps to simplify the code and removes the need for the inner `LoadedPropertySources` class. We're also able to delete the now unused `EnumerableCompositePropertySource` and `PropertySourcesLoader` classes. Fixes gh-9144 --- .../config/ConfigFileApplicationListener.java | 367 ++++++++---------- .../EnumerableCompositePropertySource.java | 78 ---- .../boot/env/PropertySourcesLoader.java | 210 ---------- .../ConfigFileApplicationListenerTests.java | 33 +- .../boot/env/PropertySourcesLoaderTests.java | 38 -- 5 files changed, 160 insertions(+), 566 deletions(-) delete mode 100644 spring-boot/src/main/java/org/springframework/boot/env/EnumerableCompositePropertySource.java delete mode 100644 spring-boot/src/main/java/org/springframework/boot/env/PropertySourcesLoader.java delete mode 100644 spring-boot/src/test/java/org/springframework/boot/env/PropertySourcesLoaderTests.java diff --git a/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java b/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java index 297ac295f0e..3b065e384b7 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java @@ -19,12 +19,13 @@ package org.springframework.boot.context.config; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; +import java.util.Map; import java.util.Queue; import java.util.Set; @@ -39,9 +40,8 @@ import org.springframework.boot.context.event.ApplicationPreparedEvent; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.boot.context.properties.bind.PropertySourcesPlaceholdersResolver; import org.springframework.boot.context.properties.source.ConfigurationPropertySources; -import org.springframework.boot.env.EnumerableCompositePropertySource; import org.springframework.boot.env.EnvironmentPostProcessor; -import org.springframework.boot.env.PropertySourcesLoader; +import org.springframework.boot.env.PropertySourceLoader; import org.springframework.boot.env.RandomValuePropertySource; import org.springframework.boot.logging.DeferredLog; import org.springframework.context.ApplicationEvent; @@ -51,10 +51,9 @@ import org.springframework.context.annotation.ConfigurationClassPostProcessor; import org.springframework.core.Ordered; import org.springframework.core.annotation.AnnotationAwareOrderComparator; import org.springframework.core.env.ConfigurableEnvironment; -import org.springframework.core.env.EnumerablePropertySource; +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.io.DefaultResourceLoader; import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; @@ -258,7 +257,6 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor, } private void reorderSources(ConfigurableEnvironment environment) { - LoadedPropertySources.finishAndRelocate(environment.getPropertySources()); PropertySource defaultProperties = environment.getPropertySources() .remove(DEFAULT_PROPERTIES); if (defaultProperties != null) { @@ -279,7 +277,7 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor, private final ResourceLoader resourceLoader; - private PropertySourcesLoader propertiesLoader; + private final List propertySourceLoaders; private Queue profiles; @@ -287,55 +285,61 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor, private boolean activatedProfiles; + private Map loaded; + Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) { this.environment = environment; this.resourceLoader = resourceLoader == null ? new DefaultResourceLoader() : resourceLoader; + this.propertySourceLoaders = SpringFactoriesLoader.loadFactories( + PropertySourceLoader.class, getClass().getClassLoader()); } public void load() { - this.propertiesLoader = new PropertySourcesLoader(); - this.activatedProfiles = false; this.profiles = Collections.asLifoQueue(new LinkedList()); this.processedProfiles = new LinkedList<>(); - - // Pre-existing active profiles set via Environment.setActiveProfiles() - // are additional profiles and config files are allowed to add more if - // they want to, so don't call addActiveProfiles() here. - Set initialActiveProfiles = initializeActiveProfiles(); - this.profiles.addAll(getUnprocessedActiveProfiles(initialActiveProfiles)); - if (this.profiles.isEmpty()) { - for (String defaultProfileName : this.environment.getDefaultProfiles()) { - Profile defaultProfile = new Profile(defaultProfileName, true); - if (!this.profiles.contains(defaultProfile)) { - this.profiles.add(defaultProfile); - } - } - } - - // The default profile for these purposes is represented as null. We add it - // last so that it is first out of the queue (active profiles will then - // override any settings in the defaults when the list is reversed later). - this.profiles.add(null); - + this.activatedProfiles = false; + this.loaded = new LinkedHashMap<>(); + initializeProfiles(); while (!this.profiles.isEmpty()) { Profile profile = this.profiles.poll(); for (String location : getSearchLocations()) { if (!location.endsWith("/")) { // location is a filename already, so don't search for more // filenames - load(location, null, profile); + load(profile, location, null); } else { for (String name : getSearchNames()) { - load(location, name, profile); + load(profile, location, name); } } } this.processedProfiles.add(profile); } + addLoadedPropertySources(); + } - addConfigurationProperties(this.propertiesLoader.getPropertySources()); + /** + * Initialize profile information from both the {@link Environment} active + * profiles and any {@code spring.profiles.active}/{@code spring.profiles.include} + * properties that are already set. + */ + private void initializeProfiles() { + Set initialActiveProfiles = initializeActiveProfiles(); + this.profiles.addAll(getUnprocessedActiveProfiles(initialActiveProfiles)); + if (this.profiles.isEmpty()) { + for (String defaultProfileName : this.environment.getDefaultProfiles()) { + Profile defaultProfile = new Profile(defaultProfileName, true); + if (!this.profiles.contains(defaultProfile)) { + this.profiles.add(defaultProfile); + } + } + } + // The default profile for these purposes is represented as null. We add it + // last so that it is first out of the queue (active profiles will then + // override any settings in the defaults when the list is reversed later). + this.profiles.add(null); } private Set initializeActiveProfiles() { @@ -345,11 +349,10 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor, } // Any pre-existing active profiles set via property sources (e.g. System // properties) take precedence over those added in config files. - Set active = getProfiles(this.environment, "spring.profiles.active"); - Set activeProfiles = new LinkedHashSet<>(active); - Set include = getProfiles(this.environment, - "spring.profiles.include"); - activeProfiles.addAll(include); + Binder binder = Binder.get(this.environment); + Set activeProfiles = new LinkedHashSet<>(); + activeProfiles.addAll(getProfiles(binder, ACTIVE_PROFILES_PROPERTY)); + activeProfiles.addAll(getProfiles(binder, INCLUDE_PROFILES_PROPERTY)); maybeActivateProfiles(activeProfiles); return activeProfiles; } @@ -381,117 +384,113 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor, return unprocessedActiveProfiles; } - private void load(String location, String name, Profile profile) { - String group = "profile=" + (profile == null ? "" : profile); + /** + * Load an actual property source file. + * @param profile the profile being loaded + * @param location the location of the resource + * @param name an optional name to be combined with the location + */ + private void load(Profile profile, String location, String name) { if (!StringUtils.hasText(name)) { - // Try to load directly from the location - loadIntoGroup(group, location, profile); - } - else { - // Search for a file with the given name - for (String ext : this.propertiesLoader.getAllFileExtensions()) { - if (profile != null) { - // Try the profile-specific file - loadIntoGroup(group, location + name + "-" + profile + "." + ext, - null); - for (Profile processedProfile : this.processedProfiles) { - if (processedProfile != null) { - loadIntoGroup(group, location + name + "-" - + processedProfile + "." + ext, profile); - } - } - // Sometimes people put "spring.profiles: dev" in - // application-dev.yml (gh-340). Arguably we should try and error - // out on that, but we can be kind and load it anyway. - loadIntoGroup(group, location + name + "-" + profile + "." + ext, - profile); + for (PropertySourceLoader loader : this.propertySourceLoaders) { + if (canLoadFileExtension(loader, location)) { + load(loader, profile, location, + (profile == null ? null : profile.getName())); } - // Also try the profile-specific section (if any) of the normal file - loadIntoGroup(group, location + name + "." + ext, profile); + } + } + for (PropertySourceLoader loader : this.propertySourceLoaders) { + for (String ext : loader.getFileExtensions()) { + loadForFileExtention(loader, profile, location + name, "." + ext); } } } - private PropertySource loadIntoGroup(String identifier, String location, - Profile profile) { - try { - return doLoadIntoGroup(identifier, location, profile); - } - catch (Exception ex) { - throw new IllegalStateException( - "Failed to load property source from location '" + location + "'", - ex); + private boolean canLoadFileExtension(PropertySourceLoader loader, String name) { + return Arrays.stream(loader.getFileExtensions()).map(String::toLowerCase) + .anyMatch(name.toLowerCase()::endsWith); + } + + private void loadForFileExtention(PropertySourceLoader loader, Profile profile, + String prefix, String ext) { + if (profile != null) { + // Try the profile-specific file + load(loader, profile, prefix + "-" + profile + ext, null); + // Support profile section in profile file (gh-340) + load(loader, profile, prefix + "-" + profile + ext, profile.getName()); + // Try profile specific sections in files we've already processed + for (Profile processedProfile : this.processedProfiles) { + if (processedProfile != null) { + String previouslyLoaded = prefix + "-" + processedProfile + ext; + load(loader, profile, previouslyLoaded, profile.getName()); + } + } } + // Also try the profile-specific section (if any) of the normal file + load(loader, profile, prefix + ext, + (profile == null ? null : profile.getName())); } - private PropertySource doLoadIntoGroup(String identifier, String location, - Profile profile) throws IOException { - Resource resource = this.resourceLoader.getResource(location); - PropertySource propertySource = null; - StringBuilder msg = new StringBuilder(); - if (resource != null && resource.exists()) { - String name = "applicationConfig: [" + location + "]"; - String group = "applicationConfig: [" + identifier + "]"; - propertySource = this.propertiesLoader.load(resource, group, name, - (profile == null ? null : profile.getName())); - if (propertySource != null) { - msg.append("Loaded "); - handleProfileProperties(propertySource); + private void load(PropertySourceLoader loader, Profile profile, String location, + String loadProfile) { + try { + Resource resource = this.resourceLoader.getResource(location); + String description = getDescription(profile, location, resource); + if (profile != null) { + description = description + " for profile " + profile; } - else { - msg.append("Skipped (empty) "); + if (resource == null || !resource.exists()) { + this.logger.trace("Skipped missing config " + description); + return; } + if (!StringUtils.hasText( + StringUtils.getFilenameExtension(resource.getFilename()))) { + this.logger.trace("Skipped empty config extension " + description); + return; + } + String name = "applicationConfig: [" + location + "]" + + (loadProfile == null ? "" : "#" + loadProfile); + PropertySource loaded = loader.load(name, resource, loadProfile); + if (loaded == null) { + this.logger.trace("Skipped unloaded config " + description); + return; + } + handleProfileProperties(loaded); + this.loaded.computeIfAbsent(profile, (k) -> new MutablePropertySources()) + .addLast(loaded); + this.logger.debug("Loaded config file " + description); } - else { - msg.append("Skipped "); - } - msg.append("config file "); - msg.append(getResourceDescription(location, resource)); - if (profile != null) { - msg.append(" for profile ").append(profile); - } - if (resource == null || !resource.exists()) { - msg.append(" resource not found"); - this.logger.trace(msg); - } - else { - this.logger.debug(msg); + catch (Exception ex) { + throw new IllegalStateException("Failed to load property " + + "source from location '" + location + "'", ex); } - return propertySource; } - private String getResourceDescription(String location, Resource resource) { - String resourceDescription = "'" + location + "'"; - if (resource != null) { - try { - resourceDescription = String.format("'%s' (%s)", - resource.getURI().toASCIIString(), location); - } - catch (IOException ex) { - // Use the location as the description + private String getDescription(Profile profile, String location, + Resource resource) { + try { + if (resource != null) { + String uri = resource.getURI().toASCIIString(); + return String.format("'%s' (%s)", uri, location); } } - return resourceDescription; + catch (IOException ex) { + } + return String.format("'%s'", location); } private void handleProfileProperties(PropertySource propertySource) { MutablePropertySources propertySources = new MutablePropertySources(); propertySources.addFirst(propertySource); - Set active = getProfiles(propertySources, "spring.profiles.active"); - Set include = getProfiles(propertySources, - "spring.profiles.include"); + Binder binder = new Binder(ConfigurationPropertySources.get(propertySources), + new PropertySourcesPlaceholdersResolver(this.environment)); + Set active = getProfiles(binder, "spring.profiles.active"); + Set include = getProfiles(binder, "spring.profiles.include"); maybeActivateProfiles(active); addProfiles(include); } - private Set getProfiles(ConfigurableEnvironment environment, - String name) { - return getProfiles(environment.getPropertySources(), name); - } - - private Set getProfiles(PropertySources sources, String name) { - Binder binder = new Binder(ConfigurationPropertySources.get(sources), - new PropertySourcesPlaceholdersResolver(this.environment)); + private Set getProfiles(Binder binder, String name) { return binder.bind(name, String[].class).map(this::asProfileSet) .orElse(Collections.emptySet()); } @@ -506,20 +505,19 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor, } private void maybeActivateProfiles(Set profiles) { - if (this.activatedProfiles) { - if (!profiles.isEmpty()) { - this.logger.debug("Profiles already activated, '" + profiles - + "' will not be applied"); - } + if (profiles.isEmpty()) { return; } - if (!profiles.isEmpty()) { - addProfiles(profiles); - this.logger.debug("Activated profiles " - + StringUtils.collectionToCommaDelimitedString(profiles)); - this.activatedProfiles = true; - removeUnprocessedDefaultProfiles(); + if (this.activatedProfiles) { + this.logger.debug("Profiles already activated, '" + profiles + + "' will not be applied"); + return; } + addProfiles(profiles); + this.logger.debug("Activated profiles " + + StringUtils.collectionToCommaDelimitedString(profiles)); + this.activatedProfiles = true; + removeUnprocessedDefaultProfiles(); } private void removeUnprocessedDefaultProfiles() { @@ -584,8 +582,8 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor, private Set getSearchNames() { if (this.environment.containsProperty(CONFIG_NAME_PROPERTY)) { - return asResolvedSet(this.environment.getProperty(CONFIG_NAME_PROPERTY), - null); + String property = this.environment.getProperty(CONFIG_NAME_PROPERTY); + return asResolvedSet(property, null); } return asResolvedSet(ConfigFileApplicationListener.this.names, DEFAULT_NAMES); } @@ -598,22 +596,26 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor, return new LinkedHashSet<>(list); } - private void addConfigurationProperties(MutablePropertySources sources) { - List> reorderedSources = new ArrayList<>(); - for (PropertySource item : sources) { - reorderedSources.add(item); - } - addConfigurationProperties(new LoadedPropertySources(reorderedSources)); - } - - private void addConfigurationProperties(LoadedPropertySources loadedSources) { - MutablePropertySources existingSources = this.environment - .getPropertySources(); - if (existingSources.contains(DEFAULT_PROPERTIES)) { - existingSources.addBefore(DEFAULT_PROPERTIES, loadedSources); - } - else { - existingSources.addLast(loadedSources); + private void addLoadedPropertySources() { + MutablePropertySources destination = this.environment.getPropertySources(); + String lastAdded = null; + List loaded = new ArrayList<>(this.loaded.values()); + Collections.reverse(loaded); + for (MutablePropertySources sources : loaded) { + for (PropertySource source : sources) { + if (lastAdded == null) { + if (destination.contains(DEFAULT_PROPERTIES)) { + destination.addBefore(DEFAULT_PROPERTIES, source); + } + else { + destination.addLast(source); + } + } + else { + destination.addAfter(lastAdded, source); + } + lastAdded = source.getName(); + } } } @@ -666,67 +668,4 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor, } - /** - * Holds the configuration {@link PropertySource}s as they are loaded can relocate - * them once configuration classes have been processed. - */ - static class LoadedPropertySources - extends EnumerablePropertySource>> { - - private final Collection> sources; - - private final String[] names; - - LoadedPropertySources(Collection> sources) { - super(APPLICATION_CONFIGURATION_PROPERTY_SOURCE_NAME, sources); - this.sources = sources; - List names = new ArrayList<>(); - for (PropertySource source : sources) { - if (source instanceof EnumerablePropertySource) { - names.addAll(Arrays.asList( - ((EnumerablePropertySource) source).getPropertyNames())); - } - } - this.names = names.toArray(new String[names.size()]); - } - - @Override - public Object getProperty(String name) { - for (PropertySource propertySource : this.sources) { - Object value = propertySource.getProperty(name); - if (value != null) { - return value; - } - } - return null; - } - - public static void finishAndRelocate(MutablePropertySources propertySources) { - String name = APPLICATION_CONFIGURATION_PROPERTY_SOURCE_NAME; - LoadedPropertySources removed = (LoadedPropertySources) propertySources - .get(name); - if (removed != null) { - for (PropertySource propertySource : removed.sources) { - if (propertySource instanceof EnumerableCompositePropertySource) { - EnumerableCompositePropertySource composite = (EnumerableCompositePropertySource) propertySource; - for (PropertySource nested : composite.getSource()) { - propertySources.addAfter(name, nested); - name = nested.getName(); - } - } - else { - propertySources.addAfter(name, propertySource); - } - } - propertySources.remove(APPLICATION_CONFIGURATION_PROPERTY_SOURCE_NAME); - } - } - - @Override - public String[] getPropertyNames() { - return this.names; - } - - } - } diff --git a/spring-boot/src/main/java/org/springframework/boot/env/EnumerableCompositePropertySource.java b/spring-boot/src/main/java/org/springframework/boot/env/EnumerableCompositePropertySource.java deleted file mode 100644 index a6f8ac04bec..00000000000 --- a/spring-boot/src/main/java/org/springframework/boot/env/EnumerableCompositePropertySource.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * 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.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.LinkedHashSet; -import java.util.List; - -import org.springframework.core.env.EnumerablePropertySource; -import org.springframework.core.env.PropertySource; - -/** - * An mutable, enumerable, composite property source. New sources are added last (and - * hence resolved with lowest priority). - * - * @author Dave Syer - * @see PropertySource - * @see EnumerablePropertySource - */ -public class EnumerableCompositePropertySource - extends EnumerablePropertySource>> { - - private volatile String[] names; - - public EnumerableCompositePropertySource(String sourceName) { - super(sourceName, new LinkedHashSet>()); - } - - @Override - public Object getProperty(String name) { - for (PropertySource propertySource : getSource()) { - Object value = propertySource.getProperty(name); - if (value != null) { - return value; - } - } - return null; - } - - @Override - public String[] getPropertyNames() { - String[] result = this.names; - if (result == null) { - List names = new ArrayList<>(); - for (PropertySource source : new ArrayList<>(getSource())) { - if (source instanceof EnumerablePropertySource) { - names.addAll(Arrays.asList( - ((EnumerablePropertySource) source).getPropertyNames())); - } - } - this.names = names.toArray(new String[0]); - result = this.names; - } - return result; - } - - public void add(PropertySource source) { - getSource().add(source); - this.names = null; - } - -} 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 deleted file mode 100644 index 938016368f5..00000000000 --- a/spring-boot/src/main/java/org/springframework/boot/env/PropertySourcesLoader.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright 2012-2017 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * 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.LinkedHashSet; -import java.util.List; -import java.util.Set; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -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; -import org.springframework.util.StringUtils; - -/** - * Utility that can be used to update {@link MutablePropertySources} using - * {@link PropertySourceLoader PropertySourceLoaders}. - * - * @author Phillip Webb - */ -public class PropertySourcesLoader { - - private static final Log logger = LogFactory.getLog(PropertySourcesLoader.class); - - 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, - getClass().getClassLoader()); - } - - /** - * Load the specified resource (if possible) and add it as the first source. - * @param resource the source resource (may be {@code null}). - * @return the loaded property source or {@code null} - * @throws IOException if the source cannot be loaded - */ - public PropertySource load(Resource resource) throws IOException { - return load(resource, null); - } - - /** - * Load the profile-specific properties from the specified resource (if any) and add - * it as the first source. - * @param resource the source resource (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 if the source cannot be loaded - */ - public PropertySource load(Resource resource, String profile) throws IOException { - return load(resource, resource.getDescription(), profile); - } - - /** - * Load the profile-specific properties from the specified resource (if any), give the - * name provided 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 if the source cannot be loaded - */ - public PropertySource load(Resource resource, String name, String profile) - throws IOException { - return load(resource, null, name, profile); - } - - /** - * Load the profile-specific properties from the specified resource (if any), give the - * name provided and add it to a group of property sources identified by the group - * name. Property sources are added to the end of a group, but new groups are added as - * the first in the chain being assembled. This means the normal sequence of calls is - * to first create the group for the default (null) profile, and then add specific - * groups afterwards (with the highest priority last). Property resolution from the - * resulting sources will consider all keys for a given group first and then move to - * the next group. - * @param resource the source resource (may be {@code null}). - * @param group an identifier for the group that this source belongs to - * @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 if the source cannot be loaded - */ - public PropertySource load(Resource resource, String group, String name, - String profile) throws IOException { - if (isFile(resource)) { - String sourceName = generatePropertySourceName(name, profile); - for (PropertySourceLoader loader : this.loaders) { - if (canLoadFileExtension(loader, resource)) { - PropertySource specific = loader.load(sourceName, resource, - profile); - addPropertySource(group, specific, profile); - return specific; - } - } - } - return null; - } - - private boolean isFile(Resource resource) { - return resource != null && resource.exists() && StringUtils - .hasText(StringUtils.getFilenameExtension(resource.getFilename())); - } - - private String generatePropertySourceName(String name, String profile) { - 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(String basename, PropertySource source, - String profile) { - - if (source == null) { - return; - } - - if (basename == null) { - this.propertySources.addLast(source); - return; - } - - EnumerableCompositePropertySource group = getGeneric(basename); - group.add(source); - logger.trace("Adding PropertySource: " + source + " in group: " + basename); - if (this.propertySources.contains(group.getName())) { - this.propertySources.replace(group.getName(), group); - } - else { - this.propertySources.addFirst(group); - } - - } - - private EnumerableCompositePropertySource getGeneric(String name) { - PropertySource source = this.propertySources.get(name); - if (source instanceof EnumerableCompositePropertySource) { - return (EnumerableCompositePropertySource) source; - } - EnumerableCompositePropertySource composite = new EnumerableCompositePropertySource( - name); - return composite; - } - - /** - * Return the {@link MutablePropertySources} being loaded. - * @return the property sources - */ - public MutablePropertySources getPropertySources() { - return this.propertySources; - } - - /** - * Returns all file extensions that could be loaded. - * @return the file extensions - */ - public Set getAllFileExtensions() { - Set fileExtensions = new LinkedHashSet<>(); - for (PropertySourceLoader loader : this.loaders) { - fileExtensions.addAll(Arrays.asList(loader.getFileExtensions())); - } - return fileExtensions; - } - -} diff --git a/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java b/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java index 11aa788a2d6..413bbb03e97 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigFileApplicationListenerTests.java @@ -21,12 +21,13 @@ import java.io.FileOutputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; @@ -40,10 +41,8 @@ import org.junit.rules.ExpectedException; import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; -import org.springframework.boot.context.config.ConfigFileApplicationListener.LoadedPropertySources; import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent; import org.springframework.boot.context.event.ApplicationPreparedEvent; -import org.springframework.boot.env.EnumerableCompositePropertySource; import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.boot.testutil.InternalOutputCapture; import org.springframework.context.ConfigurableApplicationContext; @@ -55,7 +54,6 @@ import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MapPropertySource; -import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.SimpleCommandLinePropertySource; import org.springframework.core.env.StandardEnvironment; import org.springframework.core.io.ByteArrayResource; @@ -512,24 +510,10 @@ public class ConfigFileApplicationListenerTests { String property = this.environment.getProperty("my.property"); assertThat(this.environment.getActiveProfiles()).contains("dev"); assertThat(property).isEqualTo("fromdevprofile"); - LoadedPropertySources propertySource = (LoadedPropertySources) this.environment - .getPropertySources() - .get(ConfigFileApplicationListener.APPLICATION_CONFIGURATION_PROPERTY_SOURCE_NAME); - Collection> sources = propertySource - .getSource(); - assertThat(sources).hasSize(2); - List names = new ArrayList<>(); - for (org.springframework.core.env.PropertySource source : sources) { - if (source instanceof EnumerableCompositePropertySource) { - for (org.springframework.core.env.PropertySource nested : ((EnumerableCompositePropertySource) source) - .getSource()) { - names.add(nested.getName()); - } - } - else { - names.add(source.getName()); - } - } + List names = StreamSupport + .stream(this.environment.getPropertySources().spliterator(), false) + .map(org.springframework.core.env.PropertySource::getName) + .collect(Collectors.toList()); assertThat(names).contains( "applicationConfig: [classpath:/testsetprofiles.yml]#dev", "applicationConfig: [classpath:/testsetprofiles.yml]"); @@ -846,10 +830,7 @@ public class ConfigFileApplicationListenerTests { @Override public boolean matches(ConfigurableEnvironment value) { - MutablePropertySources sources = new MutablePropertySources( - value.getPropertySources()); - LoadedPropertySources.finishAndRelocate(sources); - return sources.contains(sourceName); + return value.getPropertySources().contains(sourceName); } }; diff --git a/spring-boot/src/test/java/org/springframework/boot/env/PropertySourcesLoaderTests.java b/spring-boot/src/test/java/org/springframework/boot/env/PropertySourcesLoaderTests.java deleted file mode 100644 index bb4b83691c4..00000000000 --- a/spring-boot/src/test/java/org/springframework/boot/env/PropertySourcesLoaderTests.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2012-2016 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 org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests for {@link PropertySourcesLoader}. - * - * @author Dave Syer - */ -public class PropertySourcesLoaderTests { - - private PropertySourcesLoader loader = new PropertySourcesLoader(); - - @Test - public void fileExtensions() { - assertThat(this.loader.getAllFileExtensions()).containsOnly("yml", "yaml", - "properties", "xml"); - } - -}