Rationalize multi-document config file handling
Update `PropertySourceLoader` so that it no longer needs to deal with matching multi-document files using the `spring.profile` property. The loader now simply returns one or more `PropertSource` instances for a given `Resource`. All property matching now occurs in the `ConfigFileApplicationListener`. This allows document processing logic to be contained in a single place, and allows us to rationalize the algorithm so that negative matching profiles are processed last. Fixes gh-12159
This commit is contained in:
parent
3d8f760ea0
commit
c0d79b9273
|
@ -22,7 +22,6 @@ import org.springframework.boot.SpringApplication;
|
|||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.boot.env.YamlPropertySourceLoader;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
@ -41,17 +40,16 @@ public class EnvironmentPostProcessorExample implements EnvironmentPostProcessor
|
|||
public void postProcessEnvironment(ConfigurableEnvironment environment,
|
||||
SpringApplication application) {
|
||||
Resource path = new ClassPathResource("com/example/myapp/config.yml");
|
||||
PropertySource<?> propertySource = loadYaml(path, environment);
|
||||
PropertySource<?> propertySource = loadYaml(path);
|
||||
environment.getPropertySources().addLast(propertySource);
|
||||
}
|
||||
|
||||
private PropertySource<?> loadYaml(Resource path, Environment environment) {
|
||||
private PropertySource<?> loadYaml(Resource path) {
|
||||
if (!path.exists()) {
|
||||
throw new IllegalArgumentException("Resource " + path + " does not exist");
|
||||
}
|
||||
try {
|
||||
return this.loader.load("custom-resource", path, null,
|
||||
environment::acceptsProfiles);
|
||||
return this.loader.load("custom-resource", path).get(0);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException(
|
||||
|
|
|
@ -180,10 +180,10 @@ public class PropertiesMigrationReporterTests {
|
|||
private PropertySource<?> loadPropertySource(String name, String path)
|
||||
throws IOException {
|
||||
ClassPathResource resource = new ClassPathResource(path);
|
||||
PropertySource<?> propertySource = new PropertiesPropertySourceLoader().load(name,
|
||||
resource, null, (profile) -> true);
|
||||
assertThat(propertySource).isNotNull();
|
||||
return propertySource;
|
||||
List<PropertySource<?>> propertySources = new PropertiesPropertySourceLoader()
|
||||
.load(name, resource);
|
||||
assertThat(propertySources).isNotEmpty();
|
||||
return propertySources.get(0);
|
||||
}
|
||||
|
||||
private ConfigurationMetadataRepository loadRepository(String... content) {
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.io.IOException;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.LinkedList;
|
||||
|
@ -27,6 +28,8 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
|
@ -36,6 +39,7 @@ import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
|||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent;
|
||||
import org.springframework.boot.context.event.ApplicationPreparedEvent;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.context.properties.bind.PropertySourcesPlaceholdersResolver;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
|
||||
|
@ -58,6 +62,8 @@ import org.springframework.core.io.Resource;
|
|||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.SpringFactoriesLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
@ -101,6 +107,8 @@ public class ConfigFileApplicationListener
|
|||
|
||||
private static final String DEFAULT_NAMES = "application";
|
||||
|
||||
private static final Set<String> NO_SEARCH_NAMES = Collections.singleton(null);
|
||||
|
||||
/**
|
||||
* The "active profiles" property name.
|
||||
*/
|
||||
|
@ -302,6 +310,8 @@ public class ConfigFileApplicationListener
|
|||
|
||||
private Map<Profile, MutablePropertySources> loaded;
|
||||
|
||||
private Map<DocumentsCacheKey, List<Document>> loadDocumentsCache = new HashMap<>();
|
||||
|
||||
Loader(ConfigurableEnvironment environment, ResourceLoader resourceLoader) {
|
||||
this.environment = environment;
|
||||
this.resourceLoader = resourceLoader == null ? new DefaultResourceLoader()
|
||||
|
@ -318,20 +328,12 @@ public class ConfigFileApplicationListener
|
|||
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(profile, location, null);
|
||||
}
|
||||
else {
|
||||
for (String name : getSearchNames()) {
|
||||
load(profile, location, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
load(profile, this::getPositiveProfileFilter,
|
||||
addToLoaded(MutablePropertySources::addLast, false));
|
||||
this.processedProfiles.add(profile);
|
||||
}
|
||||
load(null, this::getNegativeProfileFilter,
|
||||
addToLoaded(MutablePropertySources::addFirst, true));
|
||||
addLoadedPropertySources();
|
||||
}
|
||||
|
||||
|
@ -399,24 +401,66 @@ public class ConfigFileApplicationListener
|
|||
return unprocessedActiveProfiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
private DocumentFilter getPositiveProfileFilter(Profile profile) {
|
||||
return (Document document) -> {
|
||||
if (profile == null) {
|
||||
return ObjectUtils.isEmpty(document.getProfiles());
|
||||
}
|
||||
return ObjectUtils.containsElement(document.getProfiles(),
|
||||
profile.getName())
|
||||
&& this.environment.acceptsProfiles(document.getProfiles());
|
||||
};
|
||||
}
|
||||
|
||||
private DocumentFilter getNegativeProfileFilter(Profile profile) {
|
||||
return (Document document) -> (profile == null
|
||||
&& !ObjectUtils.isEmpty(document.getProfiles())
|
||||
&& this.environment.acceptsProfiles(document.getProfiles()));
|
||||
}
|
||||
|
||||
private DocumentConsumer addToLoaded(
|
||||
BiConsumer<MutablePropertySources, PropertySource<?>> addMethod,
|
||||
boolean checkForExisting) {
|
||||
return (profile, document) -> {
|
||||
if (checkForExisting) {
|
||||
for (MutablePropertySources merged : this.loaded.values()) {
|
||||
if (merged.contains(document.getPropertySource().getName())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
MutablePropertySources merged = this.loaded.computeIfAbsent(profile,
|
||||
(k) -> new MutablePropertySources());
|
||||
addMethod.accept(merged, document.getPropertySource());
|
||||
};
|
||||
}
|
||||
|
||||
private void load(Profile profile, DocumentFilterFactory filterFactory,
|
||||
DocumentConsumer consumer) {
|
||||
getSearchLocations().forEach((location) -> {
|
||||
boolean isFolder = location.endsWith("/");
|
||||
Set<String> names = (isFolder ? getSearchNames() : NO_SEARCH_NAMES);
|
||||
names.forEach(
|
||||
(name) -> load(location, name, profile, filterFactory, consumer));
|
||||
});
|
||||
}
|
||||
|
||||
private void load(String location, String name, Profile profile,
|
||||
DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
|
||||
if (!StringUtils.hasText(name)) {
|
||||
for (PropertySourceLoader loader : this.propertySourceLoaders) {
|
||||
if (canLoadFileExtension(loader, location)) {
|
||||
load(loader, profile, location,
|
||||
(profile == null ? null : profile.getName()));
|
||||
load(loader, location, profile,
|
||||
filterFactory.getDocumentFilter(profile), consumer);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (PropertySourceLoader loader : this.propertySourceLoaders) {
|
||||
for (String ext : loader.getFileExtensions()) {
|
||||
loadForFileExtension(loader, profile, location + name, "." + ext);
|
||||
for (String fileExtension : loader.getFileExtensions()) {
|
||||
String prefix = location + name;
|
||||
fileExtension = "." + fileExtension;
|
||||
loadForFileExtension(loader, prefix, fileExtension, profile,
|
||||
filterFactory, consumer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -427,28 +471,31 @@ public class ConfigFileApplicationListener
|
|||
fileExtension));
|
||||
}
|
||||
|
||||
private void loadForFileExtension(PropertySourceLoader loader, Profile profile,
|
||||
String prefix, String ext) {
|
||||
private void loadForFileExtension(PropertySourceLoader loader, String prefix,
|
||||
String fileExtension, Profile profile,
|
||||
DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
|
||||
DocumentFilter defaultFilter = filterFactory.getDocumentFilter(null);
|
||||
DocumentFilter profileFilter = filterFactory.getDocumentFilter(profile);
|
||||
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 file & profile section in profile file (gh-340)
|
||||
String profileSpecificFile = prefix + "-" + profile + fileExtension;
|
||||
load(loader, profileSpecificFile, profile, defaultFilter, consumer);
|
||||
load(loader, profileSpecificFile, profile, profileFilter, consumer);
|
||||
// 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());
|
||||
String previouslyLoaded = prefix + "-" + processedProfile
|
||||
+ fileExtension;
|
||||
load(loader, previouslyLoaded, profile, profileFilter, consumer);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Also try the profile-specific section (if any) of the normal file
|
||||
load(loader, profile, prefix + ext,
|
||||
(profile == null ? null : profile.getName()));
|
||||
load(loader, prefix + fileExtension, profile, profileFilter, consumer);
|
||||
}
|
||||
|
||||
private void load(PropertySourceLoader loader, Profile profile, String location,
|
||||
String loadProfile) {
|
||||
private void load(PropertySourceLoader loader, String location, Profile profile,
|
||||
DocumentFilter filter, DocumentConsumer consumer) {
|
||||
try {
|
||||
Resource resource = this.resourceLoader.getResource(location);
|
||||
String description = getDescription(location, resource);
|
||||
|
@ -464,18 +511,25 @@ public class ConfigFileApplicationListener
|
|||
this.logger.trace("Skipped empty config extension " + description);
|
||||
return;
|
||||
}
|
||||
String name = "applicationConfig: [" + location + "]"
|
||||
+ (loadProfile == null ? "" : "#" + loadProfile);
|
||||
PropertySource<?> loaded = loader.load(name, resource, loadProfile,
|
||||
this.environment::acceptsProfiles);
|
||||
if (loaded == null) {
|
||||
String name = "applicationConfig: [" + location + "]";
|
||||
List<Document> documents = loadDocuments(loader, name, resource);
|
||||
if (CollectionUtils.isEmpty(documents)) {
|
||||
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);
|
||||
List<Document> loaded = new ArrayList<>();
|
||||
for (Document document : documents) {
|
||||
if (filter.match(document)) {
|
||||
maybeActivateProfiles(document.getActiveProfiles());
|
||||
addProfiles(document.getIncludeProfiles());
|
||||
loaded.add(document);
|
||||
}
|
||||
}
|
||||
Collections.reverse(loaded);
|
||||
if (!loaded.isEmpty()) {
|
||||
loaded.forEach((document) -> consumer.accept(profile, document));
|
||||
this.logger.debug("Loaded config file " + description);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Failed to load property "
|
||||
|
@ -483,6 +537,34 @@ public class ConfigFileApplicationListener
|
|||
}
|
||||
}
|
||||
|
||||
private List<Document> loadDocuments(PropertySourceLoader loader, String name,
|
||||
Resource resource) throws IOException {
|
||||
loader.load(name, resource);
|
||||
DocumentsCacheKey cacheKey = new DocumentsCacheKey(loader, resource);
|
||||
List<Document> documents = this.loadDocumentsCache.get(cacheKey);
|
||||
if (documents == null) {
|
||||
List<PropertySource<?>> loaded = loader.load(name, resource);
|
||||
documents = asDocuments(loaded);
|
||||
}
|
||||
return documents;
|
||||
}
|
||||
|
||||
private List<Document> asDocuments(List<PropertySource<?>> loaded) {
|
||||
if (loaded == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return loaded.stream().map((propertySource) -> {
|
||||
Binder binder = new Binder(
|
||||
ConfigurationPropertySources.from(propertySource),
|
||||
new PropertySourcesPlaceholdersResolver(this.environment));
|
||||
return new Document(propertySource,
|
||||
binder.bind("spring.profiles", Bindable.of(String[].class))
|
||||
.orElse(null),
|
||||
getProfiles(binder, ACTIVE_PROFILES_PROPERTY),
|
||||
getProfiles(binder, INCLUDE_PROFILES_PROPERTY));
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private String getDescription(String location, Resource resource) {
|
||||
try {
|
||||
if (resource != null) {
|
||||
|
@ -495,15 +577,6 @@ public class ConfigFileApplicationListener
|
|||
return String.format("'%s'", location);
|
||||
}
|
||||
|
||||
private void handleProfileProperties(PropertySource<?> propertySource) {
|
||||
Binder binder = new Binder(ConfigurationPropertySources.from(propertySource),
|
||||
new PropertySourcesPlaceholdersResolver(this.environment));
|
||||
Set<Profile> active = getProfiles(binder, "spring.profiles.active");
|
||||
Set<Profile> include = getProfiles(binder, "spring.profiles.include");
|
||||
maybeActivateProfiles(active);
|
||||
addProfiles(include);
|
||||
}
|
||||
|
||||
private Set<Profile> getProfiles(Binder binder, String name) {
|
||||
return binder.bind(name, String[].class).map(this::asProfileSet)
|
||||
.orElse(Collections.emptySet());
|
||||
|
@ -638,6 +711,9 @@ public class ConfigFileApplicationListener
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* A Spring Profile that can be loaded.
|
||||
*/
|
||||
private static class Profile {
|
||||
|
||||
private final String name;
|
||||
|
@ -685,4 +761,117 @@ public class ConfigFileApplicationListener
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache key used to save loading the same document multiple times.
|
||||
*/
|
||||
private static class DocumentsCacheKey {
|
||||
|
||||
private final PropertySourceLoader loader;
|
||||
|
||||
private final Resource resource;
|
||||
|
||||
DocumentsCacheKey(PropertySourceLoader loader, Resource resource) {
|
||||
this.loader = loader;
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.loader.hashCode() * 31 + this.resource.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
DocumentsCacheKey other = (DocumentsCacheKey) obj;
|
||||
return this.loader.equals(other.loader)
|
||||
&& this.resource.equals(other.resource);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A single document loaded by a {@link PropertySourceLoader}.
|
||||
*/
|
||||
private static class Document {
|
||||
|
||||
private final PropertySource<?> propertySource;
|
||||
|
||||
private String[] profiles;
|
||||
|
||||
private final Set<Profile> activeProfiles;
|
||||
|
||||
private final Set<Profile> includeProfiles;
|
||||
|
||||
Document(PropertySource<?> propertySource, String[] profiles,
|
||||
Set<Profile> activeProfiles, Set<Profile> includeProfiles) {
|
||||
this.propertySource = propertySource;
|
||||
this.profiles = profiles;
|
||||
this.activeProfiles = activeProfiles;
|
||||
this.includeProfiles = includeProfiles;
|
||||
}
|
||||
|
||||
public PropertySource<?> getPropertySource() {
|
||||
return this.propertySource;
|
||||
}
|
||||
|
||||
public String[] getProfiles() {
|
||||
return this.profiles;
|
||||
}
|
||||
|
||||
public Set<Profile> getActiveProfiles() {
|
||||
return this.activeProfiles;
|
||||
}
|
||||
|
||||
public Set<Profile> getIncludeProfiles() {
|
||||
return this.includeProfiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.propertySource.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory used to create a {@link DocumentFilter}.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
private interface DocumentFilterFactory {
|
||||
|
||||
/**
|
||||
* Create a filter for the given profile.
|
||||
* @param profile the profile or {@code null}
|
||||
* @return the filter
|
||||
*/
|
||||
DocumentFilter getDocumentFilter(Profile profile);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter used to restrict when a {@link Document} is loaded.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
private interface DocumentFilter {
|
||||
|
||||
boolean match(Document document);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumer used to handle a loaded {@link Document}.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
private interface DocumentConsumer {
|
||||
|
||||
void accept(Profile profile, Document document);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2018 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.function.Predicate;
|
||||
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* {@link SpringProfilesDocumentMatcher} that tests if a profile is accepted.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class AcceptsProfilesDocumentMatcher extends SpringProfilesDocumentMatcher {
|
||||
|
||||
private final Predicate<String[]> acceptsProfiles;
|
||||
|
||||
AcceptsProfilesDocumentMatcher(Predicate<String[]> acceptsProfiles) {
|
||||
this.acceptsProfiles = acceptsProfiles;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matches(String[] profiles) {
|
||||
return ObjectUtils.isEmpty(profiles) || this.acceptsProfiles.test(profiles);
|
||||
}
|
||||
|
||||
}
|
|
@ -16,9 +16,9 @@
|
|||
|
||||
package org.springframework.boot.env;
|
||||
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -53,11 +53,8 @@ class OriginTrackedYamlLoader extends YamlProcessor {
|
|||
|
||||
private final Resource resource;
|
||||
|
||||
OriginTrackedYamlLoader(Resource resource, String profileToLoad,
|
||||
Predicate<String[]> acceptsProfiles) {
|
||||
OriginTrackedYamlLoader(Resource resource) {
|
||||
this.resource = resource;
|
||||
setDocumentMatchers(new ProfileToLoadDocumentMatcher(profileToLoad),
|
||||
new AcceptsProfilesDocumentMatcher(acceptsProfiles));
|
||||
setResources(resource);
|
||||
}
|
||||
|
||||
|
@ -70,9 +67,11 @@ class OriginTrackedYamlLoader extends YamlProcessor {
|
|||
return new Yaml(constructor, representer, dumperOptions, resolver);
|
||||
}
|
||||
|
||||
public Map<String, Object> load() {
|
||||
final Map<String, Object> result = new LinkedHashMap<>();
|
||||
process((properties, map) -> result.putAll(getFlattenedMap(map)));
|
||||
public List<Map<String, Object>> load() {
|
||||
final List<Map<String, Object>> result = new ArrayList<>();
|
||||
process((properties, map) -> {
|
||||
result.add(getFlattenedMap(map));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2018 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.Arrays;
|
||||
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* {@link SpringProfilesDocumentMatcher} that matches a specific profile to load.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ProfileToLoadDocumentMatcher extends SpringProfilesDocumentMatcher {
|
||||
|
||||
private final String profile;
|
||||
|
||||
ProfileToLoadDocumentMatcher(String profile) {
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean matches(String[] profiles) {
|
||||
String[] positiveProfiles = (profiles == null ? null : Arrays.stream(profiles)
|
||||
.filter(this::isPositiveProfile).toArray(String[]::new));
|
||||
if (this.profile == null) {
|
||||
return ObjectUtils.isEmpty(positiveProfiles);
|
||||
}
|
||||
return ObjectUtils.containsElement(positiveProfiles, this.profile);
|
||||
}
|
||||
|
||||
private boolean isPositiveProfile(String profile) {
|
||||
return !profile.startsWith("!");
|
||||
}
|
||||
|
||||
}
|
|
@ -17,8 +17,9 @@
|
|||
package org.springframework.boot.env;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
@ -41,15 +42,14 @@ public class PropertiesPropertySourceLoader implements PropertySourceLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PropertySource<?> load(String name, Resource resource, String profileToLoad,
|
||||
Predicate<String[]> acceptsProfiles) throws IOException {
|
||||
if (profileToLoad == null) {
|
||||
Map<String, ?> properties = loadProperties(resource);
|
||||
if (!properties.isEmpty()) {
|
||||
return new OriginTrackedMapPropertySource(name, properties);
|
||||
}
|
||||
public List<PropertySource<?>> load(String name, Resource resource)
|
||||
throws IOException {
|
||||
Map<String, ?> properties = loadProperties(resource);
|
||||
if (properties.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return null;
|
||||
return Collections
|
||||
.singletonList(new OriginTrackedMapPropertySource(name, properties));
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
package org.springframework.boot.env;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
@ -39,17 +39,15 @@ public interface PropertySourceLoader {
|
|||
String[] getFileExtensions();
|
||||
|
||||
/**
|
||||
* Load the resource into a property source.
|
||||
* @param name the name of the property source
|
||||
* Load the resource into one or more property sources. Implementations may either
|
||||
* return a list containing a single source, or in the case of a multi-document format
|
||||
* such as yaml a source or each document in the resource.
|
||||
* @param name the root name of the property source. If multiple documents are loaded
|
||||
* an additional suffix should be added to the name for each source loaded.
|
||||
* @param resource the resource to load
|
||||
* @param profileToLoad 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.
|
||||
* @param acceptsProfiles predicate to determine if a particular profile is accepted
|
||||
* @return a property source or {@code null}
|
||||
* @return a list property sources
|
||||
* @throws IOException if the source cannot be loaded
|
||||
*/
|
||||
PropertySource<?> load(String name, Resource resource, String profileToLoad,
|
||||
Predicate<String[]> acceptsProfiles) throws IOException;
|
||||
List<PropertySource<?>> load(String name, Resource resource) throws IOException;
|
||||
|
||||
}
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2018 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.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.springframework.beans.factory.config.YamlProcessor.DocumentMatcher;
|
||||
import org.springframework.beans.factory.config.YamlProcessor.MatchStatus;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationProperty;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
|
||||
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
|
||||
import org.springframework.boot.origin.OriginTrackedValue;
|
||||
|
||||
/**
|
||||
* Base class for {@link DocumentMatcher DocumentMatchers} that check the
|
||||
* {@code spring.profiles} property.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
* @see OriginTrackedYamlLoader
|
||||
*/
|
||||
abstract class SpringProfilesDocumentMatcher implements DocumentMatcher {
|
||||
|
||||
@Override
|
||||
public final MatchStatus matches(Properties properties) {
|
||||
Binder binder = new Binder(
|
||||
new OriginTrackedValueConfigurationPropertySource(properties));
|
||||
String[] profiles = binder.bind("spring.profiles", Bindable.of(String[].class))
|
||||
.orElse(null);
|
||||
return (matches(profiles) ? MatchStatus.ABSTAIN : MatchStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
protected abstract boolean matches(String[] profiles);
|
||||
|
||||
/**
|
||||
* {@link MapConfigurationPropertySource} that deals with unwrapping
|
||||
* {@link OriginTrackedValue OriginTrackedValues} from the underlying map.
|
||||
*/
|
||||
static class OriginTrackedValueConfigurationPropertySource
|
||||
extends MapConfigurationPropertySource {
|
||||
|
||||
OriginTrackedValueConfigurationPropertySource(Map<?, ?> map) {
|
||||
super(map);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigurationProperty getConfigurationProperty(
|
||||
ConfigurationPropertyName name) {
|
||||
ConfigurationProperty property = super.getConfigurationProperty(name);
|
||||
if (property != null && property.getValue() instanceof OriginTrackedValue) {
|
||||
OriginTrackedValue originTrackedValue = (OriginTrackedValue) property
|
||||
.getValue();
|
||||
property = new ConfigurationProperty(property.getName(),
|
||||
originTrackedValue.getValue(), originTrackedValue.getOrigin());
|
||||
}
|
||||
return property;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -17,8 +17,10 @@
|
|||
package org.springframework.boot.env;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
@ -39,18 +41,23 @@ public class YamlPropertySourceLoader implements PropertySourceLoader {
|
|||
}
|
||||
|
||||
@Override
|
||||
public PropertySource<?> load(String name, Resource resource, String profileToLoad,
|
||||
Predicate<String[]> acceptsProfiles) throws IOException {
|
||||
public List<PropertySource<?>> load(String name, Resource resource)
|
||||
throws IOException {
|
||||
if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", null)) {
|
||||
throw new IllegalStateException("Attempted to load " + name
|
||||
+ " but snakeyaml was not found on the classpath");
|
||||
}
|
||||
Map<String, Object> source = new OriginTrackedYamlLoader(resource, profileToLoad,
|
||||
acceptsProfiles).load();
|
||||
if (!source.isEmpty()) {
|
||||
return new OriginTrackedMapPropertySource(name, source);
|
||||
List<Map<String, Object>> loaded = new OriginTrackedYamlLoader(resource).load();
|
||||
if (loaded.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return null;
|
||||
List<PropertySource<?>> propertySources = new ArrayList<>(loaded.size());
|
||||
for (int i = 0; i < loaded.size(); i++) {
|
||||
propertySources.add(new OriginTrackedMapPropertySource(
|
||||
name + (loaded.size() == 1 ? "" : " (document #" + i + ")"),
|
||||
loaded.get(i)));
|
||||
}
|
||||
return propertySources;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -540,8 +540,8 @@ public class ConfigFileApplicationListenerTests {
|
|||
.map(org.springframework.core.env.PropertySource::getName)
|
||||
.collect(Collectors.toList());
|
||||
assertThat(names).contains(
|
||||
"applicationConfig: [classpath:/testsetprofiles.yml]#dev",
|
||||
"applicationConfig: [classpath:/testsetprofiles.yml]");
|
||||
"applicationConfig: [classpath:/testsetprofiles.yml] (document #0)",
|
||||
"applicationConfig: [classpath:/testsetprofiles.yml] (document #1)");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -88,6 +88,48 @@ public class ConfigFileApplicationListenerYamlProfileNegationTests {
|
|||
assertVersionProperty(this.context, "NOT A", "C", "B");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void yamlProfileCascading() {
|
||||
SpringApplication application = new SpringApplication(Config.class);
|
||||
application.setWebApplicationType(WebApplicationType.NONE);
|
||||
String configName = "--spring.config.name=cascadingprofiles";
|
||||
this.context = application.run(configName);
|
||||
assertVersionProperty(this.context, "E", "D", "C", "E", "A", "B");
|
||||
assertThat(this.context.getEnvironment().getProperty("not-a")).isNull();
|
||||
assertThat(this.context.getEnvironment().getProperty("not-b")).isNull();
|
||||
assertThat(this.context.getEnvironment().getProperty("not-c")).isNull();
|
||||
assertThat(this.context.getEnvironment().getProperty("not-d")).isNull();
|
||||
assertThat(this.context.getEnvironment().getProperty("not-e")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void yamlProfileCascadingOverrideProfilesA() {
|
||||
SpringApplication application = new SpringApplication(Config.class);
|
||||
application.setWebApplicationType(WebApplicationType.NONE);
|
||||
String configName = "--spring.config.name=cascadingprofiles";
|
||||
this.context = application.run(configName, "--spring.profiles.active=A");
|
||||
assertVersionProperty(this.context, "E", "C", "E", "A");
|
||||
assertThat(this.context.getEnvironment().getProperty("not-a")).isNull();
|
||||
assertThat(this.context.getEnvironment().getProperty("not-b")).isEqualTo("true");
|
||||
assertThat(this.context.getEnvironment().getProperty("not-c")).isNull();
|
||||
assertThat(this.context.getEnvironment().getProperty("not-d")).isEqualTo("true");
|
||||
assertThat(this.context.getEnvironment().getProperty("not-e")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void yamlProfileCascadingOverrideProfilesB() {
|
||||
SpringApplication application = new SpringApplication(Config.class);
|
||||
application.setWebApplicationType(WebApplicationType.NONE);
|
||||
String configName = "--spring.config.name=cascadingprofiles";
|
||||
this.context = application.run(configName, "--spring.profiles.active=B");
|
||||
assertVersionProperty(this.context, "E", "D", "E", "B");
|
||||
assertThat(this.context.getEnvironment().getProperty("not-a")).isEqualTo("true");
|
||||
assertThat(this.context.getEnvironment().getProperty("not-b")).isNull();
|
||||
assertThat(this.context.getEnvironment().getProperty("not-c")).isEqualTo("true");
|
||||
assertThat(this.context.getEnvironment().getProperty("not-d")).isNull();
|
||||
assertThat(this.context.getEnvironment().getProperty("not-e")).isNull();
|
||||
}
|
||||
|
||||
private void assertVersionProperty(ConfigurableApplicationContext context,
|
||||
String expectedVersion, String... expectedActiveProfiles) {
|
||||
assertThat(context.getEnvironment().getActiveProfiles())
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2018 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.Properties;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import org.springframework.beans.factory.config.YamlProcessor.MatchStatus;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
|
||||
/**
|
||||
* Tests for {@link AcceptsProfilesDocumentMatcher}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class AcceptsProfilesDocumentMatcherTests {
|
||||
|
||||
@Mock
|
||||
private Predicate<String[]> acceptsProfiles;
|
||||
|
||||
private AcceptsProfilesDocumentMatcher matcher;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
this.matcher = new AcceptsProfilesDocumentMatcher(this.acceptsProfiles);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenHasNoProfilePropertyShouldReturnAbstain() {
|
||||
Properties properties = new Properties();
|
||||
assertThat(this.matcher.matches(properties)).isEqualTo(MatchStatus.ABSTAIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenAcceptsProfileShouldReturnAbstain() {
|
||||
Properties properties = new Properties();
|
||||
properties.put("spring.profiles", "foo");
|
||||
given(this.acceptsProfiles.test(new String[] { "foo" })).willReturn(true);
|
||||
assertThat(this.matcher.matches(properties)).isEqualTo(MatchStatus.ABSTAIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenDoesNotAcceptProfileShouldReturnNotFound() {
|
||||
Properties properties = new Properties();
|
||||
properties.put("spring.profiles", "foo");
|
||||
given(this.acceptsProfiles.test(new String[] { "foo" })).willReturn(false);
|
||||
assertThat(this.matcher.matches(properties)).isEqualTo(MatchStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
}
|
|
@ -46,7 +46,7 @@ public class NoSnakeYamlPropertySourceLoaderTests {
|
|||
"Attempted to load resource but snakeyaml was not found on the classpath");
|
||||
ByteArrayResource resource = new ByteArrayResource(
|
||||
"foo:\n bar: spam".getBytes());
|
||||
this.loader.load("resource", resource, null, (profile) -> true);
|
||||
this.loader.load("resource", resource);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.springframework.boot.env;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
|
@ -38,12 +39,12 @@ public class OriginTrackedYamlLoaderTests {
|
|||
|
||||
private OriginTrackedYamlLoader loader;
|
||||
|
||||
private Map<String, Object> result;
|
||||
private List<Map<String, Object>> result;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
Resource resource = new ClassPathResource("test-yaml.yml", getClass());
|
||||
this.loader = new OriginTrackedYamlLoader(resource, null, (profile) -> true);
|
||||
this.loader = new OriginTrackedYamlLoader(resource);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -90,15 +91,6 @@ public class OriginTrackedYamlLoaderTests {
|
|||
assertThat(getLocation(education)).isEqualTo("16:12");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processWithActiveProfile() {
|
||||
Resource resource = new ClassPathResource("test-yaml.yml", getClass());
|
||||
this.loader = new OriginTrackedYamlLoader(resource, "development",
|
||||
(profile) -> true);
|
||||
Map<String, Object> result = this.loader.load();
|
||||
assertThat(result.get("name").toString()).isEqualTo("Test Name");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void processListOfMaps() {
|
||||
OriginTrackedValue name = getValue("example.foo[0].name");
|
||||
|
@ -129,7 +121,7 @@ public class OriginTrackedYamlLoaderTests {
|
|||
if (this.result == null) {
|
||||
this.result = this.loader.load();
|
||||
}
|
||||
return (OriginTrackedValue) this.result.get(name);
|
||||
return (OriginTrackedValue) this.result.get(0).get(name);
|
||||
}
|
||||
|
||||
private String getLocation(OriginTrackedValue value) {
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2018 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.Properties;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.config.YamlProcessor.MatchStatus;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link ProfileToLoadDocumentMatcher}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class ProfileToLoadDocumentMatcherTests {
|
||||
|
||||
@Test
|
||||
public void matchesWhenProfilesIsNullAndHasNoProfilePropertiesShouldReturnAbstain() {
|
||||
ProfileToLoadDocumentMatcher matcher = new ProfileToLoadDocumentMatcher(null);
|
||||
Properties properties = new Properties();
|
||||
assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.ABSTAIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenProfileIsNullAndHasOnlyNegativeProfilePropertiesShouldReturnAbstain() {
|
||||
ProfileToLoadDocumentMatcher matcher = new ProfileToLoadDocumentMatcher(null);
|
||||
Properties properties = new Properties();
|
||||
properties.put("spring.profiles", "!foo,!bar");
|
||||
assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.ABSTAIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenProfileIsNullAndHasProfilePropertyShouldReturnNotFound() {
|
||||
ProfileToLoadDocumentMatcher matcher = new ProfileToLoadDocumentMatcher(null);
|
||||
Properties properties = new Properties();
|
||||
properties.put("spring.profiles", "!foo,!bar,baz");
|
||||
assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenProfilesIsSetAndHasNoProfilePropertiesShouldReturnNotFound() {
|
||||
ProfileToLoadDocumentMatcher matcher = new ProfileToLoadDocumentMatcher("bar");
|
||||
Properties properties = new Properties();
|
||||
assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenProfileIsSetAndHasOnlyNegativeProfilePropertiesShouldReturnNotFound() {
|
||||
ProfileToLoadDocumentMatcher matcher = new ProfileToLoadDocumentMatcher("bar");
|
||||
Properties properties = new Properties();
|
||||
properties.put("spring.profiles", "!foo,!bar,baz");
|
||||
assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenProfileIsSetAndHasProfilePropertyShouldReturnAbstain() {
|
||||
ProfileToLoadDocumentMatcher matcher = new ProfileToLoadDocumentMatcher("bar");
|
||||
Properties properties = new Properties();
|
||||
properties.put("spring.profiles", "!foo,bar,baz");
|
||||
assertThat(matcher.matches(properties)).isEqualTo(MatchStatus.ABSTAIN);
|
||||
}
|
||||
|
||||
}
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.boot.env;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.env.PropertySource;
|
||||
|
@ -41,17 +43,17 @@ public class PropertiesPropertySourceLoaderTests {
|
|||
|
||||
@Test
|
||||
public void loadProperties() throws Exception {
|
||||
PropertySource<?> source = this.loader.load("test.properties",
|
||||
new ClassPathResource("test-properties.properties", getClass()), null,
|
||||
(profile) -> true);
|
||||
List<PropertySource<?>> loaded = this.loader.load("test.properties",
|
||||
new ClassPathResource("test-properties.properties", getClass()));
|
||||
PropertySource<?> source = loaded.get(0);
|
||||
assertThat(source.getProperty("test")).isEqualTo("properties");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadXml() throws Exception {
|
||||
PropertySource<?> source = this.loader.load("test.xml",
|
||||
new ClassPathResource("test-xml.xml", getClass()), null,
|
||||
(profile) -> true);
|
||||
List<PropertySource<?>> loaded = this.loader.load("test.xml",
|
||||
new ClassPathResource("test-xml.xml", getClass()));
|
||||
PropertySource<?> source = loaded.get(0);
|
||||
assertThat(source.getProperty("test")).isEqualTo("xml");
|
||||
}
|
||||
|
||||
|
|
|
@ -1,93 +0,0 @@
|
|||
/*
|
||||
* Copyright 2012-2018 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.Properties;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.config.YamlProcessor.MatchStatus;
|
||||
import org.springframework.boot.origin.OriginTrackedValue;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link SpringProfilesDocumentMatcher}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class SpringProfilesDocumentMatcherTests {
|
||||
|
||||
private TestSpringProfilesDocumentMatcher matcher = new TestSpringProfilesDocumentMatcher();
|
||||
|
||||
@Test
|
||||
public void matchesShouldBindAgainstCommaList() {
|
||||
Properties properties = new Properties();
|
||||
properties.put("spring.profiles", "foo,bar");
|
||||
this.matcher.matches(properties);
|
||||
assertThat(this.matcher.getProfiles()).containsExactly("foo", "bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesShouldBindAgainstYamlList() {
|
||||
Properties properties = new Properties();
|
||||
properties.put("spring.profiles[0]", "foo");
|
||||
properties.put("spring.profiles[1]", "bar");
|
||||
this.matcher.matches(properties);
|
||||
assertThat(this.matcher.getProfiles()).containsExactly("foo", "bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesShouldBindAgainstOriginTrackedValue() {
|
||||
Properties properties = new Properties();
|
||||
properties.put("spring.profiles", OriginTrackedValue.of("foo,bar"));
|
||||
this.matcher.matches(properties);
|
||||
assertThat(this.matcher.getProfiles()).containsExactly("foo", "bar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenMatchShouldReturnAbstain() {
|
||||
Properties properties = new Properties();
|
||||
properties.put("spring.profiles", "foo,bar");
|
||||
assertThat(this.matcher.matches(properties)).isEqualTo(MatchStatus.ABSTAIN);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void matchesWhenNoMatchShouldReturnNotFound() {
|
||||
Properties properties = new Properties();
|
||||
assertThat(this.matcher.matches(properties)).isEqualTo(MatchStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
private static class TestSpringProfilesDocumentMatcher
|
||||
extends SpringProfilesDocumentMatcher {
|
||||
|
||||
private String[] profiles;
|
||||
|
||||
@Override
|
||||
protected boolean matches(String[] profiles) {
|
||||
this.profiles = profiles;
|
||||
return !ObjectUtils.isEmpty(profiles);
|
||||
}
|
||||
|
||||
public String[] getProfiles() {
|
||||
return this.profiles;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -26,7 +26,6 @@ import org.springframework.core.env.PropertySource;
|
|||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -46,8 +45,7 @@ public class YamlPropertySourceLoaderTests {
|
|||
public void load() throws Exception {
|
||||
ByteArrayResource resource = new ByteArrayResource(
|
||||
"foo:\n bar: spam".getBytes());
|
||||
PropertySource<?> source = this.loader.load("resource", resource, null,
|
||||
(profile) -> true);
|
||||
PropertySource<?> source = this.loader.load("resource", resource).get(0);
|
||||
assertThat(source).isNotNull();
|
||||
assertThat(source.getProperty("foo.bar")).isEqualTo("spam");
|
||||
}
|
||||
|
@ -62,7 +60,7 @@ public class YamlPropertySourceLoaderTests {
|
|||
}
|
||||
ByteArrayResource resource = new ByteArrayResource(yaml.toString().getBytes());
|
||||
EnumerablePropertySource<?> source = (EnumerablePropertySource<?>) this.loader
|
||||
.load("resource", resource, null, (profile) -> true);
|
||||
.load("resource", resource).get(0);
|
||||
assertThat(source).isNotNull();
|
||||
assertThat(source.getPropertyNames())
|
||||
.isEqualTo(StringUtils.toStringArray(expected));
|
||||
|
@ -75,18 +73,16 @@ public class YamlPropertySourceLoaderTests {
|
|||
yaml.append("---\n");
|
||||
yaml.append("foo:\n baz: wham\n");
|
||||
ByteArrayResource resource = new ByteArrayResource(yaml.toString().getBytes());
|
||||
PropertySource<?> source = this.loader.load("resource", resource, null,
|
||||
(profile) -> true);
|
||||
assertThat(source).isNotNull();
|
||||
assertThat(source.getProperty("foo.bar")).isEqualTo("spam");
|
||||
assertThat(source.getProperty("foo.baz")).isEqualTo("wham");
|
||||
List<PropertySource<?>> loaded = this.loader.load("resource", resource);
|
||||
assertThat(loaded).hasSize(2);
|
||||
assertThat(loaded.get(0).getProperty("foo.bar")).isEqualTo("spam");
|
||||
assertThat(loaded.get(1).getProperty("foo.baz")).isEqualTo("wham");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timestampLikeItemsDoNotBecomeDates() throws Exception {
|
||||
ByteArrayResource resource = new ByteArrayResource("foo: 2015-01-28".getBytes());
|
||||
PropertySource<?> source = this.loader.load("resource", resource, null,
|
||||
(profile) -> true);
|
||||
PropertySource<?> source = this.loader.load("resource", resource).get(0);
|
||||
assertThat(source).isNotNull();
|
||||
assertThat(source.getProperty("foo")).isEqualTo("2015-01-28");
|
||||
}
|
||||
|
@ -94,42 +90,13 @@ public class YamlPropertySourceLoaderTests {
|
|||
@Test
|
||||
public void loadOriginAware() throws Exception {
|
||||
Resource resource = new ClassPathResource("test-yaml.yml", getClass());
|
||||
PropertySource<?> source = this.loader.load("resource", resource, null,
|
||||
(profile) -> true);
|
||||
EnumerablePropertySource<?> enumerableSource = (EnumerablePropertySource<?>) source;
|
||||
for (String name : enumerableSource.getPropertyNames()) {
|
||||
System.out.println(name + " = " + enumerableSource.getProperty(name));
|
||||
List<PropertySource<?>> loaded = this.loader.load("resource", resource);
|
||||
for (PropertySource<?> source : loaded) {
|
||||
EnumerablePropertySource<?> enumerableSource = (EnumerablePropertySource<?>) source;
|
||||
for (String name : enumerableSource.getPropertyNames()) {
|
||||
System.out.println(name + " = " + enumerableSource.getProperty(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadSpecificProfile() throws Exception {
|
||||
StringBuilder yaml = new StringBuilder();
|
||||
yaml.append("foo:\n bar: spam\n");
|
||||
yaml.append("---\n");
|
||||
yaml.append("spring:\n profiles: foo\n");
|
||||
yaml.append("foo:\n bar: wham\n");
|
||||
ByteArrayResource resource = new ByteArrayResource(yaml.toString().getBytes());
|
||||
PropertySource<?> source = this.loader.load("resource", resource, "foo",
|
||||
(profile) -> true);
|
||||
assertThat(source).isNotNull();
|
||||
assertThat(source.getProperty("foo.bar")).isEqualTo("wham");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadWithAcceptProfile() throws Exception {
|
||||
StringBuilder yaml = new StringBuilder();
|
||||
yaml.append("---\n");
|
||||
yaml.append("spring:\n profiles: yay,foo\n");
|
||||
yaml.append("foo:\n bar: bang\n");
|
||||
yaml.append("---\n");
|
||||
yaml.append("spring:\n profiles: yay,!foo\n");
|
||||
yaml.append("foo:\n bar: wham\n");
|
||||
ByteArrayResource resource = new ByteArrayResource(yaml.toString().getBytes());
|
||||
PropertySource<?> source = this.loader.load("resource", resource, "yay",
|
||||
(profiles) -> ObjectUtils.containsElement(profiles, "!foo"));
|
||||
assertThat(source).isNotNull();
|
||||
assertThat(source.getProperty("foo.bar")).isEqualTo("wham");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
spring:
|
||||
profiles:
|
||||
active:
|
||||
- A
|
||||
- B
|
||||
|
||||
---
|
||||
spring.profiles: A
|
||||
|
||||
spring:
|
||||
profiles:
|
||||
include:
|
||||
- C
|
||||
- E
|
||||
|
||||
---
|
||||
spring.profiles: B
|
||||
|
||||
spring:
|
||||
profiles:
|
||||
include:
|
||||
- D
|
||||
- E
|
||||
|
||||
---
|
||||
spring.profiles: E
|
||||
|
||||
version: E
|
||||
|
||||
---
|
||||
spring.profiles: "!A"
|
||||
|
||||
not-a: true
|
||||
|
||||
---
|
||||
spring.profiles: "!B"
|
||||
|
||||
not-b: true
|
||||
|
||||
---
|
||||
spring.profiles: "!C"
|
||||
|
||||
not-c: true
|
||||
|
||||
---
|
||||
spring.profiles: "!D"
|
||||
|
||||
not-d: true
|
||||
|
||||
---
|
||||
spring.profiles: "!E"
|
||||
|
||||
not-e: true
|
Loading…
Reference in New Issue