diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc
index 20a807dcdfc..9bd73c71422 100644
--- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc
+++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc
@@ -663,7 +663,7 @@ You can use this prefix with the `spring.config.location` and `spring.config.add
For example, a `spring.config.import` value of `optional:file:./myconfig.properties` allows your application to start, even if the `myconfig.properties` file is missing.
-If you want to ignore all `ConfigDataLocationNotFoundExceptions` and always continue to start your application, you can use the `spring.config.on-location-not-found` property.
+If you want to ignore all `ConfigDataLocationNotFoundExceptions` and always continue to start your application, you can use the `spring.config.on-not-found` property.
Set the value to `ignore` using `SpringApplication.setDefaultProperties(...)` or with a system/environment variable.
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigData.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigData.java
index 28ec4915e71..4d25015ef0a 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigData.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigData.java
@@ -29,9 +29,9 @@ import org.springframework.core.env.PropertySource;
import org.springframework.util.Assert;
/**
- * Configuration data that has been loaded from an external {@link ConfigDataLocation
- * location} and may ultimately contribute {@link PropertySource property sources} to
- * Spring's {@link Environment}.
+ * Configuration data that has been loaded from a {@link ConfigDataResource} and may
+ * ultimately contribute {@link PropertySource property sources} to Spring's
+ * {@link Environment}.
*
* @author Phillip Webb
* @author Madhura Bhave
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java
index e6de58c8aeb..1c4709c688b 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java
@@ -48,10 +48,10 @@ import org.springframework.util.StringUtils;
* Wrapper around a {@link ConfigurableEnvironment} that can be used to import and apply
* {@link ConfigData}. Configures the initial set of
* {@link ConfigDataEnvironmentContributors} by wrapping property sources from the Spring
- * {@link Environment} and adding the initial set of imports.
+ * {@link Environment} and adding the initial set of locations.
*
- * The initial imports can be influenced via the {@link #LOCATION_PROPERTY},
- * {@value #ADDITIONAL_LOCATION_PROPERTY} and {@value #IMPORT_PROPERTY} properties. If not
+ * The initial locations can be influenced via the {@link #LOCATION_PROPERTY},
+ * {@value #ADDITIONAL_LOCATION_PROPERTY} and {@value #IMPORT_PROPERTY} properties. If no
* explicit properties are set, the {@link #DEFAULT_SEARCH_LOCATIONS} will be used.
*
* @author Phillip Webb
@@ -76,28 +76,41 @@ class ConfigDataEnvironment {
/**
* Property used to determine what action to take when a
- * {@code ConfigDataLocationNotFoundException} is thrown.
- * @see ConfigDataLocationNotFoundAction
+ * {@code ConfigDataNotFoundAction} is thrown.
+ * @see ConfigDataNotFoundAction
*/
- static final String ON_LOCATION_NOT_FOUND_PROPERTY = "spring.config.on-location-not-found";
+ static final String ON_NOT_FOUND_PROPERTY = "spring.config.on-not-found";
/**
* Default search locations used if not {@link #LOCATION_PROPERTY} is found.
*/
- static final String[] DEFAULT_SEARCH_LOCATIONS = { "optional:classpath:/", "optional:classpath:/config/",
- "optional:file:./", "optional:file:./config/*/", "optional:file:./config/" };
+ static final ConfigDataLocation[] DEFAULT_SEARCH_LOCATIONS;
+ static {
+ List locations = new ArrayList<>();
+ locations.add(ConfigDataLocation.of("optional:classpath:/"));
+ locations.add(ConfigDataLocation.of("optional:classpath:/config/"));
+ locations.add(ConfigDataLocation.of("optional:file:./"));
+ locations.add(ConfigDataLocation.of("optional:file:./config/*/"));
+ locations.add(ConfigDataLocation.of("optional:file:./config/"));
+ DEFAULT_SEARCH_LOCATIONS = locations.toArray(new ConfigDataLocation[0]);
+ };
- private static final String[] EMPTY_LOCATIONS = new String[0];
+ private static final ConfigDataLocation[] EMPTY_LOCATIONS = new ConfigDataLocation[0];
private static final ConfigurationPropertyName INCLUDE_PROFILES = ConfigurationPropertyName
.of(Profiles.INCLUDE_PROFILES_PROPERTY_NAME);
+ private static final Bindable CONFIG_DATA_LOCATION_ARRAY = Bindable
+ .of(ConfigDataLocation[].class);
+
private static final Bindable> STRING_LIST = Bindable.listOf(String.class);
private final DeferredLogFactory logFactory;
private final Log logger;
+ private final ConfigDataNotFoundAction notFoundAction;
+
private final ConfigurableBootstrapContext bootstrapContext;
private final ConfigurableEnvironment environment;
@@ -122,25 +135,21 @@ class ConfigDataEnvironment {
ConfigurableEnvironment environment, ResourceLoader resourceLoader, Collection additionalProfiles) {
Binder binder = Binder.get(environment);
UseLegacyConfigProcessingException.throwIfRequested(binder);
- ConfigDataLocationNotFoundAction locationNotFoundAction = binder
- .bind(ON_LOCATION_NOT_FOUND_PROPERTY, ConfigDataLocationNotFoundAction.class)
- .orElse(ConfigDataLocationNotFoundAction.FAIL);
this.logFactory = logFactory;
this.logger = logFactory.getLog(getClass());
+ this.notFoundAction = binder.bind(ON_NOT_FOUND_PROPERTY, ConfigDataNotFoundAction.class)
+ .orElse(ConfigDataNotFoundAction.FAIL);
this.bootstrapContext = bootstrapContext;
this.environment = environment;
- this.resolvers = createConfigDataLocationResolvers(logFactory, bootstrapContext, locationNotFoundAction, binder,
- resourceLoader);
+ this.resolvers = createConfigDataLocationResolvers(logFactory, bootstrapContext, binder, resourceLoader);
this.additionalProfiles = additionalProfiles;
- this.loaders = new ConfigDataLoaders(logFactory, bootstrapContext, locationNotFoundAction);
+ this.loaders = new ConfigDataLoaders(logFactory, bootstrapContext);
this.contributors = createContributors(binder);
}
protected ConfigDataLocationResolvers createConfigDataLocationResolvers(DeferredLogFactory logFactory,
- ConfigurableBootstrapContext bootstrapContext, ConfigDataLocationNotFoundAction locationNotFoundAction,
- Binder binder, ResourceLoader resourceLoader) {
- return new ConfigDataLocationResolvers(logFactory, bootstrapContext, locationNotFoundAction, binder,
- resourceLoader);
+ ConfigurableBootstrapContext bootstrapContext, Binder binder, ResourceLoader resourceLoader) {
+ return new ConfigDataLocationResolvers(logFactory, bootstrapContext, binder, resourceLoader);
}
private ConfigDataEnvironmentContributors createContributors(Binder binder) {
@@ -172,23 +181,26 @@ class ConfigDataEnvironment {
private List getInitialImportContributors(Binder binder) {
List initialContributors = new ArrayList<>();
+ addInitialImportContributors(initialContributors, bindLocations(binder, IMPORT_PROPERTY, EMPTY_LOCATIONS));
addInitialImportContributors(initialContributors,
- binder.bind(IMPORT_PROPERTY, String[].class).orElse(EMPTY_LOCATIONS));
+ bindLocations(binder, ADDITIONAL_LOCATION_PROPERTY, EMPTY_LOCATIONS));
addInitialImportContributors(initialContributors,
- binder.bind(ADDITIONAL_LOCATION_PROPERTY, String[].class).orElse(EMPTY_LOCATIONS));
- addInitialImportContributors(initialContributors,
- binder.bind(LOCATION_PROPERTY, String[].class).orElse(DEFAULT_SEARCH_LOCATIONS));
+ bindLocations(binder, LOCATION_PROPERTY, DEFAULT_SEARCH_LOCATIONS));
return initialContributors;
}
+ private ConfigDataLocation[] bindLocations(Binder binder, String propertyName, ConfigDataLocation[] other) {
+ return binder.bind(propertyName, CONFIG_DATA_LOCATION_ARRAY).orElse(other);
+ }
+
private void addInitialImportContributors(List initialContributors,
- String[] locations) {
+ ConfigDataLocation[] locations) {
for (int i = locations.length - 1; i >= 0; i--) {
initialContributors.add(createInitialImportContributor(locations[i]));
}
}
- private ConfigDataEnvironmentContributor createInitialImportContributor(String location) {
+ private ConfigDataEnvironmentContributor createInitialImportContributor(ConfigDataLocation location) {
this.logger.trace(LogMessage.format("Adding initial config data import from location '%s'", location));
return ConfigDataEnvironmentContributor.ofInitialImport(location);
}
@@ -198,7 +210,8 @@ class ConfigDataEnvironment {
* {@link Environment}.
*/
void processAndApply() {
- ConfigDataImporter importer = new ConfigDataImporter(this.resolvers, this.loaders);
+ ConfigDataImporter importer = new ConfigDataImporter(this.logFactory, this.notFoundAction, this.resolvers,
+ this.loaders);
this.bootstrapContext.register(Binder.class, InstanceSupplier
.from(() -> this.contributors.getBinder(null, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE)));
ConfigDataEnvironmentContributors contributors = processInitial(this.contributors, importer);
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributor.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributor.java
index 053b0f64f0e..30c685e8c51 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributor.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributor.java
@@ -28,7 +28,6 @@ import java.util.stream.StreamSupport;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
-import org.springframework.boot.origin.Origin;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertySource;
@@ -52,7 +51,7 @@ import org.springframework.core.env.PropertySource;
*/
class ConfigDataEnvironmentContributor implements Iterable {
- private final ConfigDataLocation location;
+ private final ConfigDataResource resource;
private final PropertySource> propertySource;
@@ -69,7 +68,7 @@ class ConfigDataEnvironmentContributor implements Iterable propertySource,
+ ConfigDataEnvironmentContributor(Kind kind, ConfigDataResource resource, PropertySource> propertySource,
ConfigurationPropertySource configurationPropertySource, ConfigDataProperties properties,
boolean ignoreImports, Map> children) {
this.kind = kind;
- this.location = location;
+ this.resource = resource;
this.properties = properties;
this.propertySource = propertySource;
this.configurationPropertySource = configurationPropertySource;
@@ -107,11 +106,11 @@ class ConfigDataEnvironmentContributor implements Iterable getImports() {
+ List getImports() {
return (this.properties != null) ? this.properties.getImports() : Collections.emptyList();
}
- Origin getImportOrigin(String importLocation) {
- return (this.properties != null) ? this.properties.getImportOrigin(importLocation) : null;
- }
-
/**
* Return true if this contributor has imports that have not yet been processed in the
* given phase.
@@ -184,13 +179,19 @@ class ConfigDataEnvironmentContributor implements Iterable children) {
Map> updatedChildren = new LinkedHashMap<>(this.children);
updatedChildren.put(importPhase, children);
- return new ConfigDataEnvironmentContributor(this.kind, this.location, this.propertySource,
+ return new ConfigDataEnvironmentContributor(this.kind, this.resource, this.propertySource,
this.configurationPropertySource, this.properties, this.ignoreImports, updatedChildren);
}
@@ -230,7 +231,7 @@ class ConfigDataEnvironmentContributor implements Iterable imports = Collections.singletonList(importLocation);
+ static ConfigDataEnvironmentContributor ofInitialImport(ConfigDataLocation initialImport) {
+ List imports = Collections.singletonList(initialImport);
ConfigDataProperties properties = new ConfigDataProperties(imports, null);
return new ConfigDataEnvironmentContributor(Kind.INITIAL_IMPORT, null, null, null, properties, false, null);
}
@@ -274,17 +275,17 @@ class ConfigDataEnvironmentContributor implements Iterable propertySource = configData.getPropertySources().get(propertySourceIndex);
ConfigurationPropertySource configurationPropertySource = ConfigurationPropertySource.from(propertySource);
boolean ignoreImports = configData.getOptions().contains(ConfigData.Option.IGNORE_IMPORTS);
- return new ConfigDataEnvironmentContributor(Kind.UNBOUND_IMPORT, location, propertySource,
+ return new ConfigDataEnvironmentContributor(Kind.UNBOUND_IMPORT, resource, propertySource,
configurationPropertySource, null, ignoreImports, null);
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorPlaceholdersResolver.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorPlaceholdersResolver.java
index 06ae47d9df1..434d0915d49 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorPlaceholdersResolver.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorPlaceholdersResolver.java
@@ -64,9 +64,9 @@ class ConfigDataEnvironmentContributorPlaceholdersResolver implements Placeholde
Object value = (propertySource != null) ? propertySource.getProperty(placeholder) : null;
if (value != null && !contributor.isActive(this.activationContext)) {
if (this.failOnResolveFromInactiveContributor) {
+ ConfigDataResource resource = contributor.getResource();
Origin origin = OriginLookup.getOrigin(propertySource, placeholder);
- throw new InactiveConfigDataAccessException(propertySource, contributor.getLocation(), placeholder,
- origin);
+ throw new InactiveConfigDataAccessException(propertySource, resource, placeholder, origin);
}
value = null;
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributors.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributors.java
index 053c2d06612..9267902275c 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributors.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributors.java
@@ -39,7 +39,6 @@ import org.springframework.boot.context.properties.bind.PlaceholdersResolver;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.logging.DeferredLogFactory;
-import org.springframework.boot.origin.Origin;
import org.springframework.core.log.LogMessage;
import org.springframework.util.ObjectUtils;
@@ -114,12 +113,12 @@ class ConfigDataEnvironmentContributors implements Iterable imports = contributor.getImports();
+ List imports = contributor.getImports();
this.logger.trace(LogMessage.format("Processing imports %s", imports));
- Map imported = importer.resolveAndLoad(activationContext,
+ Map imported = importer.resolveAndLoad(activationContext,
locationResolverContext, loaderContext, imports);
this.logger.trace(LogMessage.of(() -> imported.isEmpty() ? "Nothing imported" : "Imported "
- + imported.size() + " location " + ((imported.size() != 1) ? "s" : "") + imported.keySet()));
+ + imported.size() + " resource " + ((imported.size() != 1) ? "s" : "") + imported.keySet()));
ConfigDataEnvironmentContributor contributorAndChildren = contributor.withChildren(importPhase,
asContributors(imported));
result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapContext,
@@ -148,7 +147,7 @@ class ConfigDataEnvironmentContributors implements Iterable asContributors(Map imported) {
+ private List asContributors(Map imported) {
List contributors = new ArrayList<>(imported.size() * 5);
imported.forEach((location, data) -> {
for (int i = data.getPropertySources().size() - 1; i >= 0; i--) {
@@ -259,8 +258,8 @@ class ConfigDataEnvironmentContributors implements Iterable loadedLocations = new HashSet<>();
+ private final ConfigDataNotFoundAction notFoundAction;
+
+ private final Set loaded = new HashSet<>();
/**
* Create a new {@link ConfigDataImporter} instance.
+ * @param logFactory the log factory
+ * @param notFoundAction the action to take when a location cannot be found
* @param resolvers the config data location resolvers
* @param loaders the config data loaders
*/
- ConfigDataImporter(ConfigDataLocationResolvers resolvers, ConfigDataLoaders loaders) {
+ ConfigDataImporter(DeferredLogFactory logFactory, ConfigDataNotFoundAction notFoundAction,
+ ConfigDataLocationResolvers resolvers, ConfigDataLoaders loaders) {
+ this.logger = logFactory.getLog(getClass());
this.resolvers = resolvers;
this.loaders = loaders;
+ this.notFoundAction = notFoundAction;
}
/**
@@ -59,31 +73,70 @@ class ConfigDataImporter {
* @param locations the locations to resolve
* @return a map of the loaded locations and data
*/
- Map resolveAndLoad(ConfigDataActivationContext activationContext,
+ Map resolveAndLoad(ConfigDataActivationContext activationContext,
ConfigDataLocationResolverContext locationResolverContext, ConfigDataLoaderContext loaderContext,
- List locations) {
+ List locations) {
try {
Profiles profiles = (activationContext != null) ? activationContext.getProfiles() : null;
- return load(loaderContext, this.resolvers.resolveAll(locationResolverContext, locations, profiles));
+ List resolved = resolve(locationResolverContext, profiles, locations);
+ return load(loaderContext, resolved);
}
catch (IOException ex) {
throw new IllegalStateException("IO error on loading imports from " + locations, ex);
}
}
- private Map load(ConfigDataLoaderContext loaderContext,
- List locations) throws IOException {
- Map result = new LinkedHashMap<>();
- for (int i = locations.size() - 1; i >= 0; i--) {
- ConfigDataLocation location = locations.get(i);
- if (this.loadedLocations.add(location)) {
- ConfigData loaded = this.loaders.load(loaderContext, location);
- if (loaded != null) {
- result.put(location, loaded);
+ private List resolve(ConfigDataLocationResolverContext locationResolverContext,
+ Profiles profiles, List locations) {
+ List resolved = new ArrayList<>(locations.size());
+ for (ConfigDataLocation location : locations) {
+ resolved.addAll(resolve(locationResolverContext, profiles, location));
+ }
+ return Collections.unmodifiableList(resolved);
+ }
+
+ private List resolve(ConfigDataLocationResolverContext locationResolverContext,
+ Profiles profiles, ConfigDataLocation location) {
+ try {
+ return this.resolvers.resolve(locationResolverContext, location, profiles);
+ }
+ catch (ConfigDataNotFoundException ex) {
+ handle(ex, location);
+ return Collections.emptyList();
+ }
+ }
+
+ private Map load(ConfigDataLoaderContext loaderContext,
+ List candidates) throws IOException {
+ Map result = new LinkedHashMap<>();
+ for (int i = candidates.size() - 1; i >= 0; i--) {
+ ConfigDataResolutionResult candidate = candidates.get(i);
+ ConfigDataLocation location = candidate.getLocation();
+ ConfigDataResource resource = candidate.getResource();
+ if (this.loaded.add(resource)) {
+ try {
+ ConfigData loaded = this.loaders.load(loaderContext, resource);
+ if (loaded != null) {
+ result.put(resource, loaded);
+ }
+ }
+ catch (ConfigDataNotFoundException ex) {
+ handle(ex, location);
}
}
}
return Collections.unmodifiableMap(result);
}
+ private void handle(ConfigDataNotFoundException ex, ConfigDataLocation location) {
+ if (ex instanceof ConfigDataResourceNotFoundException) {
+ ex = ((ConfigDataResourceNotFoundException) ex).withLocation(location);
+ }
+ getNotFoundAction(location).handle(this.logger, ex);
+ }
+
+ private ConfigDataNotFoundAction getNotFoundAction(ConfigDataLocation location) {
+ return (!location.isOptional()) ? this.notFoundAction : ConfigDataNotFoundAction.IGNORE;
+ }
+
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoader.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoader.java
index 8c3fa0f6b17..22e5d3a7549 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoader.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoader.java
@@ -25,8 +25,8 @@ import org.springframework.boot.BootstrapRegistry;
import org.springframework.boot.ConfigurableBootstrapContext;
/**
- * Strategy class that can be used used to load {@link ConfigData} instances from a
- * {@link ConfigDataLocation location}. Implementations should be added as a
+ * Strategy class that can be used used to load {@link ConfigData} for a given
+ * {@link ConfigDataResource}. Implementations should be added as a
* {@code spring.factories} entries. The following constructor parameter types are
* supported:
*
@@ -36,34 +36,34 @@ import org.springframework.boot.ConfigurableBootstrapContext;
* ({@link BootstrapContext} or {@link BootstrapRegistry} may also be used).
*
*
- * Multiple loaders cannot claim the same location.
+ * Multiple loaders cannot claim the same resource.
*
- * @param the location type
+ * @param the resource type
* @author Phillip Webb
* @author Madhura Bhave
* @since 2.4.0
*/
-public interface ConfigDataLoader {
+public interface ConfigDataLoader {
/**
- * Returns if the specified location can be loaded by this instance.
+ * Returns if the specified resource can be loaded by this instance.
* @param context the loader context
- * @param location the location to check.
- * @return if the location is supported by this loader
+ * @param resource the resource to check.
+ * @return if the resource is supported by this loader
*/
- default boolean isLoadable(ConfigDataLoaderContext context, L location) {
+ default boolean isLoadable(ConfigDataLoaderContext context, R resource) {
return true;
}
/**
- * Load {@link ConfigData} for the given location.
+ * Load {@link ConfigData} for the given resource.
* @param context the loader context
- * @param location the location to load
+ * @param resource the resource to load
* @return the loaded config data or {@code null} if the location should be skipped
* @throws IOException on IO error
- * @throws ConfigDataLocationNotFoundException if the location cannot be found
+ * @throws ConfigDataResourceNotFoundException if the resource cannot be found
*/
- ConfigData load(ConfigDataLoaderContext context, L location)
- throws IOException, ConfigDataLocationNotFoundException;
+ ConfigData load(ConfigDataLoaderContext context, R resource)
+ throws IOException, ConfigDataResourceNotFoundException;
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaders.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaders.java
index e7cc4e1fc52..d2c1c681e20 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaders.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaders.java
@@ -43,37 +43,28 @@ class ConfigDataLoaders {
private final Log logger;
- private final ConfigDataLocationNotFoundAction locationNotFoundAction;
-
private final List> loaders;
- private final List> locationTypes;
+ private final List> resourceTypes;
/**
* Create a new {@link ConfigDataLoaders} instance.
* @param logFactory the deferred log factory
* @param bootstrapContext the bootstrap context
- * @param locationNotFoundAction the action to take if a
- * {@link ConfigDataLocationNotFoundException} is thrown
*/
- ConfigDataLoaders(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
- ConfigDataLocationNotFoundAction locationNotFoundAction) {
- this(logFactory, bootstrapContext, locationNotFoundAction,
- SpringFactoriesLoader.loadFactoryNames(ConfigDataLoader.class, null));
+ ConfigDataLoaders(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext) {
+ this(logFactory, bootstrapContext, SpringFactoriesLoader.loadFactoryNames(ConfigDataLoader.class, null));
}
/**
* Create a new {@link ConfigDataLoaders} instance.
* @param logFactory the deferred log factory
* @param bootstrapContext the bootstrap context
- * @param locationNotFoundAction the action to take if a
- * {@link ConfigDataLocationNotFoundException} is thrown
* @param names the {@link ConfigDataLoader} class names instantiate
*/
ConfigDataLoaders(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
- ConfigDataLocationNotFoundAction locationNotFoundAction, List names) {
+ List names) {
this.logger = logFactory.getLog(getClass());
- this.locationNotFoundAction = locationNotFoundAction;
Instantiator> instantiator = new Instantiator<>(ConfigDataLoader.class,
(availableParameters) -> {
availableParameters.add(Log.class, logFactory::getLog);
@@ -82,69 +73,52 @@ class ConfigDataLoaders {
availableParameters.add(BootstrapRegistry.class, bootstrapContext);
});
this.loaders = instantiator.instantiate(names);
- this.locationTypes = getLocationTypes(this.loaders);
+ this.resourceTypes = getResourceTypes(this.loaders);
}
- private List> getLocationTypes(List> loaders) {
- List> locationTypes = new ArrayList<>(loaders.size());
+ private List> getResourceTypes(List> loaders) {
+ List> resourceTypes = new ArrayList<>(loaders.size());
for (ConfigDataLoader> loader : loaders) {
- locationTypes.add(getLocationType(loader));
+ resourceTypes.add(getResourceType(loader));
}
- return Collections.unmodifiableList(locationTypes);
+ return Collections.unmodifiableList(resourceTypes);
}
- private Class> getLocationType(ConfigDataLoader> loader) {
+ private Class> getResourceType(ConfigDataLoader> loader) {
return ResolvableType.forClass(loader.getClass()).as(ConfigDataLoader.class).resolveGeneric();
}
/**
* Load {@link ConfigData} using the first appropriate {@link ConfigDataLoader}.
- * @param the config data location type
+ * @param the resource type
* @param context the loader context
- * @param location the location to load
+ * @param resource the resource to load
* @return the loaded {@link ConfigData}
* @throws IOException on IO error
*/
- ConfigData load(ConfigDataLoaderContext context, L location) throws IOException {
- boolean optional = location instanceof OptionalConfigDataLocation;
- location = (!optional) ? location : OptionalConfigDataLocation.unwrap(location);
- return load(context, optional, location);
- }
-
- private ConfigData load(ConfigDataLoaderContext context, boolean optional,
- L location) throws IOException {
- ConfigDataLoader loader = getLoader(context, location);
- this.logger.trace(LogMessage.of(() -> "Loading " + location + " using loader " + loader.getClass().getName()));
- try {
- return loader.load(context, location);
- }
- catch (ConfigDataLocationNotFoundException ex) {
- if (optional) {
- this.logger.trace(LogMessage.format("Skipping missing resource from optional location %s", location));
- return null;
- }
- this.locationNotFoundAction.handle(this.logger, location, ex);
- return null;
- }
+ ConfigData load(ConfigDataLoaderContext context, R resource) throws IOException {
+ ConfigDataLoader loader = getLoader(context, resource);
+ this.logger.trace(LogMessage.of(() -> "Loading " + resource + " using loader " + loader.getClass().getName()));
+ return loader.load(context, resource);
}
@SuppressWarnings("unchecked")
- private ConfigDataLoader getLoader(ConfigDataLoaderContext context, L location) {
- ConfigDataLoader result = null;
+ private ConfigDataLoader getLoader(ConfigDataLoaderContext context, R resource) {
+ ConfigDataLoader result = null;
for (int i = 0; i < this.loaders.size(); i++) {
ConfigDataLoader> candidate = this.loaders.get(i);
- if (this.locationTypes.get(i).isInstance(location)) {
- ConfigDataLoader loader = (ConfigDataLoader) candidate;
- if (loader.isLoadable(context, location)) {
+ if (this.resourceTypes.get(i).isInstance(resource)) {
+ ConfigDataLoader loader = (ConfigDataLoader) candidate;
+ if (loader.isLoadable(context, resource)) {
if (result != null) {
- throw new IllegalStateException("Multiple loaders found for location " + location + " ["
+ throw new IllegalStateException("Multiple loaders found for resource '" + resource + "' ["
+ candidate.getClass().getName() + "," + result.getClass().getName() + "]");
}
result = loader;
}
}
}
- Assert.state(result != null, () -> "No loader found for location '" + location + "'");
+ Assert.state(result != null, () -> "No loader found for resource '" + resource + "'");
return result;
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocation.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocation.java
index 137c6d63844..01075a3c2f9 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocation.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocation.java
@@ -16,20 +16,131 @@
package org.springframework.boot.context.config;
+import org.springframework.boot.origin.Origin;
+import org.springframework.boot.origin.OriginProvider;
+import org.springframework.util.StringUtils;
+
/**
- * A location from which {@link ConfigData} can be loaded. Implementations must implement
- * a valid {@link #equals(Object) equals}, {@link #hashCode() hashCode} and
- * {@link #toString() toString} methods.
+ * A user specified location that can be {@link ConfigDataLocationResolver resolved} to
+ * one or {@link ConfigDataResource config data resources}. A {@link ConfigDataLocation}
+ * is a simple wrapper around a {@link String} value. The exact format of the value will
+ * depend on the underlying technology, but is usually a URL like syntax consisting of a
+ * prefix and path. For example, {@code crypt:somehost/somepath}.
+ *
+ * Locations can be mandatory or {@link #isOptional() optional}. Optional locations are
+ * prefixed with {@code optional:}.
*
* @author Phillip Webb
- * @author Madhura Bhave
* @since 2.4.0
*/
-public abstract class ConfigDataLocation {
+public final class ConfigDataLocation implements OriginProvider {
/**
- * Prefix used to indicate that a {@link ConfigDataLocation} is optional.
+ * Prefix used to indicate that a {@link ConfigDataResource} is optional.
*/
public static final String OPTIONAL_PREFIX = "optional:";
+ private final boolean optional;
+
+ private final String value;
+
+ private final Origin origin;
+
+ private ConfigDataLocation(boolean optional, String value, Origin origin) {
+ this.value = value;
+ this.optional = optional;
+ this.origin = origin;
+ }
+
+ /**
+ * Return the the location is optional and should ignore
+ * {@link ConfigDataNotFoundException}.
+ * @return if the location is optional
+ */
+ public boolean isOptional() {
+ return this.optional;
+ }
+
+ /**
+ * Return the value of the location (always excluding any user specified
+ * {@code optional:} prefix.
+ * @return the location value
+ */
+ public String getValue() {
+ return this.value;
+ }
+
+ /**
+ * Return if {@link #getValue()} has the specified prefix.
+ * @param prefix the prefix to check
+ * @return if the value has the prefix
+ */
+ public boolean hasPrefix(String prefix) {
+ return this.value.startsWith(prefix);
+ }
+
+ /**
+ * Return {@link #getValue()} with the specified prefix removed. If the location does
+ * not have the given prefix then the {@link #getValue()} is returned unchanged.
+ * @param prefix the prefix to check
+ * @return the value with the prefix removed
+ */
+ public String getNonPrefixedValue(String prefix) {
+ if (hasPrefix(prefix)) {
+ return this.value.substring(prefix.length());
+ }
+ return this.value;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return this.origin;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ ConfigDataLocation other = (ConfigDataLocation) obj;
+ return this.value.equals(other.value);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.value.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return (!this.optional) ? this.value : OPTIONAL_PREFIX + this.value;
+ }
+
+ /**
+ * Create a new {@link ConfigDataLocation} with a specific {@link Origin}.
+ * @param origin the orgin to set
+ * @return a new {@link ConfigDataLocation} instance.
+ */
+ ConfigDataLocation withOrigin(Origin origin) {
+ return new ConfigDataLocation(this.optional, this.value, origin);
+ }
+
+ /**
+ * Factory method to create a new {@link ConfigDataLocation} from a string.
+ * @param location the location string
+ * @return a {@link ConfigDataLocation} instance or {@code null} if no location was
+ * provided
+ */
+ public static ConfigDataLocation of(String location) {
+ boolean optional = location != null && location.startsWith(OPTIONAL_PREFIX);
+ String value = (!optional) ? location : location.substring(OPTIONAL_PREFIX.length());
+ if (!StringUtils.hasText(value)) {
+ return null;
+ }
+ return new ConfigDataLocation(optional, value, null);
+ }
+
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationBindHandler.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationBindHandler.java
new file mode 100644
index 00000000000..f40cc6badde
--- /dev/null
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationBindHandler.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2012-2020 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
+ *
+ * https://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.context.config;
+
+import java.util.List;
+
+import org.springframework.boot.context.properties.bind.AbstractBindHandler;
+import org.springframework.boot.context.properties.bind.BindContext;
+import org.springframework.boot.context.properties.bind.BindHandler;
+import org.springframework.boot.context.properties.bind.Bindable;
+import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
+import org.springframework.boot.origin.Origin;
+
+/**
+ * {@link BindHandler} to set the {@link Origin} of bound {@link ConfigDataLocation}
+ * objects.
+ *
+ * @author Phillip Webb
+ */
+class ConfigDataLocationBindHandler extends AbstractBindHandler {
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Object onSuccess(ConfigurationPropertyName name, Bindable> target, BindContext context, Object result) {
+ if (result instanceof ConfigDataLocation) {
+ return withOrigin(context, (ConfigDataLocation) result);
+ }
+ if (result instanceof List) {
+ List list = (List) result;
+ for (int i = 0; i < list.size(); i++) {
+ Object element = list.get(i);
+ if (element instanceof ConfigDataLocation) {
+ list.set(i, withOrigin(context, (ConfigDataLocation) element));
+ }
+ }
+ }
+ if (result instanceof ConfigDataLocation[]) {
+ ConfigDataLocation[] locations = (ConfigDataLocation[]) result;
+ for (int i = 0; i < locations.length; i++) {
+ locations[i] = withOrigin(context, locations[i]);
+ }
+ }
+ return result;
+ }
+
+ private ConfigDataLocation withOrigin(BindContext context, ConfigDataLocation result) {
+ if (result.getOrigin() != null) {
+ return result;
+ }
+ Origin origin = Origin.from(context.getConfigurationProperty());
+ return result.withOrigin(origin);
+ }
+
+}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationNotFoundException.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationNotFoundException.java
index ee52755f4e7..33337bf589f 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationNotFoundException.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationNotFoundException.java
@@ -16,25 +16,23 @@
package org.springframework.boot.context.config;
-import java.io.File;
-import java.nio.file.Files;
-import java.nio.file.Path;
-
-import org.springframework.core.io.Resource;
+import org.springframework.boot.origin.Origin;
+import org.springframework.util.Assert;
/**
- * Exception thrown when a config data location cannot be found.
+ * {@link ConfigDataNotFoundException} thrown when a {@link ConfigDataLocation} cannot be
+ * found.
*
* @author Phillip Webb
* @since 2.4.0
*/
-public class ConfigDataLocationNotFoundException extends ConfigDataException {
+public class ConfigDataLocationNotFoundException extends ConfigDataNotFoundException {
private final ConfigDataLocation location;
/**
* Create a new {@link ConfigDataLocationNotFoundException} instance.
- * @param location the location that was not found
+ * @param location the location that could not be found
*/
public ConfigDataLocationNotFoundException(ConfigDataLocation location) {
this(location, null);
@@ -42,79 +40,39 @@ public class ConfigDataLocationNotFoundException extends ConfigDataException {
/**
* Create a new {@link ConfigDataLocationNotFoundException} instance.
- * @param location the location that was not found
- * @param cause the cause of the exception
+ * @param location the location that could not be found
+ * @param cause the exception cause
*/
public ConfigDataLocationNotFoundException(ConfigDataLocation location, Throwable cause) {
- this(getMessage(location), location, cause);
- }
-
- /**
- * Create a new {@link ConfigDataLocationNotFoundException} instance.
- * @param message the exception message
- * @param location the location that was not found
- */
- public ConfigDataLocationNotFoundException(String message, ConfigDataLocation location) {
- this(message, location, null);
- }
-
- /**
- * Create a new {@link ConfigDataLocationNotFoundException} instance.
- * @param message the exception message
- * @param location the location that was not found
- * @param cause the cause of the exception
- */
- public ConfigDataLocationNotFoundException(String message, ConfigDataLocation location, Throwable cause) {
- super(message, cause);
+ super(getMessage(location), cause);
+ Assert.notNull(location, "Location must not be null");
this.location = location;
}
/**
* Return the location that could not be found.
- * @return the location that could not be found.
+ * @return the location
*/
public ConfigDataLocation getLocation() {
return this.location;
}
+ @Override
+ public Origin getOrigin() {
+ return Origin.from(this.location);
+ }
+
+ @Override
+ public String getReferenceDescription() {
+ return getReferenceDescription(this.location);
+ }
+
private static String getMessage(ConfigDataLocation location) {
- return "Config data location '" + location + "' does not exist";
+ return String.format("Config data %s cannot be found", getReferenceDescription(location));
}
- /**
- * Throw a {@link ConfigDataLocationNotFoundException} if the specified {@link Path}
- * does not exist.
- * @param location the location being checked
- * @param path the path to check
- */
- public static void throwIfDoesNotExist(ConfigDataLocation location, Path path) {
- throwIfDoesNotExist(location, Files.exists(path));
- }
-
- /**
- * Throw a {@link ConfigDataLocationNotFoundException} if the specified {@link File}
- * does not exist.
- * @param location the location being checked
- * @param file the file to check
- */
- public static void throwIfDoesNotExist(ConfigDataLocation location, File file) {
- throwIfDoesNotExist(location, file.exists());
- }
-
- /**
- * Throw a {@link ConfigDataLocationNotFoundException} if the specified
- * {@link Resource} does not exist.
- * @param location the location being checked
- * @param resource the resource to check
- */
- public static void throwIfDoesNotExist(ConfigDataLocation location, Resource resource) {
- throwIfDoesNotExist(location, resource.exists());
- }
-
- private static void throwIfDoesNotExist(ConfigDataLocation location, boolean exists) {
- if (!exists) {
- throw new ConfigDataLocationNotFoundException(location);
- }
+ private static String getReferenceDescription(ConfigDataLocation location) {
+ return String.format("location '%s'", location);
}
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolver.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolver.java
index a29e88196a6..f51ae7c50cd 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolver.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolver.java
@@ -31,9 +31,10 @@ import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
/**
- * Strategy interface used to resolve {@link ConfigDataLocation locations} from a String
- * based location address. Implementations should be added as a {@code spring.factories}
- * entries. The following constructor parameter types are supported:
+ * Strategy interface used to resolve {@link ConfigDataLocation locations} into one or
+ * more {@link ConfigDataResource resources}. Implementations should be added as a
+ * {@code spring.factories} entries. The following constructor parameter types are
+ * supported:
*
* {@link Log} - if the resolver needs deferred logging
* {@link Binder} - if the resolver needs to obtain values from the initial
@@ -47,12 +48,12 @@ import org.springframework.core.io.ResourceLoader;
* Resolvers may implement {@link Ordered} or use the {@link Order @Order} annotation. The
* first resolver that supports the given location will be used.
*
- * @param the location type
+ * @param the location type
* @author Phillip Webb
* @author Madhura Bhave
* @since 2.4.0
*/
-public interface ConfigDataLocationResolver {
+public interface ConfigDataLocationResolver {
/**
* Returns if the specified location address can be resolved by this resolver.
@@ -60,35 +61,34 @@ public interface ConfigDataLocationResolver {
* @param location the location to check.
* @return if the location is supported by this resolver
*/
- boolean isResolvable(ConfigDataLocationResolverContext context, String location);
+ boolean isResolvable(ConfigDataLocationResolverContext context, ConfigDataLocation location);
/**
- * Resolve a location string into one or more {@link ConfigDataLocation} instances.
+ * Resolve a {@link ConfigDataLocation} into one or more {@link ConfigDataResource}
+ * instances.
* @param context the location resolver context
* @param location the location that should be resolved
- * @param optional if the location is optional
- * @return a list of resolved locations in ascending priority order. If the same key
- * is contained in more than one of the location, then the later source will win.
+ * @return a list of {@link ConfigDataResource resources} in ascending priority order.
* @throws ConfigDataLocationNotFoundException on a non-optional location that cannot
* be found
+ * @throws ConfigDataResourceNotFoundException if a resolved resource cannot be found
*/
- List resolve(ConfigDataLocationResolverContext context, String location, boolean optional)
- throws ConfigDataLocationNotFoundException;
+ List resolve(ConfigDataLocationResolverContext context, ConfigDataLocation location)
+ throws ConfigDataLocationNotFoundException, ConfigDataResourceNotFoundException;
/**
- * Resolve a location string into one or more {@link ConfigDataLocation} instances
- * based on available profiles. This method is called once profiles have been deduced
- * from the contributed values. By default this method returns an empty list.
+ * Resolve a {@link ConfigDataLocation} into one or more {@link ConfigDataResource}
+ * instances based on available profiles. This method is called once profiles have
+ * been deduced from the contributed values. By default this method returns an empty
+ * list.
* @param context the location resolver context
* @param location the location that should be resolved
- * @param optional if the location is optional
* @param profiles profile information
- * @return a list of resolved locations in ascending priority order.If the same key is
- * contained in more than one of the location, then the later source will win.
+ * @return a list of resolved locations in ascending priority order.
* @throws ConfigDataLocationNotFoundException on a non-optional location that cannot
* be found
*/
- default List resolveProfileSpecific(ConfigDataLocationResolverContext context, String location, boolean optional,
+ default List resolveProfileSpecific(ConfigDataLocationResolverContext context, ConfigDataLocation location,
Profiles profiles) throws ConfigDataLocationNotFoundException {
return Collections.emptyList();
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolverContext.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolverContext.java
index fa8a8bedc4d..2ba29d0b297 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolverContext.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolverContext.java
@@ -19,7 +19,6 @@ package org.springframework.boot.context.config;
import org.springframework.boot.ConfigurableBootstrapContext;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.env.EnvironmentPostProcessor;
-import org.springframework.boot.origin.Origin;
/**
* Context provided to {@link ConfigDataLocationResolver} methods.
@@ -38,11 +37,11 @@ public interface ConfigDataLocationResolverContext {
Binder getBinder();
/**
- * Provides access to the parent location that triggered the resolve or {@code null}
- * if there is no available parent.
+ * Provides access to the parent {@link ConfigDataResource} that triggered the resolve
+ * or {@code null} if there is no available parent.
* @return the parent location
*/
- ConfigDataLocation getParent();
+ ConfigDataResource getParent();
/**
* Provides access to the {@link ConfigurableBootstrapContext} shared across all
@@ -51,11 +50,4 @@ public interface ConfigDataLocationResolverContext {
*/
ConfigurableBootstrapContext getBootstrapContext();
- /**
- * Return the {@link Origin} of a location that's being resolved.
- * @param location the location being resolved
- * @return the {@link Origin} of the location or {@code null}
- */
- Origin getLocationOrigin(String location);
-
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolvers.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolvers.java
index 6b07ffb14e8..ab77cb3adba 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolvers.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolvers.java
@@ -32,8 +32,6 @@ import org.springframework.boot.util.Instantiator;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.SpringFactoriesLoader;
-import org.springframework.core.log.LogMessage;
-import org.springframework.util.StringUtils;
/**
* A collection of {@link ConfigDataLocationResolver} instances loaded via
@@ -44,24 +42,18 @@ import org.springframework.util.StringUtils;
*/
class ConfigDataLocationResolvers {
- private final Log logger;
-
- private final ConfigDataLocationNotFoundAction locationNotFoundAction;
-
private final List> resolvers;
/**
* Create a new {@link ConfigDataLocationResolvers} instance.
* @param logFactory a {@link DeferredLogFactory} used to inject {@link Log} instances
* @param bootstrapContext the bootstrap context
- * @param locationNotFoundAction the action to take if a
- * {@link ConfigDataLocationNotFoundException} is thrown
* @param binder a binder providing values from the initial {@link Environment}
* @param resourceLoader {@link ResourceLoader} to load resource locations
*/
ConfigDataLocationResolvers(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
- ConfigDataLocationNotFoundAction locationNotFoundAction, Binder binder, ResourceLoader resourceLoader) {
- this(logFactory, bootstrapContext, locationNotFoundAction, binder, resourceLoader,
+ Binder binder, ResourceLoader resourceLoader) {
+ this(logFactory, bootstrapContext, binder, resourceLoader,
SpringFactoriesLoader.loadFactoryNames(ConfigDataLocationResolver.class, null));
}
@@ -69,17 +61,12 @@ class ConfigDataLocationResolvers {
* Create a new {@link ConfigDataLocationResolvers} instance.
* @param logFactory a {@link DeferredLogFactory} used to inject {@link Log} instances
* @param bootstrapContext the bootstrap context
- * @param locationNotFoundAction the action to take if a
- * {@link ConfigDataLocationNotFoundException} is thrown
* @param binder {@link Binder} providing values from the initial {@link Environment}
* @param resourceLoader {@link ResourceLoader} to load resource locations
* @param names the {@link ConfigDataLocationResolver} class names
*/
ConfigDataLocationResolvers(DeferredLogFactory logFactory, ConfigurableBootstrapContext bootstrapContext,
- ConfigDataLocationNotFoundAction locationNotFoundAction, Binder binder, ResourceLoader resourceLoader,
- List names) {
- this.logger = logFactory.getLog(getClass());
- this.locationNotFoundAction = locationNotFoundAction;
+ Binder binder, ResourceLoader resourceLoader, List names) {
Instantiator> instantiator = new Instantiator<>(ConfigDataLocationResolver.class,
(availableParameters) -> {
availableParameters.add(Log.class, logFactory::getLog);
@@ -94,10 +81,10 @@ class ConfigDataLocationResolvers {
private List> reorder(List> resolvers) {
List> reordered = new ArrayList<>(resolvers.size());
- ResourceConfigDataLocationResolver resourceResolver = null;
+ StandardConfigDataLocationResolver resourceResolver = null;
for (ConfigDataLocationResolver> resolver : resolvers) {
- if (resolver instanceof ResourceConfigDataLocationResolver) {
- resourceResolver = (ResourceConfigDataLocationResolver) resolver;
+ if (resolver instanceof StandardConfigDataLocationResolver) {
+ resourceResolver = (StandardConfigDataLocationResolver) resolver;
}
else {
reordered.add(resolver);
@@ -109,67 +96,38 @@ class ConfigDataLocationResolvers {
return Collections.unmodifiableList(reordered);
}
- /**
- * Resolve all location strings using the most appropriate
- * {@link ConfigDataLocationResolver}.
- * @param context the location resolver context
- * @param locations the locations to resolve
- * @param profiles the current profiles or {@code null}
- * @return the resolved locations
- */
- List resolveAll(ConfigDataLocationResolverContext context, List locations,
+ List resolve(ConfigDataLocationResolverContext context, ConfigDataLocation location,
Profiles profiles) {
- List resolved = new ArrayList<>(locations.size());
- for (String location : locations) {
- resolved.addAll(resolveAll(context, location, profiles));
- }
- return resolved;
- }
-
- private List resolveAll(ConfigDataLocationResolverContext context, String location,
- Profiles profiles) {
- boolean optional = location != null && location.startsWith(ConfigDataLocation.OPTIONAL_PREFIX);
- location = (!optional) ? location : location.substring(ConfigDataLocation.OPTIONAL_PREFIX.length());
- if (!StringUtils.hasText(location)) {
+ if (location == null) {
return Collections.emptyList();
}
for (ConfigDataLocationResolver> resolver : getResolvers()) {
if (resolver.isResolvable(context, location)) {
- return resolve(resolver, context, optional, location, profiles);
+ return resolve(resolver, context, location, profiles);
}
}
throw new UnsupportedConfigDataLocationException(location);
}
- private List resolve(ConfigDataLocationResolver> resolver,
- ConfigDataLocationResolverContext context, boolean optional, String location, Profiles profiles) {
- List resolved = resolve(location, optional,
- () -> resolver.resolve(context, location, optional));
+ private List resolve(ConfigDataLocationResolver> resolver,
+ ConfigDataLocationResolverContext context, ConfigDataLocation location, Profiles profiles) {
+ List resolved = resolve(location, () -> resolver.resolve(context, location));
if (profiles == null) {
return resolved;
}
- List profileSpecific = resolve(location, optional,
- () -> resolver.resolveProfileSpecific(context, location, optional, profiles));
+ List profileSpecific = resolve(location,
+ () -> resolver.resolveProfileSpecific(context, location, profiles));
return merge(resolved, profileSpecific);
}
- private List resolve(String location, boolean optional,
- Supplier> resolveAction) {
- try {
- List resolved = nonNullList(resolveAction.get());
- if (!resolved.isEmpty() && optional) {
- resolved = OptionalConfigDataLocation.wrapAll(resolved);
- }
- return resolved;
- }
- catch (ConfigDataLocationNotFoundException ex) {
- if (optional) {
- this.logger.trace(LogMessage.format("Skipping missing resource from optional location %s", location));
- return Collections.emptyList();
- }
- this.locationNotFoundAction.handle(this.logger, location, ex);
- return Collections.emptyList();
+ private List resolve(ConfigDataLocation location,
+ Supplier> resolveAction) {
+ List resources = nonNullList(resolveAction.get());
+ List resolved = new ArrayList<>(resources.size());
+ for (ConfigDataResource resource : resources) {
+ resolved.add(new ConfigDataResolutionResult(location, resource));
}
+ return resolved;
}
@SuppressWarnings("unchecked")
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationNotFoundAction.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataNotFoundAction.java
similarity index 63%
rename from spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationNotFoundAction.java
rename to spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataNotFoundAction.java
index b4a3392120f..74f92d14a48 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationNotFoundAction.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataNotFoundAction.java
@@ -21,12 +21,12 @@ import org.apache.commons.logging.Log;
import org.springframework.core.log.LogMessage;
/**
- * Action to take when an uncaught {@link ConfigDataLocationNotFoundException} is thrown.
+ * Action to take when an uncaught {@link ConfigDataNotFoundException} is thrown.
*
* @author Phillip Webb
* @since 2.4.0
*/
-public enum ConfigDataLocationNotFoundAction {
+public enum ConfigDataNotFoundAction {
/**
* Throw the exception to fail startup.
@@ -34,7 +34,7 @@ public enum ConfigDataLocationNotFoundAction {
FAIL {
@Override
- void handle(Log logger, Object location, ConfigDataLocationNotFoundException ex) {
+ void handle(Log logger, ConfigDataNotFoundException ex) {
throw ex;
}
@@ -46,19 +46,17 @@ public enum ConfigDataLocationNotFoundAction {
IGNORE {
@Override
- void handle(Log logger, Object location, ConfigDataLocationNotFoundException ex) {
- logger.trace(LogMessage.format("Ignoring missing resource from location %s", location));
+ void handle(Log logger, ConfigDataNotFoundException ex) {
+ logger.trace(LogMessage.format("Ignoring missing config data %s", ex.getReferenceDescription()));
}
};
/**
* Handle the given exception.
- * @param logger the logger used for output
- * @param location the location being checked (a {@link ConfigDataLocation} or
- * {@code String})
+ * @param logger the logger used for output {@code ConfigDataLocation})
* @param ex the exception to handle
*/
- abstract void handle(Log logger, Object location, ConfigDataLocationNotFoundException ex);
+ abstract void handle(Log logger, ConfigDataNotFoundException ex);
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataNotFoundException.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataNotFoundException.java
new file mode 100644
index 00000000000..2accaeb6158
--- /dev/null
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataNotFoundException.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2012-2020 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
+ *
+ * https://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.context.config;
+
+import org.springframework.boot.origin.OriginProvider;
+
+/**
+ * {@link ConfigDataNotFoundException} thrown when a {@link ConfigData} cannot be found.
+ *
+ * @author Phillip Webb
+ * @since 2.4.0
+ */
+public abstract class ConfigDataNotFoundException extends ConfigDataException implements OriginProvider {
+
+ /**
+ * Create a new {@link ConfigDataNotFoundException} instance.
+ * @param message the exception message
+ * @param cause the exception cause
+ */
+ ConfigDataNotFoundException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Return a description of actual referenced item that could not be found.
+ * @return a description of the referenced items
+ */
+ public abstract String getReferenceDescription();
+
+}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataProperties.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataProperties.java
index c275e774527..6ece3585518 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataProperties.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataProperties.java
@@ -16,11 +16,8 @@
package org.springframework.boot.context.config;
-import java.util.ArrayList;
import java.util.Collections;
-import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Map;
import java.util.function.Predicate;
import org.springframework.boot.cloud.CloudPlatform;
@@ -28,11 +25,9 @@ import org.springframework.boot.context.properties.bind.BindContext;
import org.springframework.boot.context.properties.bind.BindHandler;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
-import org.springframework.boot.context.properties.bind.BoundPropertiesTrackingBindHandler;
import org.springframework.boot.context.properties.bind.Name;
import org.springframework.boot.context.properties.source.ConfigurationProperty;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
-import org.springframework.boot.origin.Origin;
import org.springframework.util.ObjectUtils;
/**
@@ -45,8 +40,6 @@ class ConfigDataProperties {
private static final ConfigurationPropertyName NAME = ConfigurationPropertyName.of("spring.config");
- private static final ConfigurationPropertyName IMPORT_NAME = ConfigurationPropertyName.of("spring.config.import");
-
private static final ConfigurationPropertyName LEGACY_PROFILES_NAME = ConfigurationPropertyName
.of("spring.profiles");
@@ -54,57 +47,33 @@ class ConfigDataProperties {
private static final Bindable BINDABLE_STRING_ARRAY = Bindable.of(String[].class);
- private final List imports;
+ private final List imports;
private final Activate activate;
- private final Map boundProperties;
-
/**
* Create a new {@link ConfigDataProperties} instance.
* @param imports the imports requested
* @param activate the activate properties
*/
- ConfigDataProperties(@Name("import") List imports, Activate activate) {
+ ConfigDataProperties(@Name("import") List imports, Activate activate) {
this(imports, activate, Collections.emptyList());
}
- private ConfigDataProperties(List imports, Activate activate, List boundProperties) {
+ private ConfigDataProperties(List imports, Activate activate,
+ List boundProperties) {
this.imports = (imports != null) ? imports : Collections.emptyList();
this.activate = activate;
- this.boundProperties = mapByName(boundProperties);
- }
-
- private Map mapByName(
- List boundProperties) {
- Map result = new LinkedHashMap<>();
- boundProperties.forEach((property) -> result.put(property.getName(), property));
- return Collections.unmodifiableMap(result);
}
/**
* Return any additional imports requested.
* @return the requested imports
*/
- List getImports() {
+ List getImports() {
return this.imports;
}
- /**
- * Return the {@link Origin} of a given import location.
- * @param importLocation the import location to check
- * @return the origin of the import or {@code null}
- */
- Origin getImportOrigin(String importLocation) {
- int index = this.imports.indexOf(importLocation);
- if (index == -1) {
- return null;
- }
- ConfigurationProperty bound = this.boundProperties.get(IMPORT_NAME.append("[" + index + "]"));
- bound = (bound != null) ? bound : this.boundProperties.get(IMPORT_NAME);
- return (bound != null) ? bound.getOrigin() : null;
- }
-
/**
* Return {@code true} if the properties indicate that the config data property source
* is active for the given activation context.
@@ -130,10 +99,6 @@ class ConfigDataProperties {
return new ConfigDataProperties(this.imports, new Activate(this.activate.onCloudPlatform, legacyProfiles));
}
- ConfigDataProperties withBoundProperties(List boundProperties) {
- return new ConfigDataProperties(this.imports, this.activate, boundProperties);
- }
-
/**
* Factory method used to create {@link ConfigDataProperties} from the given
* {@link Binder}.
@@ -144,16 +109,14 @@ class ConfigDataProperties {
LegacyProfilesBindHandler legacyProfilesBindHandler = new LegacyProfilesBindHandler();
String[] legacyProfiles = binder.bind(LEGACY_PROFILES_NAME, BINDABLE_STRING_ARRAY, legacyProfilesBindHandler)
.orElse(null);
- List boundProperties = new ArrayList<>();
- ConfigDataProperties properties = binder
- .bind(NAME, BINDABLE_PROPERTIES, new BoundPropertiesTrackingBindHandler(boundProperties::add))
+ ConfigDataProperties properties = binder.bind(NAME, BINDABLE_PROPERTIES, new ConfigDataLocationBindHandler())
.orElse(null);
if (!ObjectUtils.isEmpty(legacyProfiles)) {
properties = (properties != null)
? properties.withLegacyProfiles(legacyProfiles, legacyProfilesBindHandler.getProperty())
: new ConfigDataProperties(null, new Activate(null, legacyProfiles));
}
- return (properties != null) ? properties.withBoundProperties(boundProperties) : null;
+ return properties;
}
/**
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataResolutionResult.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataResolutionResult.java
new file mode 100644
index 00000000000..09c7d7aed3f
--- /dev/null
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataResolutionResult.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2012-2020 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
+ *
+ * https://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.context.config;
+
+/**
+ * Result returned from {@link ConfigDataLocationResolvers} containing both the
+ * {@link ConfigDataResource} and the original {@link ConfigDataLocation}.
+ *
+ * @author Phillip Webb
+ */
+class ConfigDataResolutionResult {
+
+ private final ConfigDataLocation location;
+
+ private final ConfigDataResource resource;
+
+ ConfigDataResolutionResult(ConfigDataLocation location, ConfigDataResource resource) {
+ this.location = location;
+ this.resource = resource;
+ }
+
+ ConfigDataLocation getLocation() {
+ return this.location;
+ }
+
+ ConfigDataResource getResource() {
+ return this.resource;
+ }
+
+}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ResourceConfigDataLoader.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataResource.java
similarity index 59%
rename from spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ResourceConfigDataLoader.java
rename to spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataResource.java
index 743cfe7b950..e16dbd4b0bd 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ResourceConfigDataLoader.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataResource.java
@@ -16,23 +16,15 @@
package org.springframework.boot.context.config;
-import java.io.IOException;
-
-import org.springframework.core.io.Resource;
-
/**
- * {@link ConfigDataLoader} for {@link Resource} backed locations.
+ * A single resource from which {@link ConfigData} can be loaded. Implementations must
+ * implement a valid {@link #equals(Object) equals}, {@link #hashCode() hashCode} and
+ * {@link #toString() toString} methods.
*
* @author Phillip Webb
* @author Madhura Bhave
* @since 2.4.0
*/
-public class ResourceConfigDataLoader implements ConfigDataLoader {
-
- @Override
- public ConfigData load(ConfigDataLoaderContext context, ResourceConfigDataLocation location) throws IOException {
- ConfigDataLocationNotFoundException.throwIfDoesNotExist(location, location.getResource());
- return new ConfigData(location.load());
- }
+public abstract class ConfigDataResource {
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataResourceNotFoundException.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataResourceNotFoundException.java
new file mode 100644
index 00000000000..523b4642388
--- /dev/null
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataResourceNotFoundException.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2012-2020 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
+ *
+ * https://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.context.config;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import org.springframework.boot.origin.Origin;
+import org.springframework.core.io.Resource;
+import org.springframework.util.Assert;
+
+/**
+ * {@link ConfigDataNotFoundException} thrown when a {@link ConfigDataResource} cannot be
+ * found.
+ *
+ * @author Phillip Webb
+ * @since 2.4.0
+ */
+public class ConfigDataResourceNotFoundException extends ConfigDataNotFoundException {
+
+ private final ConfigDataResource resource;
+
+ private final ConfigDataLocation location;
+
+ /**
+ * Create a new {@link ConfigDataResourceNotFoundException} instance.
+ * @param resource the resource that could not be found
+ */
+ public ConfigDataResourceNotFoundException(ConfigDataResource resource) {
+ this(resource, null);
+ }
+
+ /**
+ * Create a new {@link ConfigDataResourceNotFoundException} instance.
+ * @param resource the resource that could not be found
+ * @param cause the exception cause
+ */
+ public ConfigDataResourceNotFoundException(ConfigDataResource resource, Throwable cause) {
+ this(resource, null, cause);
+ }
+
+ private ConfigDataResourceNotFoundException(ConfigDataResource resource, ConfigDataLocation location,
+ Throwable cause) {
+ super(getMessage(resource, location), cause);
+ Assert.notNull(resource, "Resource must not be null");
+ this.resource = resource;
+ this.location = location;
+ }
+
+ /**
+ * Return the resource that could not be found.
+ * @return the resource
+ */
+ public ConfigDataResource getResource() {
+ return this.resource;
+ }
+
+ /**
+ * Return the original location that was resolved to determine the resource.
+ * @return the location or {@code null} if no location is availble
+ */
+ public ConfigDataLocation getLocation() {
+ return this.location;
+ }
+
+ @Override
+ public Origin getOrigin() {
+ return Origin.from(this.location);
+ }
+
+ @Override
+ public String getReferenceDescription() {
+ return getReferenceDescription(this.resource, this.location);
+ }
+
+ /**
+ * Create a new {@link ConfigDataResourceNotFoundException} instance with a location.
+ * @param location the location to set
+ * @return a new {@link ConfigDataResourceNotFoundException} instance
+ */
+ ConfigDataResourceNotFoundException withLocation(ConfigDataLocation location) {
+ return new ConfigDataResourceNotFoundException(this.resource, location, getCause());
+ }
+
+ private static String getMessage(ConfigDataResource resource, ConfigDataLocation location) {
+ return String.format("Config data %s cannot be found", getReferenceDescription(resource, location));
+ }
+
+ private static String getReferenceDescription(ConfigDataResource resource, ConfigDataLocation location) {
+ String description = String.format("resource '%s'", resource);
+ if (location != null) {
+ description += String.format(" via location '%s'", location);
+ }
+ return description;
+ }
+
+ /**
+ * Throw a {@link ConfigDataNotFoundException} if the specified {@link Path} does not
+ * exist.
+ * @param resource the config data resource
+ * @param pathToCheck the path to check
+ */
+ public static void throwIfDoesNotExist(ConfigDataResource resource, Path pathToCheck) {
+ throwIfDoesNotExist(resource, Files.exists(pathToCheck));
+ }
+
+ /**
+ * Throw a {@link ConfigDataNotFoundException} if the specified {@link File} does not
+ * exist.
+ * @param resource the config data resource
+ * @param fileToCheck the file to check
+ */
+ public static void throwIfDoesNotExist(ConfigDataResource resource, File fileToCheck) {
+ throwIfDoesNotExist(resource, fileToCheck.exists());
+ }
+
+ /**
+ * Throw a {@link ConfigDataNotFoundException} if the specified {@link Resource} does
+ * not exist.
+ * @param resource the config data resource
+ * @param resourceToCheck the resource to check
+ */
+ public static void throwIfDoesNotExist(ConfigDataResource resource, Resource resourceToCheck) {
+ throwIfDoesNotExist(resource, resourceToCheck.exists());
+ }
+
+ private static void throwIfDoesNotExist(ConfigDataResource resource, boolean exists) {
+ if (!exists) {
+ throw new ConfigDataResourceNotFoundException(resource);
+ }
+ }
+
+}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java
index 52cf90550f6..46a32071424 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigFileApplicationListener.java
@@ -426,25 +426,13 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
private void load(Profile profile, DocumentFilterFactory filterFactory, DocumentConsumer consumer) {
getSearchLocations().forEach((location) -> {
+ String nonOptionalLocation = ConfigDataLocation.of(location).getValue();
boolean isDirectory = location.endsWith("/");
Set names = isDirectory ? getSearchNames() : NO_SEARCH_NAMES;
- names.forEach((name) -> load(stripOptionalPrefix(location), name, profile, filterFactory, consumer));
+ names.forEach((name) -> load(nonOptionalLocation, name, profile, filterFactory, consumer));
});
}
- /**
- * Strip the optional prefix from the location. When using the legacy method, all
- * locations are optional.
- * @param location the location to strip
- * @return the stripped location
- */
- private String stripOptionalPrefix(String location) {
- if (location != null && location.startsWith(ConfigDataLocation.OPTIONAL_PREFIX)) {
- return location.substring(ConfigDataLocation.OPTIONAL_PREFIX.length());
- }
- return location;
- }
-
private void load(String location, String name, Profile profile, DocumentFilterFactory filterFactory,
DocumentConsumer consumer) {
if (!StringUtils.hasText(name)) {
@@ -552,9 +540,9 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
}
}
- private String getLocationName(String location, Resource resource) {
- if (!location.contains("*")) {
- return location;
+ private String getLocationName(String locationReference, Resource resource) {
+ if (!locationReference.contains("*")) {
+ return locationReference;
}
if (resource instanceof FileSystemResource) {
return ((FileSystemResource) resource).getPath();
@@ -562,24 +550,24 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
return resource.getDescription();
}
- private Resource[] getResources(String location) {
+ private Resource[] getResources(String locationReference) {
try {
- if (location.contains("*")) {
- return getResourcesFromPatternLocation(location);
+ if (locationReference.contains("*")) {
+ return getResourcesFromPatternLocationReference(locationReference);
}
- return new Resource[] { this.resourceLoader.getResource(location) };
+ return new Resource[] { this.resourceLoader.getResource(locationReference) };
}
catch (Exception ex) {
return EMPTY_RESOURCES;
}
}
- private Resource[] getResourcesFromPatternLocation(String location) throws IOException {
- String directoryPath = location.substring(0, location.indexOf("*/"));
+ private Resource[] getResourcesFromPatternLocationReference(String locationReference) throws IOException {
+ String directoryPath = locationReference.substring(0, locationReference.indexOf("*/"));
Resource resource = this.resourceLoader.getResource(directoryPath);
File[] files = resource.getFile().listFiles(File::isDirectory);
if (files != null) {
- String fileName = location.substring(location.lastIndexOf("/") + 1);
+ String fileName = locationReference.substring(locationReference.lastIndexOf("/") + 1);
Arrays.sort(files, FILE_COMPARATOR);
return Arrays.stream(files).map((file) -> file.listFiles((dir, name) -> name.equals(fileName)))
.filter(Objects::nonNull).flatMap((Function>) Arrays::stream)
@@ -622,7 +610,8 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
}).collect(Collectors.toList());
}
- private StringBuilder getDescription(String prefix, String location, Resource resource, Profile profile) {
+ private StringBuilder getDescription(String prefix, String locationReference, Resource resource,
+ Profile profile) {
StringBuilder result = new StringBuilder(prefix);
try {
if (resource != null) {
@@ -630,12 +619,12 @@ public class ConfigFileApplicationListener implements EnvironmentPostProcessor,
result.append("'");
result.append(uri);
result.append("' (");
- result.append(location);
+ result.append(locationReference);
result.append(")");
}
}
catch (IOException ex) {
- result.append(location);
+ result.append(locationReference);
}
if (profile != null) {
result.append(" for profile ");
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigTreeConfigDataLoader.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigTreeConfigDataLoader.java
index cc010983300..254a02a0212 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigTreeConfigDataLoader.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigTreeConfigDataLoader.java
@@ -29,12 +29,13 @@ import org.springframework.boot.env.ConfigTreePropertySource;
* @author Phillip Webb
* @since 2.4.0
*/
-public class ConfigTreeConfigDataLoader implements ConfigDataLoader {
+public class ConfigTreeConfigDataLoader implements ConfigDataLoader {
@Override
- public ConfigData load(ConfigDataLoaderContext context, ConfigTreeConfigDataLocation location) throws IOException {
- ConfigDataLocationNotFoundException.throwIfDoesNotExist(location, location.getPath());
- Path path = location.getPath();
+ public ConfigData load(ConfigDataLoaderContext context, ConfigTreeConfigDataResource resource)
+ throws IOException, ConfigDataResourceNotFoundException {
+ Path path = resource.getPath();
+ ConfigDataResourceNotFoundException.throwIfDoesNotExist(resource, path);
String name = "Config tree '" + path + "'";
ConfigTreePropertySource source = new ConfigTreePropertySource(name, path);
return new ConfigData(Collections.singletonList(source));
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigTreeConfigDataLocationResolver.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigTreeConfigDataLocationResolver.java
index e507bf6aa30..9810491c9ca 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigTreeConfigDataLocationResolver.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigTreeConfigDataLocationResolver.java
@@ -26,19 +26,19 @@ import java.util.List;
* @author Phillip Webb
* @since 2.4.0
*/
-public class ConfigTreeConfigDataLocationResolver implements ConfigDataLocationResolver {
+public class ConfigTreeConfigDataLocationResolver implements ConfigDataLocationResolver {
private static final String PREFIX = "configtree:";
@Override
- public boolean isResolvable(ConfigDataLocationResolverContext context, String location) {
- return location.startsWith(PREFIX);
+ public boolean isResolvable(ConfigDataLocationResolverContext context, ConfigDataLocation location) {
+ return location.hasPrefix(PREFIX);
}
@Override
- public List resolve(ConfigDataLocationResolverContext context, String location,
- boolean optional) {
- ConfigTreeConfigDataLocation resolved = new ConfigTreeConfigDataLocation(location.substring(PREFIX.length()));
+ public List resolve(ConfigDataLocationResolverContext context,
+ ConfigDataLocation location) {
+ ConfigTreeConfigDataResource resolved = new ConfigTreeConfigDataResource(location.getNonPrefixedValue(PREFIX));
return Collections.singletonList(resolved);
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigTreeConfigDataLocation.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigTreeConfigDataResource.java
similarity index 85%
rename from spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigTreeConfigDataLocation.java
rename to spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigTreeConfigDataResource.java
index 9e9a135a4d1..4cb6eeb9b53 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigTreeConfigDataLocation.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigTreeConfigDataResource.java
@@ -24,18 +24,18 @@ import org.springframework.boot.env.ConfigTreePropertySource;
import org.springframework.util.Assert;
/**
- * {@link ConfigDataLocation} backed by a config tree directory.
+ * {@link ConfigDataResource} backed by a config tree directory.
*
* @author Madhura Bhave
* @author Phillip Webb
* @since 2.4.0
* @see ConfigTreePropertySource
*/
-public class ConfigTreeConfigDataLocation extends ConfigDataLocation {
+public class ConfigTreeConfigDataResource extends ConfigDataResource {
private final Path path;
- ConfigTreeConfigDataLocation(String path) {
+ ConfigTreeConfigDataResource(String path) {
Assert.notNull(path, "Path must not be null");
this.path = Paths.get(path).toAbsolutePath();
}
@@ -52,7 +52,7 @@ public class ConfigTreeConfigDataLocation extends ConfigDataLocation {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
- ConfigTreeConfigDataLocation other = (ConfigTreeConfigDataLocation) obj;
+ ConfigTreeConfigDataResource other = (ConfigTreeConfigDataResource) obj;
return Objects.equals(this.path, other.path);
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/InactiveConfigDataAccessException.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/InactiveConfigDataAccessException.java
index 6b535117b51..143e68932b5 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/InactiveConfigDataAccessException.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/InactiveConfigDataAccessException.java
@@ -35,7 +35,7 @@ public class InactiveConfigDataAccessException extends ConfigDataException {
private final PropertySource> propertySource;
- private final ConfigDataLocation location;
+ private final ConfigDataResource location;
private final String propertyName;
@@ -44,12 +44,12 @@ public class InactiveConfigDataAccessException extends ConfigDataException {
/**
* Create a new {@link InactiveConfigDataAccessException} instance.
* @param propertySource the inactive property source
- * @param location the {@link ConfigDataLocation} of the property source or
+ * @param location the {@link ConfigDataResource} of the property source or
* {@code null} if the source was not loaded from {@link ConfigData}.
* @param propertyName the name of the property
* @param origin the origin or the property or {@code null}
*/
- InactiveConfigDataAccessException(PropertySource> propertySource, ConfigDataLocation location,
+ InactiveConfigDataAccessException(PropertySource> propertySource, ConfigDataResource location,
String propertyName, Origin origin) {
super(getMessage(propertySource, location, propertyName, origin), null);
this.propertySource = propertySource;
@@ -58,7 +58,7 @@ public class InactiveConfigDataAccessException extends ConfigDataException {
this.origin = origin;
}
- private static String getMessage(PropertySource> propertySource, ConfigDataLocation location, String propertyName,
+ private static String getMessage(PropertySource> propertySource, ConfigDataResource location, String propertyName,
Origin origin) {
StringBuilder message = new StringBuilder("Inactive property source '");
message.append(propertySource.getName());
@@ -86,11 +86,11 @@ public class InactiveConfigDataAccessException extends ConfigDataException {
}
/**
- * Return the {@link ConfigDataLocation} of the property source or {@code null} if the
+ * Return the {@link ConfigDataResource} of the property source or {@code null} if the
* source was not loaded from {@link ConfigData}.
* @return the config data location or {@code null}
*/
- public ConfigDataLocation getLocation() {
+ public ConfigDataResource getLocation() {
return this.location;
}
@@ -121,7 +121,7 @@ public class InactiveConfigDataAccessException extends ConfigDataException {
ConfigurationProperty property = (source != null) ? source.getConfigurationProperty(name) : null;
if (property != null) {
PropertySource> propertySource = contributor.getPropertySource();
- ConfigDataLocation location = contributor.getLocation();
+ ConfigDataResource location = contributor.getResource();
throw new InactiveConfigDataAccessException(propertySource, location, name.toString(),
property.getOrigin());
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/InvalidConfigDataPropertyException.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/InvalidConfigDataPropertyException.java
index b44775de0a2..fc77435d70f 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/InvalidConfigDataPropertyException.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/InvalidConfigDataPropertyException.java
@@ -47,10 +47,10 @@ public class InvalidConfigDataPropertyException extends ConfigDataException {
private final ConfigurationPropertyName replacement;
- private final ConfigDataLocation location;
+ private final ConfigDataResource location;
InvalidConfigDataPropertyException(ConfigurationProperty property, ConfigurationPropertyName replacement,
- ConfigDataLocation location) {
+ ConfigDataResource location) {
super(getMessage(property, replacement, location), null);
this.property = property;
this.replacement = replacement;
@@ -66,11 +66,11 @@ public class InvalidConfigDataPropertyException extends ConfigDataException {
}
/**
- * Return the {@link ConfigDataLocation} of the invalid property or {@code null} if
+ * Return the {@link ConfigDataResource} of the invalid property or {@code null} if
* the source was not loaded from {@link ConfigData}.
* @return the config data location or {@code null}
*/
- public ConfigDataLocation getLocation() {
+ public ConfigDataResource getLocation() {
return this.location;
}
@@ -97,14 +97,14 @@ public class InvalidConfigDataPropertyException extends ConfigDataException {
WARNING.forEach((invalid, replacement) -> {
ConfigurationProperty property = propertySource.getConfigurationProperty(invalid);
if (property != null) {
- logger.warn(getMessage(property, replacement, contributor.getLocation()));
+ logger.warn(getMessage(property, replacement, contributor.getResource()));
}
});
}
}
private static String getMessage(ConfigurationProperty property, ConfigurationPropertyName replacement,
- ConfigDataLocation location) {
+ ConfigDataResource location) {
StringBuilder message = new StringBuilder("Property '");
message.append(property.getName());
if (location != null) {
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/OptionalConfigDataLocation.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/OptionalConfigDataLocation.java
deleted file mode 100644
index 3954f650517..00000000000
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/OptionalConfigDataLocation.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2012-2020 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
- *
- * https://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.context.config;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * {@link ConfigDataLocation} wrapper used to indicate that it's optional.
- *
- * @author Phillip Webb
- */
-class OptionalConfigDataLocation extends ConfigDataLocation {
-
- private ConfigDataLocation location;
-
- OptionalConfigDataLocation(ConfigDataLocation location) {
- this.location = location;
- }
-
- ConfigDataLocation getLocation() {
- return this.location;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null || getClass() != obj.getClass()) {
- return false;
- }
- OptionalConfigDataLocation other = (OptionalConfigDataLocation) obj;
- return this.location.equals(other.location);
- }
-
- @Override
- public int hashCode() {
- return this.location.hashCode();
- }
-
- @Override
- public String toString() {
- return this.location.toString();
- }
-
- static List wrapAll(List locations) {
- List wrapped = new ArrayList<>(locations.size());
- locations.forEach((location) -> wrapped.add(new OptionalConfigDataLocation(location)));
- return wrapped;
- }
-
- @SuppressWarnings("unchecked")
- static L unwrap(ConfigDataLocation wrapped) {
- return (L) ((OptionalConfigDataLocation) wrapped).getLocation();
- }
-
-}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ResourceConfigDataLocationResolver.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ResourceConfigDataLocationResolver.java
deleted file mode 100644
index c03b9d12e5b..00000000000
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ResourceConfigDataLocationResolver.java
+++ /dev/null
@@ -1,423 +0,0 @@
-/*
- * Copyright 2012-2020 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
- *
- * https://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.context.config;
-
-import java.io.File;
-import java.io.FilenameFilter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.commons.logging.Log;
-
-import org.springframework.boot.context.properties.bind.Binder;
-import org.springframework.boot.env.PropertySourceLoader;
-import org.springframework.boot.origin.Origin;
-import org.springframework.core.Ordered;
-import org.springframework.core.env.Environment;
-import org.springframework.core.io.FileSystemResource;
-import org.springframework.core.io.Resource;
-import org.springframework.core.io.ResourceLoader;
-import org.springframework.core.io.support.ResourcePatternResolver;
-import org.springframework.core.io.support.SpringFactoriesLoader;
-import org.springframework.core.log.LogMessage;
-import org.springframework.util.Assert;
-import org.springframework.util.ResourceUtils;
-import org.springframework.util.StringUtils;
-
-/**
- * {@link ConfigDataLocationResolver} for standard locations.
- *
- * @author Madhura Bhave
- * @author Phillip Webb
- * @since 2.4.0
- */
-public class ResourceConfigDataLocationResolver
- implements ConfigDataLocationResolver, Ordered {
-
- private static final String PREFIX = "resource:";
-
- static final String CONFIG_NAME_PROPERTY = "spring.config.name";
-
- private static final String[] DEFAULT_CONFIG_NAMES = { "application" };
-
- private static final Resource[] EMPTY_RESOURCES = {};
-
- private static final Comparator FILE_COMPARATOR = Comparator.comparing(File::getAbsolutePath);
-
- private static final Pattern URL_PREFIX = Pattern.compile("^([a-zA-Z][a-zA-Z0-9*]*?:)(.*$)");
-
- private static final Pattern EXTENSION_HINT_PATTERN = Pattern.compile("^(.*)\\[(\\.\\w+)\\](?!\\[)$");
-
- private static final String NO_PROFILE = null;
-
- private final Log logger;
-
- private final List propertySourceLoaders;
-
- private final String[] configNames;
-
- private final ResourceLoader resourceLoader;
-
- /**
- * Create a new {@link ResourceConfigDataLocationResolver} instance.
- * @param logger the logger to use
- * @param binder a binder backed by the initial {@link Environment}
- * @param resourceLoader a {@link ResourceLoader} used to load resources
- */
- public ResourceConfigDataLocationResolver(Log logger, Binder binder, ResourceLoader resourceLoader) {
- this.logger = logger;
- this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
- getClass().getClassLoader());
- this.configNames = getConfigNames(binder);
- this.resourceLoader = resourceLoader;
- }
-
- private String[] getConfigNames(Binder binder) {
- String[] configNames = binder.bind(CONFIG_NAME_PROPERTY, String[].class).orElse(DEFAULT_CONFIG_NAMES);
- for (String configName : configNames) {
- validateConfigName(configName);
- }
- return configNames;
- }
-
- private void validateConfigName(String name) {
- Assert.state(!name.contains("*"), () -> "Config name '" + name + "' cannot contain '*'");
- }
-
- @Override
- public int getOrder() {
- return Ordered.LOWEST_PRECEDENCE;
- }
-
- @Override
- public boolean isResolvable(ConfigDataLocationResolverContext context, String location) {
- return true;
- }
-
- @Override
- public List resolve(ConfigDataLocationResolverContext context, String location,
- boolean optional) {
- return resolve(location, getResolvables(context, location, optional));
- }
-
- @Override
- public List resolveProfileSpecific(ConfigDataLocationResolverContext context,
- String location, boolean optional, Profiles profiles) {
- return resolve(location, getProfileSpecificResolvables(context, location, optional, profiles));
- }
-
- private Set getResolvables(ConfigDataLocationResolverContext context, String location,
- boolean optional) {
- Origin origin = context.getLocationOrigin(location);
- String resourceLocation = getResourceLocation(context, location);
- try {
- if (isDirectoryLocation(resourceLocation)) {
- return getResolvablesForDirectory(resourceLocation, optional, NO_PROFILE, origin);
- }
- return getResolvablesForFile(resourceLocation, optional, NO_PROFILE, origin);
- }
- catch (RuntimeException ex) {
- throw new IllegalStateException("Unable to load config data from '" + location + "'", ex);
- }
- }
-
- private Set getProfileSpecificResolvables(ConfigDataLocationResolverContext context, String location,
- boolean optional, Profiles profiles) {
- Origin origin = context.getLocationOrigin(location);
- Set resolvables = new LinkedHashSet<>();
- String resourceLocation = getResourceLocation(context, location);
- for (String profile : profiles) {
- resolvables.addAll(getResolvables(resourceLocation, optional, profile, origin));
- }
- return resolvables;
- }
-
- private String getResourceLocation(ConfigDataLocationResolverContext context, String location) {
- String resourceLocation = (location.startsWith(PREFIX)) ? location.substring(PREFIX.length()) : location;
- boolean isAbsolute = resourceLocation.startsWith("/") || URL_PREFIX.matcher(resourceLocation).matches();
- if (isAbsolute) {
- return resourceLocation;
- }
- ConfigDataLocation parent = context.getParent();
- if (parent instanceof ResourceConfigDataLocation) {
- String parentLocation = ((ResourceConfigDataLocation) parent).getLocation();
- String parentDirectory = parentLocation.substring(0, parentLocation.lastIndexOf("/") + 1);
- return parentDirectory + resourceLocation;
- }
- return resourceLocation;
- }
-
- private Set getResolvables(String resourceLocation, boolean optional, String profile, Origin origin) {
- if (isDirectoryLocation(resourceLocation)) {
- return getResolvablesForDirectory(resourceLocation, optional, profile, origin);
- }
- return getResolvablesForFile(resourceLocation, optional, profile, origin);
- }
-
- private Set getResolvablesForDirectory(String directoryLocation, boolean optional, String profile,
- Origin origin) {
- Set resolvables = new LinkedHashSet<>();
- for (String name : this.configNames) {
- String rootLocation = directoryLocation + name;
- for (PropertySourceLoader loader : this.propertySourceLoaders) {
- for (String extension : loader.getFileExtensions()) {
- Resolvable resolvable = new Resolvable(directoryLocation, rootLocation, optional, profile,
- extension, origin, loader);
- resolvables.add(resolvable);
- }
- }
- }
- return resolvables;
- }
-
- private Set getResolvablesForFile(String fileLocation, boolean optional, String profile,
- Origin origin) {
- Matcher extensionHintMatcher = EXTENSION_HINT_PATTERN.matcher(fileLocation);
- boolean extensionHintLocation = extensionHintMatcher.matches();
- if (extensionHintLocation) {
- fileLocation = extensionHintMatcher.group(1) + extensionHintMatcher.group(2);
- }
- for (PropertySourceLoader loader : this.propertySourceLoaders) {
- String extension = getLoadableFileExtension(loader, fileLocation);
- if (extension != null) {
- String root = fileLocation.substring(0, fileLocation.length() - extension.length() - 1);
- return Collections.singleton(new Resolvable(null, root, optional, profile,
- (!extensionHintLocation) ? extension : null, origin, loader));
- }
- }
- throw new IllegalStateException("File extension is not known to any PropertySourceLoader. "
- + "If the location is meant to reference a directory, it must end in '/'");
- }
-
- private String getLoadableFileExtension(PropertySourceLoader loader, String resourceLocation) {
- for (String fileExtension : loader.getFileExtensions()) {
- if (StringUtils.endsWithIgnoreCase(resourceLocation, fileExtension)) {
- return fileExtension;
- }
- }
- return null;
- }
-
- private boolean isDirectoryLocation(String resourceLocation) {
- return resourceLocation.endsWith("/");
- }
-
- private List resolve(String location, Set resolvables) {
- List resolved = new ArrayList<>();
- for (Resolvable resolvable : resolvables) {
- resolved.addAll(resolve(location, resolvable));
- }
- if (resolved.isEmpty()) {
- assertNonOptionalDirectories(location, resolvables);
- }
- return resolved;
- }
-
- private void assertNonOptionalDirectories(String location, Set resolvables) {
- for (Resolvable resolvable : resolvables) {
- if (resolvable.isNonOptionalDirectory()) {
- Resource resource = loadResource(resolvable.getDirectory());
- ResourceConfigDataLocation resourceLocation = createConfigResourceLocation(location, resolvable,
- resource);
- ConfigDataLocationNotFoundException.throwIfDoesNotExist(resourceLocation, resource);
- }
- }
- }
-
- private List resolve(String location, Resolvable resolvable) {
- if (!resolvable.isPatternLocation()) {
- return resolveNonPattern(location, resolvable);
- }
- return resolvePattern(location, resolvable);
- }
-
- private List resolveNonPattern(String location, Resolvable resolvable) {
- Resource resource = loadResource(resolvable.getResourceLocation());
- if (!resource.exists() && resolvable.isSkippable()) {
- logSkippingResource(resolvable);
- return Collections.emptyList();
- }
- return Collections.singletonList(createConfigResourceLocation(location, resolvable, resource));
- }
-
- private List resolvePattern(String location, Resolvable resolvable) {
- validatePatternLocation(resolvable.getResourceLocation());
- List resolved = new ArrayList<>();
- for (Resource resource : getResourcesFromResourceLocationPattern(resolvable.getResourceLocation())) {
- if (!resource.exists() && resolvable.isSkippable()) {
- logSkippingResource(resolvable);
- }
- else {
- resolved.add(createConfigResourceLocation(location, resolvable, resource));
- }
- }
- return resolved;
- }
-
- private void logSkippingResource(Resolvable resolvable) {
- this.logger.trace(LogMessage.format("Skipping missing resource location %s", resolvable.getResourceLocation()));
- }
-
- private ResourceConfigDataLocation createConfigResourceLocation(String location, Resolvable resolvable,
- Resource resource) {
- String name = String.format("Resource config '%s' imported via location \"%s\"",
- resolvable.getResourceLocation(), location);
- return new ResourceConfigDataLocation(name, resource, resolvable.getOrigin(), resolvable.getLoader());
- }
-
- private void validatePatternLocation(String resourceLocation) {
- Assert.state(!resourceLocation.startsWith(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX),
- "Classpath wildcard patterns cannot be used as a search location");
- Assert.state(StringUtils.countOccurrencesOf(resourceLocation, "*") == 1,
- () -> "Search location '" + resourceLocation + "' cannot contain multiple wildcards");
- String directoryPath = resourceLocation.substring(0, resourceLocation.lastIndexOf("/") + 1);
- Assert.state(directoryPath.endsWith("*/"),
- () -> "Search location '" + resourceLocation + "' must end with '*/'");
- }
-
- private Resource[] getResourcesFromResourceLocationPattern(String resourceLocationPattern) {
- String directoryPath = resourceLocationPattern.substring(0, resourceLocationPattern.indexOf("*/"));
- String fileName = resourceLocationPattern.substring(resourceLocationPattern.lastIndexOf("/") + 1);
- Resource directoryResource = loadResource(directoryPath);
- if (!directoryResource.exists()) {
- return new Resource[] { directoryResource };
- }
- File directory = getDirectory(resourceLocationPattern, directoryResource);
- File[] subDirectories = directory.listFiles(File::isDirectory);
- if (subDirectories == null) {
- return EMPTY_RESOURCES;
- }
- Arrays.sort(subDirectories, FILE_COMPARATOR);
- List resources = new ArrayList<>();
- FilenameFilter filter = (dir, name) -> name.equals(fileName);
- for (File subDirectory : subDirectories) {
- File[] files = subDirectory.listFiles(filter);
- if (files != null) {
- Arrays.stream(files).map(FileSystemResource::new).forEach(resources::add);
- }
- }
- return resources.toArray(EMPTY_RESOURCES);
- }
-
- private Resource loadResource(String location) {
- location = StringUtils.cleanPath(location);
- if (!ResourceUtils.isUrl(location)) {
- location = ResourceUtils.FILE_URL_PREFIX + location;
- }
- return this.resourceLoader.getResource(location);
- }
-
- private File getDirectory(String patternLocation, Resource resource) {
- try {
- File directory = resource.getFile();
- Assert.state(directory.isDirectory(), () -> "'" + directory + "' is not a directory");
- return directory;
- }
- catch (Exception ex) {
- throw new IllegalStateException(
- "Unable to load config data resource from pattern '" + patternLocation + "'", ex);
- }
- }
-
- /**
- * A resource location that could be resolved by this resolver.
- */
- private static class Resolvable {
-
- private final String directory;
-
- private final String resourceLocation;
-
- private final boolean optional;
-
- private final String profile;
-
- private Origin origin;
-
- private final PropertySourceLoader loader;
-
- Resolvable(String directory, String rootLocation, boolean optional, String profile, String extension,
- Origin origin, PropertySourceLoader loader) {
- String profileSuffix = (StringUtils.hasText(profile)) ? "-" + profile : "";
- this.directory = directory;
- this.resourceLocation = rootLocation + profileSuffix + ((extension != null) ? "." + extension : "");
- this.optional = optional;
- this.profile = profile;
- this.loader = loader;
- this.origin = origin;
- }
-
- boolean isNonOptionalDirectory() {
- return !this.optional && this.directory != null;
- }
-
- String getDirectory() {
- return this.directory;
- }
-
- boolean isSkippable() {
- return this.optional || this.directory != null || this.profile != null;
- }
-
- boolean isPatternLocation() {
- return this.resourceLocation.contains("*");
- }
-
- String getResourceLocation() {
- return this.resourceLocation;
- }
-
- Origin getOrigin() {
- return this.origin;
- }
-
- PropertySourceLoader getLoader() {
- return this.loader;
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if ((obj == null) || (getClass() != obj.getClass())) {
- return false;
- }
- Resolvable other = (Resolvable) obj;
- return this.resourceLocation.equals(other.resourceLocation);
- }
-
- @Override
- public int hashCode() {
- return this.resourceLocation.hashCode();
- }
-
- @Override
- public String toString() {
- return this.resourceLocation;
- }
-
- }
-
-}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/StandardConfigDataLoader.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/StandardConfigDataLoader.java
new file mode 100644
index 00000000000..bd8f730dfc8
--- /dev/null
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/StandardConfigDataLoader.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2012-2020 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
+ *
+ * https://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.context.config;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.springframework.boot.origin.Origin;
+import org.springframework.boot.origin.OriginTrackedResource;
+import org.springframework.core.env.PropertySource;
+import org.springframework.core.io.Resource;
+
+/**
+ * {@link ConfigDataLoader} for {@link Resource} backed locations.
+ *
+ * @author Phillip Webb
+ * @author Madhura Bhave
+ * @since 2.4.0
+ */
+public class StandardConfigDataLoader implements ConfigDataLoader {
+
+ @Override
+ public ConfigData load(ConfigDataLoaderContext context, StandardConfigDataResource resource)
+ throws IOException, ConfigDataNotFoundException {
+ ConfigDataResourceNotFoundException.throwIfDoesNotExist(resource, resource.getResource());
+ StandardConfigDataReference reference = resource.getReference();
+ Resource originTrackedResource = OriginTrackedResource.of(resource.getResource(),
+ Origin.from(reference.getConfigDataLocation()));
+ String name = String.format("Config resource '%s' via location '%s'", reference.getResourceLocation(),
+ reference.getConfigDataLocation());
+ List> propertySources = reference.getPropertySourceLoader().load(name, originTrackedResource);
+ return new ConfigData(propertySources);
+ }
+
+}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/StandardConfigDataLocationResolver.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/StandardConfigDataLocationResolver.java
new file mode 100644
index 00000000000..3b0cbabc013
--- /dev/null
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/StandardConfigDataLocationResolver.java
@@ -0,0 +1,343 @@
+/*
+ * Copyright 2012-2020 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
+ *
+ * https://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.context.config;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.logging.Log;
+
+import org.springframework.boot.context.properties.bind.Binder;
+import org.springframework.boot.env.PropertySourceLoader;
+import org.springframework.core.Ordered;
+import org.springframework.core.env.Environment;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.core.io.support.ResourcePatternResolver;
+import org.springframework.core.io.support.SpringFactoriesLoader;
+import org.springframework.core.log.LogMessage;
+import org.springframework.util.Assert;
+import org.springframework.util.ResourceUtils;
+import org.springframework.util.StringUtils;
+
+/**
+ * {@link ConfigDataLocationResolver} for standard locations.
+ *
+ * @author Madhura Bhave
+ * @author Phillip Webb
+ * @since 2.4.0
+ */
+public class StandardConfigDataLocationResolver
+ implements ConfigDataLocationResolver, Ordered {
+
+ private static final String PREFIX = "resource:";
+
+ static final String CONFIG_NAME_PROPERTY = "spring.config.name";
+
+ private static final String[] DEFAULT_CONFIG_NAMES = { "application" };
+
+ private static final Resource[] EMPTY_RESOURCES = {};
+
+ private static final Comparator FILE_COMPARATOR = Comparator.comparing(File::getAbsolutePath);
+
+ private static final Pattern URL_PREFIX = Pattern.compile("^([a-zA-Z][a-zA-Z0-9*]*?:)(.*$)");
+
+ private static final Pattern EXTENSION_HINT_PATTERN = Pattern.compile("^(.*)\\[(\\.\\w+)\\](?!\\[)$");
+
+ private static final String NO_PROFILE = null;
+
+ private final Log logger;
+
+ private final List propertySourceLoaders;
+
+ private final String[] configNames;
+
+ private final ResourceLoader resourceLoader;
+
+ /**
+ * Create a new {@link StandardConfigDataLocationResolver} instance.
+ * @param logger the logger to use
+ * @param binder a binder backed by the initial {@link Environment}
+ * @param resourceLoader a {@link ResourceLoader} used to load resources
+ */
+ public StandardConfigDataLocationResolver(Log logger, Binder binder, ResourceLoader resourceLoader) {
+ this.logger = logger;
+ this.propertySourceLoaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
+ getClass().getClassLoader());
+ this.configNames = getConfigNames(binder);
+ this.resourceLoader = resourceLoader;
+ }
+
+ private String[] getConfigNames(Binder binder) {
+ String[] configNames = binder.bind(CONFIG_NAME_PROPERTY, String[].class).orElse(DEFAULT_CONFIG_NAMES);
+ for (String configName : configNames) {
+ validateConfigName(configName);
+ }
+ return configNames;
+ }
+
+ private void validateConfigName(String name) {
+ Assert.state(!name.contains("*"), () -> "Config name '" + name + "' cannot contain '*'");
+ }
+
+ @Override
+ public int getOrder() {
+ return Ordered.LOWEST_PRECEDENCE;
+ }
+
+ @Override
+ public boolean isResolvable(ConfigDataLocationResolverContext context, ConfigDataLocation location) {
+ return true;
+ }
+
+ @Override
+ public List resolve(ConfigDataLocationResolverContext context,
+ ConfigDataLocation location) throws ConfigDataNotFoundException {
+ return resolve(getReferences(context, location));
+ }
+
+ private Set getReferences(ConfigDataLocationResolverContext context,
+ ConfigDataLocation configDataLocation) {
+ String resourceLocation = getResourceLocation(context, configDataLocation);
+ try {
+ if (isDirectory(resourceLocation)) {
+ return getReferencesForDirectory(configDataLocation, resourceLocation, NO_PROFILE);
+ }
+ return getReferencesForFile(configDataLocation, resourceLocation, NO_PROFILE);
+ }
+ catch (RuntimeException ex) {
+ throw new IllegalStateException("Unable to load config data from '" + configDataLocation + "'", ex);
+ }
+ }
+
+ @Override
+ public List resolveProfileSpecific(ConfigDataLocationResolverContext context,
+ ConfigDataLocation location, Profiles profiles) {
+ return resolve(getProfileSpecificReferences(context, location, profiles));
+ }
+
+ private Set getProfileSpecificReferences(ConfigDataLocationResolverContext context,
+ ConfigDataLocation configDataLocation, Profiles profiles) {
+ Set references = new LinkedHashSet<>();
+ String resourceLocation = getResourceLocation(context, configDataLocation);
+ for (String profile : profiles) {
+ references.addAll(getReferences(configDataLocation, resourceLocation, profile));
+ }
+ return references;
+ }
+
+ private String getResourceLocation(ConfigDataLocationResolverContext context,
+ ConfigDataLocation configDataLocation) {
+ String resourceLocation = configDataLocation.getNonPrefixedValue(PREFIX);
+ boolean isAbsolute = resourceLocation.startsWith("/") || URL_PREFIX.matcher(resourceLocation).matches();
+ if (isAbsolute) {
+ return resourceLocation;
+ }
+ ConfigDataResource parent = context.getParent();
+ if (parent instanceof StandardConfigDataResource) {
+ String parentResourceLocation = ((StandardConfigDataResource) parent).getReference().getResourceLocation();
+ String parentDirectory = parentResourceLocation.substring(0, parentResourceLocation.lastIndexOf("/") + 1);
+ return parentDirectory + resourceLocation;
+ }
+ return resourceLocation;
+ }
+
+ private Set getReferences(ConfigDataLocation configDataLocation,
+ String resourceLocation, String profile) {
+ if (isDirectory(resourceLocation)) {
+ return getReferencesForDirectory(configDataLocation, resourceLocation, profile);
+ }
+ return getReferencesForFile(configDataLocation, resourceLocation, profile);
+ }
+
+ private Set getReferencesForDirectory(ConfigDataLocation configDataLocation,
+ String directory, String profile) {
+ Set references = new LinkedHashSet<>();
+ for (String name : this.configNames) {
+ for (PropertySourceLoader propertySourceLoader : this.propertySourceLoaders) {
+ for (String extension : propertySourceLoader.getFileExtensions()) {
+ StandardConfigDataReference reference = new StandardConfigDataReference(configDataLocation,
+ directory, directory + name, profile, extension, propertySourceLoader);
+ references.add(reference);
+ }
+ }
+ }
+ return references;
+ }
+
+ private Set getReferencesForFile(ConfigDataLocation configDataLocation, String file,
+ String profile) {
+ Matcher extensionHintMatcher = EXTENSION_HINT_PATTERN.matcher(file);
+ boolean extensionHintLocation = extensionHintMatcher.matches();
+ if (extensionHintLocation) {
+ file = extensionHintMatcher.group(1) + extensionHintMatcher.group(2);
+ }
+ for (PropertySourceLoader propertySourceLoader : this.propertySourceLoaders) {
+ String extension = getLoadableFileExtension(propertySourceLoader, file);
+ if (extension != null) {
+ String root = file.substring(0, file.length() - extension.length() - 1);
+ StandardConfigDataReference reference = new StandardConfigDataReference(configDataLocation, null, root,
+ profile, (!extensionHintLocation) ? extension : null, propertySourceLoader);
+ return Collections.singleton(reference);
+ }
+ }
+ throw new IllegalStateException("File extension is not known to any PropertySourceLoader. "
+ + "If the location is meant to reference a directory, it must end in '/'");
+ }
+
+ private String getLoadableFileExtension(PropertySourceLoader loader, String file) {
+ for (String fileExtension : loader.getFileExtensions()) {
+ if (StringUtils.endsWithIgnoreCase(file, fileExtension)) {
+ return fileExtension;
+ }
+ }
+ return null;
+ }
+
+ private boolean isDirectory(String resourceLocation) {
+ return resourceLocation.endsWith("/");
+ }
+
+ private List resolve(Set references) {
+ List resolved = new ArrayList<>();
+ for (StandardConfigDataReference reference : references) {
+ resolved.addAll(resolve(reference));
+ }
+ if (resolved.isEmpty()) {
+ assertNonOptionalDirectories(references);
+ }
+ return resolved;
+ }
+
+ private void assertNonOptionalDirectories(Set references) {
+ for (StandardConfigDataReference reference : references) {
+ if (reference.isNonOptionalDirectory()) {
+ assertDirectoryExists(reference);
+ }
+ }
+ }
+
+ private void assertDirectoryExists(StandardConfigDataReference reference) {
+ Resource resource = loadResource(reference.getDirectory());
+ StandardConfigDataResource configDataResource = new StandardConfigDataResource(reference, resource);
+ ConfigDataResourceNotFoundException.throwIfDoesNotExist(configDataResource, resource);
+ }
+
+ private List resolve(StandardConfigDataReference reference) {
+ if (!reference.isPatternLocation()) {
+ return resolveNonPattern(reference);
+ }
+ return resolvePattern(reference);
+ }
+
+ private List resolveNonPattern(StandardConfigDataReference reference) {
+ Resource resource = loadResource(reference.getResourceLocation());
+ if (!resource.exists() && reference.isSkippable()) {
+ logSkippingResource(reference);
+ return Collections.emptyList();
+ }
+ return Collections.singletonList(createConfigResourceLocation(reference, resource));
+ }
+
+ private List resolvePattern(StandardConfigDataReference reference) {
+ validatePatternLocation(reference.getResourceLocation());
+ List resolved = new ArrayList<>();
+ for (Resource resource : getResourcesFromResourceLocationPattern(reference.getResourceLocation())) {
+ if (!resource.exists() && reference.isSkippable()) {
+ logSkippingResource(reference);
+ }
+ else {
+ resolved.add(createConfigResourceLocation(reference, resource));
+ }
+ }
+ return resolved;
+ }
+
+ private void logSkippingResource(StandardConfigDataReference reference) {
+ this.logger.trace(LogMessage.format("Skipping missing resource %s", reference));
+ }
+
+ private StandardConfigDataResource createConfigResourceLocation(StandardConfigDataReference reference,
+ Resource resource) {
+ return new StandardConfigDataResource(reference, resource);
+ }
+
+ private void validatePatternLocation(String resourceLocation) {
+ Assert.state(!resourceLocation.startsWith(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX),
+ "Classpath wildcard patterns cannot be used as a search location");
+ Assert.state(StringUtils.countOccurrencesOf(resourceLocation, "*") == 1,
+ () -> "Search location '" + resourceLocation + "' cannot contain multiple wildcards");
+ String directoryPath = resourceLocation.substring(0, resourceLocation.lastIndexOf("/") + 1);
+ Assert.state(directoryPath.endsWith("*/"),
+ () -> "Search location '" + resourceLocation + "' must end with '*/'");
+ }
+
+ private Resource[] getResourcesFromResourceLocationPattern(String resourceLocationPattern) {
+ String directoryPath = resourceLocationPattern.substring(0, resourceLocationPattern.indexOf("*/"));
+ String fileName = resourceLocationPattern.substring(resourceLocationPattern.lastIndexOf("/") + 1);
+ Resource directoryResource = loadResource(directoryPath);
+ if (!directoryResource.exists()) {
+ return new Resource[] { directoryResource };
+ }
+ File directory = getDirectory(resourceLocationPattern, directoryResource);
+ File[] subDirectories = directory.listFiles(File::isDirectory);
+ if (subDirectories == null) {
+ return EMPTY_RESOURCES;
+ }
+ Arrays.sort(subDirectories, FILE_COMPARATOR);
+ List resources = new ArrayList<>();
+ FilenameFilter filter = (dir, name) -> name.equals(fileName);
+ for (File subDirectory : subDirectories) {
+ File[] files = subDirectory.listFiles(filter);
+ if (files != null) {
+ Arrays.stream(files).map(FileSystemResource::new).forEach(resources::add);
+ }
+ }
+ return resources.toArray(EMPTY_RESOURCES);
+ }
+
+ private Resource loadResource(String location) {
+ location = StringUtils.cleanPath(location);
+ if (!ResourceUtils.isUrl(location)) {
+ location = ResourceUtils.FILE_URL_PREFIX + location;
+ }
+ return this.resourceLoader.getResource(location);
+ }
+
+ private File getDirectory(String patternLocation, Resource resource) {
+ try {
+ File directory = resource.getFile();
+ Assert.state(directory.isDirectory(), () -> "'" + directory + "' is not a directory");
+ return directory;
+ }
+ catch (Exception ex) {
+ throw new IllegalStateException(
+ "Unable to load config data resource from pattern '" + patternLocation + "'", ex);
+ }
+ }
+
+}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/StandardConfigDataReference.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/StandardConfigDataReference.java
new file mode 100644
index 00000000000..55878557bf7
--- /dev/null
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/StandardConfigDataReference.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2012-2020 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
+ *
+ * https://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.context.config;
+
+import org.springframework.boot.env.PropertySourceLoader;
+import org.springframework.util.StringUtils;
+
+/**
+ * An reference expanded from the original {@link ConfigDataLocation} that can ultimately
+ * be resolved to one or more {@link StandardConfigDataResource resources}.
+ *
+ * @author Phillip Webb
+ */
+class StandardConfigDataReference {
+
+ private final ConfigDataLocation configDataLocation;
+
+ private final String resourceLocation;
+
+ private final String directory;
+
+ private final String profile;
+
+ private final PropertySourceLoader propertySourceLoader;
+
+ /**
+ * Create a new {@link StandardConfigDataReference} instance.
+ * @param configDataLocation the original location passed to the resolver
+ * @param directory the directory of the resource or {@code null} if the reference is
+ * to a file
+ * @param root the root of the resource location
+ * @param profile the profile being loaded
+ * @param extension the file extension for the resource
+ * @param propertySourceLoader the property source loader that should be used for this
+ * reference
+ */
+ StandardConfigDataReference(ConfigDataLocation configDataLocation, String directory, String root, String profile,
+ String extension, PropertySourceLoader propertySourceLoader) {
+ this.configDataLocation = configDataLocation;
+ String profileSuffix = (StringUtils.hasText(profile)) ? "-" + profile : "";
+ this.resourceLocation = root + profileSuffix + ((extension != null) ? "." + extension : "");
+ this.directory = directory;
+ this.profile = profile;
+ this.propertySourceLoader = propertySourceLoader;
+ }
+
+ ConfigDataLocation getConfigDataLocation() {
+ return this.configDataLocation;
+ }
+
+ String getResourceLocation() {
+ return this.resourceLocation;
+ }
+
+ boolean isNonOptionalDirectory() {
+ return !this.configDataLocation.isOptional() && this.directory != null;
+ }
+
+ String getDirectory() {
+ return this.directory;
+ }
+
+ boolean isSkippable() {
+ return this.configDataLocation.isOptional() || this.directory != null || this.profile != null;
+ }
+
+ boolean isPatternLocation() {
+ return this.resourceLocation.contains("*");
+ }
+
+ PropertySourceLoader getPropertySourceLoader() {
+ return this.propertySourceLoader;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if ((obj == null) || (getClass() != obj.getClass())) {
+ return false;
+ }
+ StandardConfigDataReference other = (StandardConfigDataReference) obj;
+ return this.resourceLocation.equals(other.resourceLocation);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.resourceLocation.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return this.resourceLocation;
+ }
+
+}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ResourceConfigDataLocation.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/StandardConfigDataResource.java
similarity index 56%
rename from spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ResourceConfigDataLocation.java
rename to spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/StandardConfigDataResource.java
index 54668b37a4c..59f097cc618 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ResourceConfigDataLocation.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/StandardConfigDataResource.java
@@ -17,65 +17,45 @@
package org.springframework.boot.context.config;
import java.io.IOException;
-import java.util.List;
-import org.springframework.boot.env.PropertySourceLoader;
-import org.springframework.boot.origin.Origin;
-import org.springframework.boot.origin.OriginTrackedResource;
-import org.springframework.core.env.PropertySource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.FileUrlResource;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
/**
- * {@link ConfigDataLocation} backed by a {@link Resource}.
+ * {@link ConfigDataResource} backed by a {@link Resource}.
*
* @author Madhura Bhave
* @author Phillip Webb
* @since 2.4.0
*/
-public class ResourceConfigDataLocation extends ConfigDataLocation {
+public class StandardConfigDataResource extends ConfigDataResource {
- private final String name;
+ private final StandardConfigDataReference reference;
private final Resource resource;
- private final Origin origin;
-
- private final PropertySourceLoader propertySourceLoader;
-
/**
- * Create a new {@link ResourceConfigDataLocation} instance.
- * @param name the source location
+ * Create a new {@link StandardConfigDataResource} instance.
+ * @param reference the resource reference
* @param resource the underlying resource
- * @param origin the origin of the resource
- * @param propertySourceLoader the loader that should be used to load the resource
*/
- ResourceConfigDataLocation(String name, Resource resource, Origin origin,
- PropertySourceLoader propertySourceLoader) {
- Assert.notNull(name, "Name must not be null");
+ StandardConfigDataResource(StandardConfigDataReference reference, Resource resource) {
+ Assert.notNull(reference, "Reference must not be null");
Assert.notNull(resource, "Resource must not be null");
- Assert.notNull(propertySourceLoader, "PropertySourceLoader must not be null");
- this.name = name;
+ this.reference = reference;
this.resource = resource;
- this.origin = origin;
- this.propertySourceLoader = propertySourceLoader;
+ }
+
+ StandardConfigDataReference getReference() {
+ return this.reference;
}
Resource getResource() {
return this.resource;
}
- String getLocation() {
- return this.name;
- }
-
- List> load() throws IOException {
- Resource resource = OriginTrackedResource.of(this.resource, this.origin);
- return this.propertySourceLoader.load(this.name, resource);
- }
-
@Override
public boolean equals(Object obj) {
if (this == obj) {
@@ -84,7 +64,7 @@ public class ResourceConfigDataLocation extends ConfigDataLocation {
if (obj == null || getClass() != obj.getClass()) {
return false;
}
- ResourceConfigDataLocation other = (ResourceConfigDataLocation) obj;
+ StandardConfigDataResource other = (StandardConfigDataResource) obj;
return this.resource.equals(other.resource);
}
diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/UnsupportedConfigDataLocationException.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/UnsupportedConfigDataLocationException.java
index 6581b428c8a..072107fecda 100644
--- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/UnsupportedConfigDataLocationException.java
+++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/UnsupportedConfigDataLocationException.java
@@ -25,22 +25,22 @@ package org.springframework.boot.context.config;
*/
public class UnsupportedConfigDataLocationException extends ConfigDataException {
- private final String location;
+ private final ConfigDataLocation location;
/**
* Create a new {@link UnsupportedConfigDataLocationException} instance.
* @param location the unsupported location
*/
- UnsupportedConfigDataLocationException(String location) {
+ UnsupportedConfigDataLocationException(ConfigDataLocation location) {
super("Unsupported config data location '" + location + "'", null);
this.location = location;
}
/**
- * Return the unsupported location.
- * @return the unsupported location
+ * Return the unsupported location reference.
+ * @return the unsupported location reference
*/
- public String getLocation() {
+ public ConfigDataLocation getLocation() {
return this.location;
}
diff --git a/spring-boot-project/spring-boot/src/main/resources/META-INF/spring.factories b/spring-boot-project/spring-boot/src/main/resources/META-INF/spring.factories
index d460701d02f..303f9ced73f 100644
--- a/spring-boot-project/spring-boot/src/main/resources/META-INF/spring.factories
+++ b/spring-boot-project/spring-boot/src/main/resources/META-INF/spring.factories
@@ -12,12 +12,12 @@ org.springframework.boot.env.YamlPropertySourceLoader
# ConfigData Location Resolvers
org.springframework.boot.context.config.ConfigDataLocationResolver=\
org.springframework.boot.context.config.ConfigTreeConfigDataLocationResolver,\
-org.springframework.boot.context.config.ResourceConfigDataLocationResolver
+org.springframework.boot.context.config.StandardConfigDataLocationResolver
# ConfigData Loaders
org.springframework.boot.context.config.ConfigDataLoader=\
org.springframework.boot.context.config.ConfigTreeConfigDataLoader,\
-org.springframework.boot.context.config.ResourceConfigDataLoader
+org.springframework.boot.context.config.StandardConfigDataLoader
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorTests.java
index 7c680de4ae6..4c6cdb0df7a 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorTests.java
@@ -44,18 +44,20 @@ import static org.mockito.Mockito.mock;
*/
class ConfigDataEnvironmentContributorTests {
+ private static final ConfigDataLocation TEST_LOCATION = ConfigDataLocation.of("test");
+
private ConfigDataActivationContext activationContext = new ConfigDataActivationContext(CloudPlatform.KUBERNETES,
null);
@Test
void getKindReturnsKind() {
- ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("test");
+ ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(TEST_LOCATION);
assertThat(contributor.getKind()).isEqualTo(Kind.INITIAL_IMPORT);
}
@Test
void isActiveWhenPropertiesIsNullReturnsTrue() {
- ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("test");
+ ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(TEST_LOCATION);
assertThat(contributor.isActive(null)).isTrue();
}
@@ -80,10 +82,10 @@ class ConfigDataEnvironmentContributorTests {
@Test
void getLocationReturnsLocation() {
ConfigData configData = new ConfigData(Collections.singleton(new MockPropertySource()));
- ConfigDataLocation location = mock(ConfigDataLocation.class);
- ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofUnboundImport(location,
+ ConfigDataResource resource = mock(ConfigDataResource.class);
+ ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofUnboundImport(resource,
configData, 0);
- assertThat(contributor.getLocation()).isSameAs(location);
+ assertThat(contributor.getResource()).isSameAs(resource);
}
@Test
@@ -117,7 +119,8 @@ class ConfigDataEnvironmentContributorTests {
propertySource.setProperty("spring.config.import", "spring,boot");
ConfigData configData = new ConfigData(Collections.singleton(propertySource));
ConfigDataEnvironmentContributor contributor = createBoundContributor(null, configData, 0);
- assertThat(contributor.getImports()).containsExactly("spring", "boot");
+ assertThat(contributor.getImports()).containsExactly(ConfigDataLocation.of("spring"),
+ ConfigDataLocation.of("boot"));
}
@Test
@@ -239,7 +242,7 @@ class ConfigDataEnvironmentContributorTests {
ConfigDataEnvironmentContributor two = createBoundContributor("two");
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.of(Arrays.asList(one, two));
assertThat(contributor.getKind()).isEqualTo(Kind.ROOT);
- assertThat(contributor.getLocation()).isNull();
+ assertThat(contributor.getResource()).isNull();
assertThat(contributor.getImports()).isEmpty();
assertThat(contributor.isActive(this.activationContext)).isTrue();
assertThat(contributor.getPropertySource()).isNull();
@@ -249,10 +252,10 @@ class ConfigDataEnvironmentContributorTests {
@Test
void ofInitialImportCreatedInitialImportContributor() {
- ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("test");
+ ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(TEST_LOCATION);
assertThat(contributor.getKind()).isEqualTo(Kind.INITIAL_IMPORT);
- assertThat(contributor.getLocation()).isNull();
- assertThat(contributor.getImports()).containsExactly("test");
+ assertThat(contributor.getResource()).isNull();
+ assertThat(contributor.getImports()).containsExactly(TEST_LOCATION);
assertThat(contributor.isActive(this.activationContext)).isTrue();
assertThat(contributor.getPropertySource()).isNull();
assertThat(contributor.getConfigurationPropertySource()).isNull();
@@ -266,7 +269,7 @@ class ConfigDataEnvironmentContributorTests {
propertySource.setProperty("spring.config.activate.on-cloud-platform", "cloudfoundry");
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofExisting(propertySource);
assertThat(contributor.getKind()).isEqualTo(Kind.EXISTING);
- assertThat(contributor.getLocation()).isNull();
+ assertThat(contributor.getResource()).isNull();
assertThat(contributor.getImports()).isEmpty(); // Properties must not be bound
assertThat(contributor.isActive(this.activationContext)).isTrue();
assertThat(contributor.getPropertySource()).isEqualTo(propertySource);
@@ -276,14 +279,14 @@ class ConfigDataEnvironmentContributorTests {
@Test
void ofUnboundImportCreatesImportedContributor() {
- TestLocation location = new TestLocation("test");
+ TestResource location = new TestResource("test");
MockPropertySource propertySource = new MockPropertySource();
propertySource.setProperty("spring.config.import", "test");
ConfigData configData = new ConfigData(Collections.singleton(propertySource));
ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofUnboundImport(location,
configData, 0);
assertThat(contributor.getKind()).isEqualTo(Kind.UNBOUND_IMPORT);
- assertThat(contributor.getLocation()).isSameAs(location);
+ assertThat(contributor.getResource()).isSameAs(location);
assertThat(contributor.getImports()).isEmpty();
assertThat(contributor.isActive(this.activationContext)).isTrue();
assertThat(contributor.getPropertySource()).isEqualTo(propertySource);
@@ -293,14 +296,14 @@ class ConfigDataEnvironmentContributorTests {
@Test
void bindCreatesImportedContributor() {
- TestLocation location = new TestLocation("test");
+ TestResource location = new TestResource("test");
MockPropertySource propertySource = new MockPropertySource();
propertySource.setProperty("spring.config.import", "test");
ConfigData configData = new ConfigData(Collections.singleton(propertySource));
ConfigDataEnvironmentContributor contributor = createBoundContributor(location, configData, 0);
assertThat(contributor.getKind()).isEqualTo(Kind.BOUND_IMPORT);
- assertThat(contributor.getLocation()).isSameAs(location);
- assertThat(contributor.getImports()).containsExactly("test");
+ assertThat(contributor.getResource()).isSameAs(location);
+ assertThat(contributor.getImports()).containsExactly(TEST_LOCATION);
assertThat(contributor.isActive(this.activationContext)).isTrue();
assertThat(contributor.getPropertySource()).isEqualTo(propertySource);
assertThat(contributor.getConfigurationPropertySource()).isNotNull();
@@ -309,13 +312,13 @@ class ConfigDataEnvironmentContributorTests {
@Test
void bindWhenConfigDataHasIgnoreImportsOptionsCreatesImportedContributorWithoutImports() {
- TestLocation location = new TestLocation("test");
+ TestResource location = new TestResource("test");
MockPropertySource propertySource = new MockPropertySource();
propertySource.setProperty("spring.config.import", "test");
ConfigData configData = new ConfigData(Collections.singleton(propertySource), ConfigData.Option.IGNORE_IMPORTS);
ConfigDataEnvironmentContributor contributor = createBoundContributor(location, configData, 0);
assertThat(contributor.getKind()).isEqualTo(Kind.BOUND_IMPORT);
- assertThat(contributor.getLocation()).isSameAs(location);
+ assertThat(contributor.getResource()).isSameAs(location);
assertThat(contributor.getImports()).isEmpty();
assertThat(contributor.isActive(this.activationContext)).isTrue();
assertThat(contributor.getPropertySource()).isEqualTo(propertySource);
@@ -332,13 +335,13 @@ class ConfigDataEnvironmentContributorTests {
}
private ConfigDataEnvironmentContributor createBoundContributor(String location) {
- return createBoundContributor(new TestLocation(location),
+ return createBoundContributor(new TestResource(location),
new ConfigData(Collections.singleton(new MockPropertySource())), 0);
}
- private ConfigDataEnvironmentContributor createBoundContributor(ConfigDataLocation location, ConfigData configData,
+ private ConfigDataEnvironmentContributor createBoundContributor(ConfigDataResource resource, ConfigData configData,
int propertySourceIndex) {
- ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofUnboundImport(location,
+ ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofUnboundImport(resource,
configData, propertySourceIndex);
Binder binder = new Binder(contributor.getConfigurationPropertySource());
return contributor.withBoundProperties(binder);
@@ -351,14 +354,14 @@ class ConfigDataEnvironmentContributorTests {
}
private String getLocationName(ConfigDataEnvironmentContributor contributor) {
- return contributor.getLocation().toString();
+ return contributor.getResource().toString();
}
- static class TestLocation extends ConfigDataLocation {
+ static class TestResource extends ConfigDataResource {
private final String location;
- TestLocation(String location) {
+ TestResource(String location) {
this.location = location;
}
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorsTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorsTests.java
index 3225b68ecc8..dfbc888a083 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorsTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorsTests.java
@@ -57,6 +57,10 @@ import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
class ConfigDataEnvironmentContributorsTests {
+ private static final ConfigDataLocation LOCATION_1 = ConfigDataLocation.of("location1");
+
+ private static final ConfigDataLocation LOCATION_2 = ConfigDataLocation.of("location2");
+
private DeferredLogFactory logFactory = Supplier::get;
private DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
@@ -80,16 +84,15 @@ class ConfigDataEnvironmentContributorsTests {
this.environment = new MockEnvironment();
this.binder = Binder.get(this.environment);
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
- ConfigDataLocationNotFoundAction.FAIL, this.binder, null);
- ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext,
- ConfigDataLocationNotFoundAction.FAIL);
- this.importer = new ConfigDataImporter(resolvers, loaders);
+ this.binder, null);
+ ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext);
+ this.importer = new ConfigDataImporter(this.logFactory, ConfigDataNotFoundAction.FAIL, resolvers, loaders);
this.activationContext = new ConfigDataActivationContext(CloudPlatform.KUBERNETES, null);
}
@Test
void createCreatesWithInitialContributors() {
- ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("test");
+ ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(contributor));
Iterator iterator = contributors.iterator();
@@ -111,13 +114,13 @@ class ConfigDataEnvironmentContributorsTests {
@Test
void withProcessedImportsResolvesAndLoads() {
this.importer = mock(ConfigDataImporter.class);
- List locations = Arrays.asList("testimport");
+ List locations = Arrays.asList(LOCATION_1);
MockPropertySource propertySource = new MockPropertySource();
- Map imported = new LinkedHashMap<>();
- imported.put(new TestConfigDataLocation("a"), new ConfigData(Arrays.asList(propertySource)));
+ Map imported = new LinkedHashMap<>();
+ imported.put(new TestConfigDataResource("a"), new ConfigData(Arrays.asList(propertySource)));
given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(locations)))
.willReturn(imported);
- ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("testimport");
+ ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(contributor));
ConfigDataEnvironmentContributors withProcessedImports = contributors.withProcessedImports(this.importer,
@@ -132,21 +135,20 @@ class ConfigDataEnvironmentContributorsTests {
@Test
void withProcessedImportsResolvesAndLoadsChainedImports() {
this.importer = mock(ConfigDataImporter.class);
- List initialLocations = Arrays.asList("initialimport");
+ List initialLocations = Arrays.asList(LOCATION_1);
MockPropertySource initialPropertySource = new MockPropertySource();
- initialPropertySource.setProperty("spring.config.import", "secondimport");
- Map initialImported = new LinkedHashMap<>();
- initialImported.put(new TestConfigDataLocation("a"), new ConfigData(Arrays.asList(initialPropertySource)));
+ initialPropertySource.setProperty("spring.config.import", "location2");
+ Map initialImported = new LinkedHashMap<>();
+ initialImported.put(new TestConfigDataResource("a"), new ConfigData(Arrays.asList(initialPropertySource)));
given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(initialLocations)))
.willReturn(initialImported);
- List secondLocations = Arrays.asList("secondimport");
+ List secondLocations = Arrays.asList(LOCATION_2);
MockPropertySource secondPropertySource = new MockPropertySource();
- Map secondImported = new LinkedHashMap<>();
- secondImported.put(new TestConfigDataLocation("b"), new ConfigData(Arrays.asList(secondPropertySource)));
+ Map secondImported = new LinkedHashMap<>();
+ secondImported.put(new TestConfigDataResource("b"), new ConfigData(Arrays.asList(secondPropertySource)));
given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(secondLocations)))
.willReturn(secondImported);
- ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor
- .ofInitialImport("initialimport");
+ ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(contributor));
ConfigDataEnvironmentContributors withProcessedImports = contributors.withProcessedImports(this.importer,
@@ -166,13 +168,13 @@ class ConfigDataEnvironmentContributorsTests {
ConfigDataEnvironmentContributor existingContributor = ConfigDataEnvironmentContributor
.ofExisting(existingPropertySource);
this.importer = mock(ConfigDataImporter.class);
- List locations = Arrays.asList("testimport");
+ List locations = Arrays.asList(LOCATION_1);
MockPropertySource propertySource = new MockPropertySource();
- Map imported = new LinkedHashMap<>();
- imported.put(new TestConfigDataLocation("a'"), new ConfigData(Arrays.asList(propertySource)));
+ Map imported = new LinkedHashMap<>();
+ imported.put(new TestConfigDataResource("a'"), new ConfigData(Arrays.asList(propertySource)));
given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(locations)))
.willReturn(imported);
- ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("testimport");
+ ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(existingContributor, contributor));
contributors.withProcessedImports(this.importer, this.activationContext);
@@ -184,21 +186,20 @@ class ConfigDataEnvironmentContributorsTests {
@Test
void withProcessedImportsProvidesLocationResolverContextWithAccessToParent() {
this.importer = mock(ConfigDataImporter.class);
- List initialLocations = Arrays.asList("initialimport");
+ List initialLocations = Arrays.asList(LOCATION_1);
MockPropertySource initialPropertySource = new MockPropertySource();
- initialPropertySource.setProperty("spring.config.import", "secondimport");
- Map initialImported = new LinkedHashMap<>();
- initialImported.put(new TestConfigDataLocation("a"), new ConfigData(Arrays.asList(initialPropertySource)));
+ initialPropertySource.setProperty("spring.config.import", "location2");
+ Map initialImported = new LinkedHashMap<>();
+ initialImported.put(new TestConfigDataResource("a"), new ConfigData(Arrays.asList(initialPropertySource)));
given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(initialLocations)))
.willReturn(initialImported);
- List secondLocations = Arrays.asList("secondimport");
+ List secondLocations = Arrays.asList(LOCATION_2);
MockPropertySource secondPropertySource = new MockPropertySource();
- Map secondImported = new LinkedHashMap<>();
- secondImported.put(new TestConfigDataLocation("b"), new ConfigData(Arrays.asList(secondPropertySource)));
+ Map secondImported = new LinkedHashMap<>();
+ secondImported.put(new TestConfigDataResource("b"), new ConfigData(Arrays.asList(secondPropertySource)));
given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(secondLocations)))
.willReturn(secondImported);
- ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor
- .ofInitialImport("initialimport");
+ ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(contributor));
contributors.withProcessedImports(this.importer, this.activationContext);
@@ -214,13 +215,13 @@ class ConfigDataEnvironmentContributorsTests {
ConfigDataEnvironmentContributor existingContributor = ConfigDataEnvironmentContributor
.ofExisting(existingPropertySource);
this.importer = mock(ConfigDataImporter.class);
- List locations = Arrays.asList("testimport");
+ List locations = Arrays.asList(LOCATION_1);
MockPropertySource propertySource = new MockPropertySource();
- Map imported = new LinkedHashMap<>();
- imported.put(new TestConfigDataLocation("a'"), new ConfigData(Arrays.asList(propertySource)));
+ Map imported = new LinkedHashMap<>();
+ imported.put(new TestConfigDataResource("a'"), new ConfigData(Arrays.asList(propertySource)));
given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(locations)))
.willReturn(imported);
- ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("testimport");
+ ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(existingContributor, contributor));
contributors.withProcessedImports(this.importer, this.activationContext);
@@ -236,13 +237,13 @@ class ConfigDataEnvironmentContributorsTests {
ConfigDataEnvironmentContributor existingContributor = ConfigDataEnvironmentContributor
.ofExisting(existingPropertySource);
this.importer = mock(ConfigDataImporter.class);
- List locations = Arrays.asList("testimport");
+ List locations = Arrays.asList(LOCATION_1);
MockPropertySource propertySource = new MockPropertySource();
- Map imported = new LinkedHashMap<>();
- imported.put(new TestConfigDataLocation("a'"), new ConfigData(Arrays.asList(propertySource)));
+ Map imported = new LinkedHashMap<>();
+ imported.put(new TestConfigDataResource("a'"), new ConfigData(Arrays.asList(propertySource)));
given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(locations)))
.willReturn(imported);
- ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("testimport");
+ ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport(LOCATION_1);
ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory,
this.bootstrapContext, Arrays.asList(existingContributor, contributor));
contributors.withProcessedImports(this.importer, this.activationContext);
@@ -382,11 +383,11 @@ class ConfigDataEnvironmentContributorsTests {
return contributor.withBoundProperties(binder);
}
- private static class TestConfigDataLocation extends ConfigDataLocation {
+ private static class TestConfigDataResource extends ConfigDataResource {
private final String value;
- TestConfigDataLocation(String value) {
+ TestConfigDataResource(String value) {
this.value = value;
}
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorBootstrapContextIntegrationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorBootstrapContextIntegrationTests.java
index 9ea73591825..1e4eda88fe1 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorBootstrapContextIntegrationTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorBootstrapContextIntegrationTests.java
@@ -51,7 +51,8 @@ class ConfigDataEnvironmentPostProcessorBootstrapContextIntegrationTests {
LoaderHelper bean = context.getBean(TestConfigDataBootstrap.LoaderHelper.class);
assertThat(bean).isNotNull();
assertThat(bean.getBound()).isEqualTo("igotbound");
- assertThat(bean.getLocation().getResolverHelper().getLocation()).isEqualTo("testbootstrap:test");
+ assertThat(bean.getLocation().getResolverHelper().getLocation())
+ .isEqualTo(ConfigDataLocation.of("testbootstrap:test"));
}
}
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorIntegrationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorIntegrationTests.java
index 6fae8b88606..1efc1060954 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorIntegrationTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorIntegrationTests.java
@@ -378,8 +378,8 @@ class ConfigDataEnvironmentPostProcessorIntegrationTests {
List names = StreamSupport.stream(context.getEnvironment().getPropertySources().spliterator(), false)
.map(org.springframework.core.env.PropertySource::getName).collect(Collectors.toList());
assertThat(names).contains(
- "Resource config 'classpath:configdata/profiles/testsetprofiles.yml' imported via location \"classpath:configdata/profiles/\" (document #0)",
- "Resource config 'classpath:configdata/profiles/testsetprofiles.yml' imported via location \"classpath:configdata/profiles/\" (document #1)");
+ "Config resource 'classpath:configdata/profiles/testsetprofiles.yml' via location 'classpath:configdata/profiles/' (document #0)",
+ "Config resource 'classpath:configdata/profiles/testsetprofiles.yml' via location 'classpath:configdata/profiles/' (document #1)");
}
@Test
@@ -406,8 +406,8 @@ class ConfigDataEnvironmentPostProcessorIntegrationTests {
String location = "file:src/test/resources/specificlocation.properties";
ConfigurableApplicationContext context = this.application.run("--spring.config.location=" + location);
assertThat(context.getEnvironment()).has(matchingPropertySource(
- "Resource config 'file:src/test/resources/specificlocation.properties' imported via location \""
- + location + "\""));
+ "Config resource 'file:src/test/resources/specificlocation.properties' via location '" + location
+ + "'"));
}
@Test
@@ -415,8 +415,7 @@ class ConfigDataEnvironmentPostProcessorIntegrationTests {
String location = "src/test/resources/specificlocation.properties";
ConfigurableApplicationContext context = this.application.run("--spring.config.location=" + location);
assertThat(context.getEnvironment()).has(matchingPropertySource(
- "Resource config 'src/test/resources/specificlocation.properties' imported via location \"" + location
- + "\""));
+ "Config resource 'src/test/resources/specificlocation.properties' via location '" + location + "'"));
}
@Test
@@ -519,7 +518,7 @@ class ConfigDataEnvironmentPostProcessorIntegrationTests {
@Test
void runWhenConfigLocationHasNonOptionalMissingDirectoryThrowsException() {
String location = "classpath:application.unknown/";
- assertThatExceptionOfType(ConfigDataLocationNotFoundException.class)
+ assertThatExceptionOfType(ConfigDataResourceNotFoundException.class)
.isThrownBy(() -> this.application.run("--spring.config.location=" + location));
}
@@ -553,13 +552,13 @@ class ConfigDataEnvironmentPostProcessorIntegrationTests {
@Test
void runWhenHasNonOptionalImportThrowsException() {
- assertThatExceptionOfType(ConfigDataLocationNotFoundException.class).isThrownBy(
+ assertThatExceptionOfType(ConfigDataResourceNotFoundException.class).isThrownBy(
() -> this.application.run("--spring.config.location=classpath:missing-appplication.properties"));
}
@Test
void runWhenHasNonOptionalImportAndIgnoreNotFoundPropertyDoesNotThrowException() {
- this.application.run("--spring.config.on-location-not-found=ignore",
+ this.application.run("--spring.config.on-not-found=ignore",
"--spring.config.location=classpath:missing-appplication.properties");
}
@@ -613,6 +612,7 @@ class ConfigDataEnvironmentPostProcessorIntegrationTests {
@Override
public boolean matches(ConfigurableEnvironment value) {
+ value.getPropertySources().forEach((ps) -> System.out.println(ps.getName()));
return value.getPropertySources().contains(sourceName);
}
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java
index be14fb81b38..8327d9d52f7 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java
@@ -214,11 +214,9 @@ class ConfigDataEnvironmentTests {
@Override
protected ConfigDataLocationResolvers createConfigDataLocationResolvers(DeferredLogFactory logFactory,
- ConfigurableBootstrapContext bootstrapContext, ConfigDataLocationNotFoundAction locationNotFoundAction,
- Binder binder, ResourceLoader resourceLoader) {
+ ConfigurableBootstrapContext bootstrapContext, Binder binder, ResourceLoader resourceLoader) {
this.configDataLocationResolversBinder = binder;
- return super.createConfigDataLocationResolvers(logFactory, bootstrapContext, locationNotFoundAction, binder,
- resourceLoader);
+ return super.createConfigDataLocationResolvers(logFactory, bootstrapContext, binder, resourceLoader);
}
Binder getConfigDataLocationResolversBinder() {
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataImporterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataImporterTests.java
index a86fe987b81..76df8e511f0 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataImporterTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataImporterTests.java
@@ -20,6 +20,7 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
+import java.util.function.Supplier;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -28,6 +29,7 @@ import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.boot.context.properties.bind.Binder;
+import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.mock.env.MockPropertySource;
import static org.assertj.core.api.Assertions.assertThat;
@@ -42,6 +44,8 @@ import static org.mockito.BDDMockito.given;
@ExtendWith(MockitoExtension.class)
class ConfigDataImporterTests {
+ private DeferredLogFactory logFactory = Supplier::get;
+
@Mock
private ConfigDataLocationResolvers resolvers;
@@ -70,43 +74,49 @@ class ConfigDataImporterTests {
@Test
void loadImportsResolvesAndLoadsLocations() throws Exception {
- List locations = Arrays.asList("test1", "test2");
- TestLocation resolvedLocation1 = new TestLocation();
- TestLocation resolvedLocation2 = new TestLocation();
- List resolvedLocations = Arrays.asList(resolvedLocation1, resolvedLocation2);
+ ConfigDataLocation location1 = ConfigDataLocation.of("test1");
+ ConfigDataLocation location2 = ConfigDataLocation.of("test2");
+ TestResource resource1 = new TestResource("r1");
+ TestResource resource2 = new TestResource("r2");
ConfigData configData1 = new ConfigData(Collections.singleton(new MockPropertySource()));
ConfigData configData2 = new ConfigData(Collections.singleton(new MockPropertySource()));
- given(this.resolvers.resolveAll(this.locationResolverContext, locations, this.profiles))
- .willReturn(resolvedLocations);
- given(this.loaders.load(this.loaderContext, resolvedLocation1)).willReturn(configData1);
- given(this.loaders.load(this.loaderContext, resolvedLocation2)).willReturn(configData2);
- ConfigDataImporter importer = new ConfigDataImporter(this.resolvers, this.loaders);
- Collection loaded = importer
- .resolveAndLoad(this.activationContext, this.locationResolverContext, this.loaderContext, locations)
- .values();
+ given(this.resolvers.resolve(this.locationResolverContext, location1, this.profiles))
+ .willReturn(Collections.singletonList(new ConfigDataResolutionResult(location1, resource1)));
+ given(this.resolvers.resolve(this.locationResolverContext, location2, this.profiles))
+ .willReturn(Collections.singletonList(new ConfigDataResolutionResult(location2, resource2)));
+ given(this.loaders.load(this.loaderContext, resource1)).willReturn(configData1);
+ given(this.loaders.load(this.loaderContext, resource2)).willReturn(configData2);
+ ConfigDataImporter importer = new ConfigDataImporter(this.logFactory, ConfigDataNotFoundAction.FAIL,
+ this.resolvers, this.loaders);
+ Collection loaded = importer.resolveAndLoad(this.activationContext, this.locationResolverContext,
+ this.loaderContext, Arrays.asList(location1, location2)).values();
assertThat(loaded).containsExactly(configData2, configData1);
}
@Test
void loadImportsWhenAlreadyImportedLocationSkipsLoad() throws Exception {
- List locations1and2 = Arrays.asList("test1", "test2");
- List locations2and3 = Arrays.asList("test2", "test3");
- TestLocation resolvedLocation1 = new TestLocation();
- TestLocation resolvedLocation2 = new TestLocation();
- TestLocation resolvedLocation3 = new TestLocation();
- List resolvedLocations1and2 = Arrays.asList(resolvedLocation1, resolvedLocation2);
- List resolvedLocations2and3 = Arrays.asList(resolvedLocation2, resolvedLocation3);
+ ConfigDataLocation location1 = ConfigDataLocation.of("test1");
+ ConfigDataLocation location2 = ConfigDataLocation.of("test2");
+ ConfigDataLocation location3 = ConfigDataLocation.of("test3");
+ List locations1and2 = Arrays.asList(location1, location2);
+ List locations2and3 = Arrays.asList(location2, location3);
+ TestResource resource1 = new TestResource("r1");
+ TestResource resource2 = new TestResource("r2");
+ TestResource resource3 = new TestResource("r3");
ConfigData configData1 = new ConfigData(Collections.singleton(new MockPropertySource()));
ConfigData configData2 = new ConfigData(Collections.singleton(new MockPropertySource()));
ConfigData configData3 = new ConfigData(Collections.singleton(new MockPropertySource()));
- given(this.resolvers.resolveAll(this.locationResolverContext, locations1and2, this.profiles))
- .willReturn(resolvedLocations1and2);
- given(this.resolvers.resolveAll(this.locationResolverContext, locations2and3, this.profiles))
- .willReturn(resolvedLocations2and3);
- given(this.loaders.load(this.loaderContext, resolvedLocation1)).willReturn(configData1);
- given(this.loaders.load(this.loaderContext, resolvedLocation2)).willReturn(configData2);
- given(this.loaders.load(this.loaderContext, resolvedLocation3)).willReturn(configData3);
- ConfigDataImporter importer = new ConfigDataImporter(this.resolvers, this.loaders);
+ given(this.resolvers.resolve(this.locationResolverContext, location1, this.profiles))
+ .willReturn(Collections.singletonList(new ConfigDataResolutionResult(location1, resource1)));
+ given(this.resolvers.resolve(this.locationResolverContext, location2, this.profiles))
+ .willReturn(Collections.singletonList(new ConfigDataResolutionResult(location2, resource2)));
+ given(this.resolvers.resolve(this.locationResolverContext, location3, this.profiles))
+ .willReturn(Collections.singletonList(new ConfigDataResolutionResult(location3, resource3)));
+ given(this.loaders.load(this.loaderContext, resource1)).willReturn(configData1);
+ given(this.loaders.load(this.loaderContext, resource2)).willReturn(configData2);
+ given(this.loaders.load(this.loaderContext, resource3)).willReturn(configData3);
+ ConfigDataImporter importer = new ConfigDataImporter(this.logFactory, ConfigDataNotFoundAction.FAIL,
+ this.resolvers, this.loaders);
Collection loaded1and2 = importer.resolveAndLoad(this.activationContext,
this.locationResolverContext, this.loaderContext, locations1and2).values();
Collection loaded2and3 = importer.resolveAndLoad(this.activationContext,
@@ -115,7 +125,18 @@ class ConfigDataImporterTests {
assertThat(loaded2and3).containsExactly(configData3);
}
- static class TestLocation extends ConfigDataLocation {
+ static class TestResource extends ConfigDataResource {
+
+ private final String name;
+
+ TestResource(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public String toString() {
+ return this.name;
+ }
}
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoaderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoaderTests.java
index 87aae67504e..2f538516b42 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoaderTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoaderTests.java
@@ -37,19 +37,19 @@ class ConfigDataLoaderTests {
@Test
void isLoadableAlwaysReturnsTrue() {
- assertThat(this.loader.isLoadable(this.context, new TestConfigDataLocation())).isTrue();
+ assertThat(this.loader.isLoadable(this.context, new TestConfigDataResource())).isTrue();
}
- static class TestConfigDataLoader implements ConfigDataLoader {
+ static class TestConfigDataLoader implements ConfigDataLoader {
@Override
- public ConfigData load(ConfigDataLoaderContext context, TestConfigDataLocation location) throws IOException {
+ public ConfigData load(ConfigDataLoaderContext context, TestConfigDataResource resource) throws IOException {
return null;
}
}
- static class TestConfigDataLocation extends ConfigDataLocation {
+ static class TestConfigDataResource extends ConfigDataResource {
}
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoadersTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoadersTests.java
index f69339508e1..ca0aa74b381 100644
--- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoadersTests.java
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoadersTests.java
@@ -53,50 +53,48 @@ class ConfigDataLoadersTests {
@Test
void createWhenLoaderHasLogParameterInjectsLog() {
- new ConfigDataLoaders(this.logFactory, this.bootstrapContext, ConfigDataLocationNotFoundAction.FAIL,
+ new ConfigDataLoaders(this.logFactory, this.bootstrapContext,
Arrays.asList(LoggingConfigDataLoader.class.getName()));
}
@Test
void createWhenLoaderHasBootstrapParametersInjectsBootstrapContext() {
- new ConfigDataLoaders(this.logFactory, this.bootstrapContext, ConfigDataLocationNotFoundAction.FAIL,
+ new ConfigDataLoaders(this.logFactory, this.bootstrapContext,
Arrays.asList(BootstrappingConfigDataLoader.class.getName()));
assertThat(this.bootstrapContext.get(String.class)).isEqualTo("boot");
}
@Test
void loadWhenSingleLoaderSupportsLocationReturnsLoadedConfigData() throws Exception {
- TestConfigDataLocation location = new TestConfigDataLocation("test");
+ TestConfigDataResource location = new TestConfigDataResource("test");
ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext,
- ConfigDataLocationNotFoundAction.FAIL, Arrays.asList(TestConfigDataLoader.class.getName()));
+ Arrays.asList(TestConfigDataLoader.class.getName()));
ConfigData loaded = loaders.load(this.context, location);
assertThat(getLoader(loaded)).isInstanceOf(TestConfigDataLoader.class);
}
@Test
void loadWhenMultipleLoadersSupportLocationThrowsException() throws Exception {
- TestConfigDataLocation location = new TestConfigDataLocation("test");
+ TestConfigDataResource location = new TestConfigDataResource("test");
ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext,
- ConfigDataLocationNotFoundAction.FAIL,
Arrays.asList(LoggingConfigDataLoader.class.getName(), TestConfigDataLoader.class.getName()));
assertThatIllegalStateException().isThrownBy(() -> loaders.load(this.context, location))
- .withMessageContaining("Multiple loaders found for location test");
+ .withMessageContaining("Multiple loaders found for resource 'test'");
}
@Test
void loadWhenNoLoaderSupportsLocationThrowsException() {
- TestConfigDataLocation location = new TestConfigDataLocation("test");
+ TestConfigDataResource location = new TestConfigDataResource("test");
ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext,
- ConfigDataLocationNotFoundAction.FAIL, Arrays.asList(NonLoadableConfigDataLoader.class.getName()));
+ Arrays.asList(NonLoadableConfigDataLoader.class.getName()));
assertThatIllegalStateException().isThrownBy(() -> loaders.load(this.context, location))
- .withMessage("No loader found for location 'test'");
+ .withMessage("No loader found for resource 'test'");
}
@Test
void loadWhenGenericTypeDoesNotMatchSkipsLoader() throws Exception {
- TestConfigDataLocation location = new TestConfigDataLocation("test");
+ TestConfigDataResource location = new TestConfigDataResource("test");
ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, this.bootstrapContext,
- ConfigDataLocationNotFoundAction.FAIL,
Arrays.asList(OtherConfigDataLoader.class.getName(), SpecificConfigDataLoader.class.getName()));
ConfigData loaded = loaders.load(this.context, location);
assertThat(getLoader(loaded)).isInstanceOf(SpecificConfigDataLoader.class);
@@ -106,20 +104,19 @@ class ConfigDataLoadersTests {
return (ConfigDataLoader>) loaded.getPropertySources().get(0).getProperty("loader");
}
- private static ConfigData createConfigData(ConfigDataLoader> loader, ConfigDataLocation location) {
+ private static ConfigData createConfigData(ConfigDataLoader> loader, ConfigDataResource resource) {
MockPropertySource propertySource = new MockPropertySource();
propertySource.setProperty("loader", loader);
- propertySource.setProperty("location", location);
+ propertySource.setProperty("resource", resource);
List> propertySources = Arrays.asList(propertySource);
return new ConfigData(propertySources);
-
}
- static class TestConfigDataLocation extends ConfigDataLocation {
+ static class TestConfigDataResource extends ConfigDataResource {
private final String value;
- TestConfigDataLocation(String value) {
+ TestConfigDataResource(String value) {
this.value = value;
}
@@ -130,24 +127,24 @@ class ConfigDataLoadersTests {
}
- static class OtherConfigDataLocation extends ConfigDataLocation {
+ static class OtherConfigDataResource extends ConfigDataResource {
}
- static class LoggingConfigDataLoader implements ConfigDataLoader {
+ static class LoggingConfigDataLoader implements ConfigDataLoader {
LoggingConfigDataLoader(Log log) {
assertThat(log).isNotNull();
}
@Override
- public ConfigData load(ConfigDataLoaderContext context, ConfigDataLocation location) throws IOException {
+ public ConfigData load(ConfigDataLoaderContext context, ConfigDataResource resource) throws IOException {
throw new AssertionError("Unexpected call");
}
}
- static class BootstrappingConfigDataLoader implements ConfigDataLoader {
+ static class BootstrappingConfigDataLoader implements ConfigDataLoader {
BootstrappingConfigDataLoader(ConfigurableBootstrapContext configurableBootstrapContext,
BootstrapRegistry bootstrapRegistry, BootstrapContext bootstrapContext) {
@@ -159,17 +156,17 @@ class ConfigDataLoadersTests {
}
@Override
- public ConfigData load(ConfigDataLoaderContext context, ConfigDataLocation location) throws IOException {
+ public ConfigData load(ConfigDataLoaderContext context, ConfigDataResource resource) throws IOException {
throw new AssertionError("Unexpected call");
}
}
- static class TestConfigDataLoader implements ConfigDataLoader {
+ static class TestConfigDataLoader implements ConfigDataLoader {
@Override
- public ConfigData load(ConfigDataLoaderContext context, ConfigDataLocation location) throws IOException {
- return createConfigData(this, location);
+ public ConfigData load(ConfigDataLoaderContext context, ConfigDataResource resource) throws IOException {
+ return createConfigData(this, resource);
}
}
@@ -177,25 +174,25 @@ class ConfigDataLoadersTests {
static class NonLoadableConfigDataLoader extends TestConfigDataLoader {
@Override
- public boolean isLoadable(ConfigDataLoaderContext context, ConfigDataLocation location) {
+ public boolean isLoadable(ConfigDataLoaderContext context, ConfigDataResource resource) {
return false;
}
}
- static class SpecificConfigDataLoader implements ConfigDataLoader {
+ static class SpecificConfigDataLoader implements ConfigDataLoader {
@Override
- public ConfigData load(ConfigDataLoaderContext context, TestConfigDataLocation location) throws IOException {
+ public ConfigData load(ConfigDataLoaderContext context, TestConfigDataResource location) throws IOException {
return createConfigData(this, location);
}
}
- static class OtherConfigDataLoader implements ConfigDataLoader {
+ static class OtherConfigDataLoader implements ConfigDataLoader {
@Override
- public ConfigData load(ConfigDataLoaderContext context, OtherConfigDataLocation location) throws IOException {
+ public ConfigData load(ConfigDataLoaderContext context, OtherConfigDataResource location) throws IOException {
return createConfigData(this, location);
}
diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLocationBindHandlerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLocationBindHandlerTests.java
new file mode 100644
index 00000000000..b7b12405d65
--- /dev/null
+++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLocationBindHandlerTests.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2012-2020 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
+ *
+ * https://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.context.config;
+
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+
+import org.springframework.boot.context.properties.bind.Bindable;
+import org.springframework.boot.context.properties.bind.Binder;
+import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link ConfigDataLocationBindHandler}.
+ *
+ * @author Phillip Webb
+ */
+class ConfigDataLocationBindHandlerTests {
+
+ private static final Bindable ARRAY = Bindable.of(ConfigDataLocation[].class);
+
+ private static final Bindable VALUE_OBJECT = Bindable.of(ValueObject.class);
+
+ private final ConfigDataLocationBindHandler handler = new ConfigDataLocationBindHandler();
+
+ @Test
+ void bindToArrayFromCommaStringPropertySetsOrigin() {
+ MapConfigurationPropertySource source = new MapConfigurationPropertySource();
+ source.put("locations", "a,b,c");
+ Binder binder = new Binder(source);
+ ConfigDataLocation[] bound = binder.bind("locations", ARRAY, this.handler).get();
+ String expectedLocation = "\"locations\" from property source \"source\"";
+ assertThat(bound[0]).hasToString("a");
+ assertThat(bound[0].getOrigin()).hasToString(expectedLocation);
+ assertThat(bound[1]).hasToString("b");
+ assertThat(bound[1].getOrigin()).hasToString(expectedLocation);
+ assertThat(bound[2]).hasToString("c");
+ assertThat(bound[2].getOrigin()).hasToString(expectedLocation);
+ }
+
+ @Test
+ void bindToArrayFromIndexedPropertiesSetsOrigin() {
+ MapConfigurationPropertySource source = new MapConfigurationPropertySource();
+ source.put("locations[0]", "a");
+ source.put("locations[1]", "b");
+ source.put("locations[2]", "c");
+ Binder binder = new Binder(source);
+ ConfigDataLocation[] bound = binder.bind("locations", ARRAY, this.handler).get();
+ assertThat(bound[0]).hasToString("a");
+ assertThat(bound[0].getOrigin()).hasToString("\"locations[0]\" from property source \"source\"");
+ assertThat(bound[1]).hasToString("b");
+ assertThat(bound[1].getOrigin()).hasToString("\"locations[1]\" from property source \"source\"");
+ assertThat(bound[2]).hasToString("c");
+ assertThat(bound[2].getOrigin()).hasToString("\"locations[2]\" from property source \"source\"");
+ }
+
+ @Test
+ void bindToValueObjectFromCommaStringPropertySetsOrigin() {
+ MapConfigurationPropertySource source = new MapConfigurationPropertySource();
+ source.put("test.locations", "a,b,c");
+ Binder binder = new Binder(source);
+ ValueObject bound = binder.bind("test", VALUE_OBJECT, this.handler).get();
+ String expectedLocation = "\"test.locations\" from property source \"source\"";
+ assertThat(bound.getLocation(0)).hasToString("a");
+ assertThat(bound.getLocation(0).getOrigin()).hasToString(expectedLocation);
+ assertThat(bound.getLocation(1)).hasToString("b");
+ assertThat(bound.getLocation(1).getOrigin()).hasToString(expectedLocation);
+ assertThat(bound.getLocation(2)).hasToString("c");
+ assertThat(bound.getLocation(2).getOrigin()).hasToString(expectedLocation);
+ }
+
+ @Test
+ void bindToValueObjectFromIndexedPropertiesSetsOrigin() {
+ MapConfigurationPropertySource source = new MapConfigurationPropertySource();
+ source.put("test.locations[0]", "a");
+ source.put("test.locations[1]", "b");
+ source.put("test.locations[2]", "c");
+ Binder binder = new Binder(source);
+ ValueObject bound = binder.bind("test", VALUE_OBJECT, this.handler).get();
+ assertThat(bound.getLocation(0)).hasToString("a");
+ assertThat(bound.getLocation(0).getOrigin())
+ .hasToString("\"test.locations[0]\" from property source \"source\"");
+ assertThat(bound.getLocation(1)).hasToString("b");
+ assertThat(bound.getLocation(1).getOrigin())
+ .hasToString("\"test.locations[1]\" from property source \"source\"");
+ assertThat(bound.getLocation(2)).hasToString("c");
+ assertThat(bound.getLocation(2).getOrigin())
+ .hasToString("\"test.locations[2]\" from property source \"source\"");
+ }
+
+ static class ValueObject {
+
+ private final List