Improve ConfigData processing code
Refactor `ConfigData` processing code to make it less awkward to follow. Prior to this commit the `ConfigDataLocationResolver` would take a String location and return a `ConfigDataLocation` instance. This was a little confusing since sometimes we would refer to `location` as the String value, and sometimes it would be the typed instance. We also had nowhere sensible to put the `optional:` prefix logic and we needed to pass a `boolean` parameter to a number of methods. The recently introduced `Orgin` support also didn't have a good home. To solve this, `ConfigDataLocation` has been renamed to `ConfigDataResource`. This frees up `ConfigDataLocation` to be used as a richer `location` type that holds the String value, the `Orgin` and provides a home for the `optional:` logic. This commit also cleans up a few other areas of the code, including renaming `ResourceConfigData...` to `StandardConfigData...`. It also introduces a new exception hierarchy for `ConfigDataNotFoundExceptions`. Closes gh-23711
This commit is contained in:
parent
f89b99bdbc
commit
1cf9fc107e
|
|
@ -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.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
* <p>
|
||||
* 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<ConfigDataLocation> 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<ConfigDataLocation[]> CONFIG_DATA_LOCATION_ARRAY = Bindable
|
||||
.of(ConfigDataLocation[].class);
|
||||
|
||||
private static final Bindable<List<String>> 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<String> 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<ConfigDataEnvironmentContributor> getInitialImportContributors(Binder binder) {
|
||||
List<ConfigDataEnvironmentContributor> 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<ConfigDataEnvironmentContributor> 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);
|
||||
|
|
|
|||
|
|
@ -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<ConfigDataEnvironmentContributor> {
|
||||
|
||||
private final ConfigDataLocation location;
|
||||
private final ConfigDataResource resource;
|
||||
|
||||
private final PropertySource<?> propertySource;
|
||||
|
||||
|
|
@ -69,7 +68,7 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment
|
|||
/**
|
||||
* Create a new {@link ConfigDataEnvironmentContributor} instance.
|
||||
* @param kind the contributor kind
|
||||
* @param location the location that contributed the data or {@code null}
|
||||
* @param resource the resource that contributed the data or {@code null}
|
||||
* @param propertySource the property source for the data or {@code null}
|
||||
* @param configurationPropertySource the configuration property source for the data
|
||||
* or {@code null}
|
||||
|
|
@ -77,11 +76,11 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment
|
|||
* @param ignoreImports if import properties should be ignored
|
||||
* @param children the children of this contributor at each {@link ImportPhase}
|
||||
*/
|
||||
ConfigDataEnvironmentContributor(Kind kind, ConfigDataLocation location, PropertySource<?> propertySource,
|
||||
ConfigDataEnvironmentContributor(Kind kind, ConfigDataResource resource, PropertySource<?> propertySource,
|
||||
ConfigurationPropertySource configurationPropertySource, ConfigDataProperties properties,
|
||||
boolean ignoreImports, Map<ImportPhase, List<ConfigDataEnvironmentContributor>> 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<ConfigDataEnvironment
|
|||
}
|
||||
|
||||
/**
|
||||
* Return the location that contributed this instance.
|
||||
* @return the location or {@code null}
|
||||
* Return the resource that contributed this instance.
|
||||
* @return the resource or {@code null}
|
||||
*/
|
||||
ConfigDataLocation getLocation() {
|
||||
return this.location;
|
||||
ConfigDataResource getResource() {
|
||||
return this.resource;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -134,14 +133,10 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment
|
|||
* Return any imports requested by this contributor.
|
||||
* @return the imports
|
||||
*/
|
||||
List<String> getImports() {
|
||||
List<ConfigDataLocation> 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<ConfigDataEnvironment
|
|||
return new ContributorIterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an new {@link ConfigDataEnvironmentContributor} with bound
|
||||
* {@link ConfigDataProperties}.
|
||||
* @param binder the binder to use
|
||||
* @return a new contributor instance
|
||||
*/
|
||||
ConfigDataEnvironmentContributor withBoundProperties(Binder binder) {
|
||||
UseLegacyConfigProcessingException.throwIfRequested(binder);
|
||||
ConfigDataProperties properties = ConfigDataProperties.get(binder);
|
||||
if (this.ignoreImports) {
|
||||
properties = properties.withoutImports();
|
||||
}
|
||||
return new ConfigDataEnvironmentContributor(Kind.BOUND_IMPORT, this.location, this.propertySource,
|
||||
return new ConfigDataEnvironmentContributor(Kind.BOUND_IMPORT, this.resource, this.propertySource,
|
||||
this.configurationPropertySource, properties, this.ignoreImports, null);
|
||||
}
|
||||
|
||||
|
|
@ -205,7 +206,7 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment
|
|||
List<ConfigDataEnvironmentContributor> children) {
|
||||
Map<ImportPhase, List<ConfigDataEnvironmentContributor>> 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<ConfigDataEnvironment
|
|||
}
|
||||
updatedChildren.put(importPhase, Collections.unmodifiableList(updatedContributors));
|
||||
});
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -249,11 +250,11 @@ class ConfigDataEnvironmentContributor implements Iterable<ConfigDataEnvironment
|
|||
* Factory method to create a {@link Kind#INITIAL_IMPORT initial import} contributor.
|
||||
* This contributor is used to trigger initial imports of additional contributors. It
|
||||
* does not contribute any properties itself.
|
||||
* @param importLocation the initial import location (with placeholder resolved)
|
||||
* @param initialImport the initial import location (with placeholders resolved)
|
||||
* @return a new {@link ConfigDataEnvironmentContributor} instance
|
||||
*/
|
||||
static ConfigDataEnvironmentContributor ofInitialImport(String importLocation) {
|
||||
List<String> imports = Collections.singletonList(importLocation);
|
||||
static ConfigDataEnvironmentContributor ofInitialImport(ConfigDataLocation initialImport) {
|
||||
List<ConfigDataLocation> 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<ConfigDataEnvironment
|
|||
* Factory method to create an {@link Kind#UNBOUND_IMPORT unbound import} contributor.
|
||||
* This contributor has been actively imported from another contributor and may itself
|
||||
* import further contributors later.
|
||||
* @param location the location of imported config data
|
||||
* @param resource the condig data resource
|
||||
* @param configData the config data
|
||||
* @param propertySourceIndex the index of the property source that should be used
|
||||
* @return a new {@link ConfigDataEnvironmentContributor} instance
|
||||
*/
|
||||
static ConfigDataEnvironmentContributor ofUnboundImport(ConfigDataLocation location, ConfigData configData,
|
||||
static ConfigDataEnvironmentContributor ofUnboundImport(ConfigDataResource resource, ConfigData configData,
|
||||
int propertySourceIndex) {
|
||||
PropertySource<?> 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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ConfigDataEnvironmen
|
|||
ConfigDataLocationResolverContext locationResolverContext = new ContributorConfigDataLocationResolverContext(
|
||||
result, contributor, activationContext);
|
||||
ConfigDataLoaderContext loaderContext = new ContributorDataLoaderContext(this);
|
||||
List<String> imports = contributor.getImports();
|
||||
List<ConfigDataLocation> imports = contributor.getImports();
|
||||
this.logger.trace(LogMessage.format("Processing imports %s", imports));
|
||||
Map<ConfigDataLocation, ConfigData> imported = importer.resolveAndLoad(activationContext,
|
||||
Map<ConfigDataResource, ConfigData> 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<ConfigDataEnvironmen
|
|||
return contributor.isActive(activationContext) && contributor.hasUnprocessedImports(importPhase);
|
||||
}
|
||||
|
||||
private List<ConfigDataEnvironmentContributor> asContributors(Map<ConfigDataLocation, ConfigData> imported) {
|
||||
private List<ConfigDataEnvironmentContributor> asContributors(Map<ConfigDataResource, ConfigData> imported) {
|
||||
List<ConfigDataEnvironmentContributor> 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<ConfigDataEnvironmen
|
|||
}
|
||||
|
||||
@Override
|
||||
public ConfigDataLocation getParent() {
|
||||
return this.contributor.getLocation();
|
||||
public ConfigDataResource getParent() {
|
||||
return this.contributor.getResource();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -268,11 +267,6 @@ class ConfigDataEnvironmentContributors implements Iterable<ConfigDataEnvironmen
|
|||
return this.contributors.getBootstrapContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Origin getLocationOrigin(String location) {
|
||||
return this.contributor.getImportOrigin(location);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class InactiveSourceChecker implements BindHandler {
|
||||
|
|
|
|||
|
|
@ -53,9 +53,9 @@ public class ConfigDataEnvironmentPostProcessor implements EnvironmentPostProces
|
|||
/**
|
||||
* Property used to determine what action to take when a
|
||||
* {@code ConfigDataLocationNotFoundException} is thrown.
|
||||
* @see ConfigDataLocationNotFoundAction
|
||||
* @see ConfigDataNotFoundAction
|
||||
*/
|
||||
public static final String ON_LOCATION_NOT_FOUND_PROPERTY = ConfigDataEnvironment.ON_LOCATION_NOT_FOUND_PROPERTY;
|
||||
public static final String ON_LOCATION_NOT_FOUND_PROPERTY = ConfigDataEnvironment.ON_NOT_FOUND_PROPERTY;
|
||||
|
||||
private final DeferredLogFactory logFactory;
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.boot.context.config;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
|
|
@ -24,9 +25,13 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
|
||||
import org.springframework.boot.logging.DeferredLogFactory;
|
||||
|
||||
/**
|
||||
* Imports {@link ConfigData} by {@link ConfigDataLocationResolver resolving} and
|
||||
* {@link ConfigDataLoader loading} imports. {@link ConfigDataLocation locations} are
|
||||
* {@link ConfigDataLoader loading} locations. {@link ConfigDataResource resources} are
|
||||
* tracked to ensure that they are not imported multiple times.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
|
|
@ -34,20 +39,29 @@ import java.util.Set;
|
|||
*/
|
||||
class ConfigDataImporter {
|
||||
|
||||
private final Log logger;
|
||||
|
||||
private final ConfigDataLocationResolvers resolvers;
|
||||
|
||||
private final ConfigDataLoaders loaders;
|
||||
|
||||
private final Set<ConfigDataLocation> loadedLocations = new HashSet<>();
|
||||
private final ConfigDataNotFoundAction notFoundAction;
|
||||
|
||||
private final Set<ConfigDataResource> 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<ConfigDataLocation, ConfigData> resolveAndLoad(ConfigDataActivationContext activationContext,
|
||||
Map<ConfigDataResource, ConfigData> resolveAndLoad(ConfigDataActivationContext activationContext,
|
||||
ConfigDataLocationResolverContext locationResolverContext, ConfigDataLoaderContext loaderContext,
|
||||
List<String> locations) {
|
||||
List<ConfigDataLocation> locations) {
|
||||
try {
|
||||
Profiles profiles = (activationContext != null) ? activationContext.getProfiles() : null;
|
||||
return load(loaderContext, this.resolvers.resolveAll(locationResolverContext, locations, profiles));
|
||||
List<ConfigDataResolutionResult> 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<ConfigDataLocation, ConfigData> load(ConfigDataLoaderContext loaderContext,
|
||||
List<ConfigDataLocation> locations) throws IOException {
|
||||
Map<ConfigDataLocation, ConfigData> 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<ConfigDataResolutionResult> resolve(ConfigDataLocationResolverContext locationResolverContext,
|
||||
Profiles profiles, List<ConfigDataLocation> locations) {
|
||||
List<ConfigDataResolutionResult> resolved = new ArrayList<>(locations.size());
|
||||
for (ConfigDataLocation location : locations) {
|
||||
resolved.addAll(resolve(locationResolverContext, profiles, location));
|
||||
}
|
||||
return Collections.unmodifiableList(resolved);
|
||||
}
|
||||
|
||||
private List<ConfigDataResolutionResult> 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<ConfigDataResource, ConfigData> load(ConfigDataLoaderContext loaderContext,
|
||||
List<ConfigDataResolutionResult> candidates) throws IOException {
|
||||
Map<ConfigDataResource, ConfigData> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
* <ul>
|
||||
|
|
@ -36,34 +36,34 @@ import org.springframework.boot.ConfigurableBootstrapContext;
|
|||
* ({@link BootstrapContext} or {@link BootstrapRegistry} may also be used).</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* Multiple loaders cannot claim the same location.
|
||||
* Multiple loaders cannot claim the same resource.
|
||||
*
|
||||
* @param <L> the location type
|
||||
* @param <R> the resource type
|
||||
* @author Phillip Webb
|
||||
* @author Madhura Bhave
|
||||
* @since 2.4.0
|
||||
*/
|
||||
public interface ConfigDataLoader<L extends ConfigDataLocation> {
|
||||
public interface ConfigDataLoader<R extends ConfigDataResource> {
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,37 +43,28 @@ class ConfigDataLoaders {
|
|||
|
||||
private final Log logger;
|
||||
|
||||
private final ConfigDataLocationNotFoundAction locationNotFoundAction;
|
||||
|
||||
private final List<ConfigDataLoader<?>> loaders;
|
||||
|
||||
private final List<Class<?>> locationTypes;
|
||||
private final List<Class<?>> 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<String> names) {
|
||||
List<String> names) {
|
||||
this.logger = logFactory.getLog(getClass());
|
||||
this.locationNotFoundAction = locationNotFoundAction;
|
||||
Instantiator<ConfigDataLoader<?>> 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<Class<?>> getLocationTypes(List<ConfigDataLoader<?>> loaders) {
|
||||
List<Class<?>> locationTypes = new ArrayList<>(loaders.size());
|
||||
private List<Class<?>> getResourceTypes(List<ConfigDataLoader<?>> loaders) {
|
||||
List<Class<?>> 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 <L> the config data location type
|
||||
* @param <R> 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
|
||||
*/
|
||||
<L extends ConfigDataLocation> 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 <L extends ConfigDataLocation> ConfigData load(ConfigDataLoaderContext context, boolean optional,
|
||||
L location) throws IOException {
|
||||
ConfigDataLoader<L> 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;
|
||||
}
|
||||
<R extends ConfigDataResource> ConfigData load(ConfigDataLoaderContext context, R resource) throws IOException {
|
||||
ConfigDataLoader<R> loader = getLoader(context, resource);
|
||||
this.logger.trace(LogMessage.of(() -> "Loading " + resource + " using loader " + loader.getClass().getName()));
|
||||
return loader.load(context, resource);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <L extends ConfigDataLocation> ConfigDataLoader<L> getLoader(ConfigDataLoaderContext context, L location) {
|
||||
ConfigDataLoader<L> result = null;
|
||||
private <R extends ConfigDataResource> ConfigDataLoader<R> getLoader(ConfigDataLoaderContext context, R resource) {
|
||||
ConfigDataLoader<R> 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<L> loader = (ConfigDataLoader<L>) candidate;
|
||||
if (loader.isLoadable(context, location)) {
|
||||
if (this.resourceTypes.get(i).isInstance(resource)) {
|
||||
ConfigDataLoader<R> loader = (ConfigDataLoader<R>) 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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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}.
|
||||
* <p>
|
||||
* 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Object> list = (List<Object>) 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
* <ul>
|
||||
* <li>{@link Log} - if the resolver needs deferred logging</li>
|
||||
* <li>{@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 <L> the location type
|
||||
* @param <R> the location type
|
||||
* @author Phillip Webb
|
||||
* @author Madhura Bhave
|
||||
* @since 2.4.0
|
||||
*/
|
||||
public interface ConfigDataLocationResolver<L extends ConfigDataLocation> {
|
||||
public interface ConfigDataLocationResolver<R extends ConfigDataResource> {
|
||||
|
||||
/**
|
||||
* Returns if the specified location address can be resolved by this resolver.
|
||||
|
|
@ -60,35 +61,34 @@ public interface ConfigDataLocationResolver<L extends ConfigDataLocation> {
|
|||
* @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<L> resolve(ConfigDataLocationResolverContext context, String location, boolean optional)
|
||||
throws ConfigDataLocationNotFoundException;
|
||||
List<R> 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<L> resolveProfileSpecific(ConfigDataLocationResolverContext context, String location, boolean optional,
|
||||
default List<R> resolveProfileSpecific(ConfigDataLocationResolverContext context, ConfigDataLocation location,
|
||||
Profiles profiles) throws ConfigDataLocationNotFoundException {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<ConfigDataLocationResolver<?>> 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<String> names) {
|
||||
this.logger = logFactory.getLog(getClass());
|
||||
this.locationNotFoundAction = locationNotFoundAction;
|
||||
Binder binder, ResourceLoader resourceLoader, List<String> names) {
|
||||
Instantiator<ConfigDataLocationResolver<?>> instantiator = new Instantiator<>(ConfigDataLocationResolver.class,
|
||||
(availableParameters) -> {
|
||||
availableParameters.add(Log.class, logFactory::getLog);
|
||||
|
|
@ -94,10 +81,10 @@ class ConfigDataLocationResolvers {
|
|||
|
||||
private List<ConfigDataLocationResolver<?>> reorder(List<ConfigDataLocationResolver<?>> resolvers) {
|
||||
List<ConfigDataLocationResolver<?>> 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<ConfigDataLocation> resolveAll(ConfigDataLocationResolverContext context, List<String> locations,
|
||||
List<ConfigDataResolutionResult> resolve(ConfigDataLocationResolverContext context, ConfigDataLocation location,
|
||||
Profiles profiles) {
|
||||
List<ConfigDataLocation> resolved = new ArrayList<>(locations.size());
|
||||
for (String location : locations) {
|
||||
resolved.addAll(resolveAll(context, location, profiles));
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
private List<ConfigDataLocation> 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<ConfigDataLocation> resolve(ConfigDataLocationResolver<?> resolver,
|
||||
ConfigDataLocationResolverContext context, boolean optional, String location, Profiles profiles) {
|
||||
List<ConfigDataLocation> resolved = resolve(location, optional,
|
||||
() -> resolver.resolve(context, location, optional));
|
||||
private List<ConfigDataResolutionResult> resolve(ConfigDataLocationResolver<?> resolver,
|
||||
ConfigDataLocationResolverContext context, ConfigDataLocation location, Profiles profiles) {
|
||||
List<ConfigDataResolutionResult> resolved = resolve(location, () -> resolver.resolve(context, location));
|
||||
if (profiles == null) {
|
||||
return resolved;
|
||||
}
|
||||
List<ConfigDataLocation> profileSpecific = resolve(location, optional,
|
||||
() -> resolver.resolveProfileSpecific(context, location, optional, profiles));
|
||||
List<ConfigDataResolutionResult> profileSpecific = resolve(location,
|
||||
() -> resolver.resolveProfileSpecific(context, location, profiles));
|
||||
return merge(resolved, profileSpecific);
|
||||
}
|
||||
|
||||
private List<ConfigDataLocation> resolve(String location, boolean optional,
|
||||
Supplier<List<? extends ConfigDataLocation>> resolveAction) {
|
||||
try {
|
||||
List<ConfigDataLocation> 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<ConfigDataResolutionResult> resolve(ConfigDataLocation location,
|
||||
Supplier<List<? extends ConfigDataResource>> resolveAction) {
|
||||
List<ConfigDataResource> resources = nonNullList(resolveAction.get());
|
||||
List<ConfigDataResolutionResult> resolved = new ArrayList<>(resources.size());
|
||||
for (ConfigDataResource resource : resources) {
|
||||
resolved.add(new ConfigDataResolutionResult(location, resource));
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
@ -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<String[]> BINDABLE_STRING_ARRAY = Bindable.of(String[].class);
|
||||
|
||||
private final List<String> imports;
|
||||
private final List<ConfigDataLocation> imports;
|
||||
|
||||
private final Activate activate;
|
||||
|
||||
private final Map<ConfigurationPropertyName, ConfigurationProperty> boundProperties;
|
||||
|
||||
/**
|
||||
* Create a new {@link ConfigDataProperties} instance.
|
||||
* @param imports the imports requested
|
||||
* @param activate the activate properties
|
||||
*/
|
||||
ConfigDataProperties(@Name("import") List<String> imports, Activate activate) {
|
||||
ConfigDataProperties(@Name("import") List<ConfigDataLocation> imports, Activate activate) {
|
||||
this(imports, activate, Collections.emptyList());
|
||||
}
|
||||
|
||||
private ConfigDataProperties(List<String> imports, Activate activate, List<ConfigurationProperty> boundProperties) {
|
||||
private ConfigDataProperties(List<ConfigDataLocation> imports, Activate activate,
|
||||
List<ConfigurationProperty> boundProperties) {
|
||||
this.imports = (imports != null) ? imports : Collections.emptyList();
|
||||
this.activate = activate;
|
||||
this.boundProperties = mapByName(boundProperties);
|
||||
}
|
||||
|
||||
private Map<ConfigurationPropertyName, ConfigurationProperty> mapByName(
|
||||
List<ConfigurationProperty> boundProperties) {
|
||||
Map<ConfigurationPropertyName, ConfigurationProperty> 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<String> getImports() {
|
||||
List<ConfigDataLocation> 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<ConfigurationProperty> 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<ConfigurationProperty> 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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<ResourceConfigDataLocation> {
|
||||
|
||||
@Override
|
||||
public ConfigData load(ConfigDataLoaderContext context, ResourceConfigDataLocation location) throws IOException {
|
||||
ConfigDataLocationNotFoundException.throwIfDoesNotExist(location, location.getResource());
|
||||
return new ConfigData(location.load());
|
||||
}
|
||||
public abstract class ConfigDataResource {
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<String> 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<File[], Stream<File>>) 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 ");
|
||||
|
|
|
|||
|
|
@ -29,12 +29,13 @@ import org.springframework.boot.env.ConfigTreePropertySource;
|
|||
* @author Phillip Webb
|
||||
* @since 2.4.0
|
||||
*/
|
||||
public class ConfigTreeConfigDataLoader implements ConfigDataLoader<ConfigTreeConfigDataLocation> {
|
||||
public class ConfigTreeConfigDataLoader implements ConfigDataLoader<ConfigTreeConfigDataResource> {
|
||||
|
||||
@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));
|
||||
|
|
|
|||
|
|
@ -26,19 +26,19 @@ import java.util.List;
|
|||
* @author Phillip Webb
|
||||
* @since 2.4.0
|
||||
*/
|
||||
public class ConfigTreeConfigDataLocationResolver implements ConfigDataLocationResolver<ConfigTreeConfigDataLocation> {
|
||||
public class ConfigTreeConfigDataLocationResolver implements ConfigDataLocationResolver<ConfigTreeConfigDataResource> {
|
||||
|
||||
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<ConfigTreeConfigDataLocation> resolve(ConfigDataLocationResolverContext context, String location,
|
||||
boolean optional) {
|
||||
ConfigTreeConfigDataLocation resolved = new ConfigTreeConfigDataLocation(location.substring(PREFIX.length()));
|
||||
public List<ConfigTreeConfigDataResource> resolve(ConfigDataLocationResolverContext context,
|
||||
ConfigDataLocation location) {
|
||||
ConfigTreeConfigDataResource resolved = new ConfigTreeConfigDataResource(location.getNonPrefixedValue(PREFIX));
|
||||
return Collections.singletonList(resolved);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
@ -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());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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<ConfigDataLocation> wrapAll(List<ConfigDataLocation> locations) {
|
||||
List<ConfigDataLocation> wrapped = new ArrayList<>(locations.size());
|
||||
locations.forEach((location) -> wrapped.add(new OptionalConfigDataLocation(location)));
|
||||
return wrapped;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static <L extends ConfigDataLocation> L unwrap(ConfigDataLocation wrapped) {
|
||||
return (L) ((OptionalConfigDataLocation) wrapped).getLocation();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<ResourceConfigDataLocation>, 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> 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<PropertySourceLoader> 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<ResourceConfigDataLocation> resolve(ConfigDataLocationResolverContext context, String location,
|
||||
boolean optional) {
|
||||
return resolve(location, getResolvables(context, location, optional));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResourceConfigDataLocation> resolveProfileSpecific(ConfigDataLocationResolverContext context,
|
||||
String location, boolean optional, Profiles profiles) {
|
||||
return resolve(location, getProfileSpecificResolvables(context, location, optional, profiles));
|
||||
}
|
||||
|
||||
private Set<Resolvable> 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<Resolvable> getProfileSpecificResolvables(ConfigDataLocationResolverContext context, String location,
|
||||
boolean optional, Profiles profiles) {
|
||||
Origin origin = context.getLocationOrigin(location);
|
||||
Set<Resolvable> 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<Resolvable> 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<Resolvable> getResolvablesForDirectory(String directoryLocation, boolean optional, String profile,
|
||||
Origin origin) {
|
||||
Set<Resolvable> 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<Resolvable> 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<ResourceConfigDataLocation> resolve(String location, Set<Resolvable> resolvables) {
|
||||
List<ResourceConfigDataLocation> 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<Resolvable> 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<ResourceConfigDataLocation> resolve(String location, Resolvable resolvable) {
|
||||
if (!resolvable.isPatternLocation()) {
|
||||
return resolveNonPattern(location, resolvable);
|
||||
}
|
||||
return resolvePattern(location, resolvable);
|
||||
}
|
||||
|
||||
private List<ResourceConfigDataLocation> 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<ResourceConfigDataLocation> resolvePattern(String location, Resolvable resolvable) {
|
||||
validatePatternLocation(resolvable.getResourceLocation());
|
||||
List<ResourceConfigDataLocation> 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<Resource> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<StandardConfigDataResource> {
|
||||
|
||||
@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<PropertySource<?>> propertySources = reference.getPropertySourceLoader().load(name, originTrackedResource);
|
||||
return new ConfigData(propertySources);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<StandardConfigDataResource>, 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> 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<PropertySourceLoader> 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<StandardConfigDataResource> resolve(ConfigDataLocationResolverContext context,
|
||||
ConfigDataLocation location) throws ConfigDataNotFoundException {
|
||||
return resolve(getReferences(context, location));
|
||||
}
|
||||
|
||||
private Set<StandardConfigDataReference> 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<StandardConfigDataResource> resolveProfileSpecific(ConfigDataLocationResolverContext context,
|
||||
ConfigDataLocation location, Profiles profiles) {
|
||||
return resolve(getProfileSpecificReferences(context, location, profiles));
|
||||
}
|
||||
|
||||
private Set<StandardConfigDataReference> getProfileSpecificReferences(ConfigDataLocationResolverContext context,
|
||||
ConfigDataLocation configDataLocation, Profiles profiles) {
|
||||
Set<StandardConfigDataReference> 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<StandardConfigDataReference> getReferences(ConfigDataLocation configDataLocation,
|
||||
String resourceLocation, String profile) {
|
||||
if (isDirectory(resourceLocation)) {
|
||||
return getReferencesForDirectory(configDataLocation, resourceLocation, profile);
|
||||
}
|
||||
return getReferencesForFile(configDataLocation, resourceLocation, profile);
|
||||
}
|
||||
|
||||
private Set<StandardConfigDataReference> getReferencesForDirectory(ConfigDataLocation configDataLocation,
|
||||
String directory, String profile) {
|
||||
Set<StandardConfigDataReference> 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<StandardConfigDataReference> 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<StandardConfigDataResource> resolve(Set<StandardConfigDataReference> references) {
|
||||
List<StandardConfigDataResource> resolved = new ArrayList<>();
|
||||
for (StandardConfigDataReference reference : references) {
|
||||
resolved.addAll(resolve(reference));
|
||||
}
|
||||
if (resolved.isEmpty()) {
|
||||
assertNonOptionalDirectories(references);
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
|
||||
private void assertNonOptionalDirectories(Set<StandardConfigDataReference> 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<StandardConfigDataResource> resolve(StandardConfigDataReference reference) {
|
||||
if (!reference.isPatternLocation()) {
|
||||
return resolveNonPattern(reference);
|
||||
}
|
||||
return resolvePattern(reference);
|
||||
}
|
||||
|
||||
private List<StandardConfigDataResource> 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<StandardConfigDataResource> resolvePattern(StandardConfigDataReference reference) {
|
||||
validatePatternLocation(reference.getResourceLocation());
|
||||
List<StandardConfigDataResource> 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<Resource> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<PropertySource<?>> 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);
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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=\
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<ConfigDataEnvironmentContributor> iterator = contributors.iterator();
|
||||
|
|
@ -111,13 +114,13 @@ class ConfigDataEnvironmentContributorsTests {
|
|||
@Test
|
||||
void withProcessedImportsResolvesAndLoads() {
|
||||
this.importer = mock(ConfigDataImporter.class);
|
||||
List<String> locations = Arrays.asList("testimport");
|
||||
List<ConfigDataLocation> locations = Arrays.asList(LOCATION_1);
|
||||
MockPropertySource propertySource = new MockPropertySource();
|
||||
Map<ConfigDataLocation, ConfigData> imported = new LinkedHashMap<>();
|
||||
imported.put(new TestConfigDataLocation("a"), new ConfigData(Arrays.asList(propertySource)));
|
||||
Map<ConfigDataResource, ConfigData> 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<String> initialLocations = Arrays.asList("initialimport");
|
||||
List<ConfigDataLocation> initialLocations = Arrays.asList(LOCATION_1);
|
||||
MockPropertySource initialPropertySource = new MockPropertySource();
|
||||
initialPropertySource.setProperty("spring.config.import", "secondimport");
|
||||
Map<ConfigDataLocation, ConfigData> initialImported = new LinkedHashMap<>();
|
||||
initialImported.put(new TestConfigDataLocation("a"), new ConfigData(Arrays.asList(initialPropertySource)));
|
||||
initialPropertySource.setProperty("spring.config.import", "location2");
|
||||
Map<ConfigDataResource, ConfigData> 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<String> secondLocations = Arrays.asList("secondimport");
|
||||
List<ConfigDataLocation> secondLocations = Arrays.asList(LOCATION_2);
|
||||
MockPropertySource secondPropertySource = new MockPropertySource();
|
||||
Map<ConfigDataLocation, ConfigData> secondImported = new LinkedHashMap<>();
|
||||
secondImported.put(new TestConfigDataLocation("b"), new ConfigData(Arrays.asList(secondPropertySource)));
|
||||
Map<ConfigDataResource, ConfigData> 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<String> locations = Arrays.asList("testimport");
|
||||
List<ConfigDataLocation> locations = Arrays.asList(LOCATION_1);
|
||||
MockPropertySource propertySource = new MockPropertySource();
|
||||
Map<ConfigDataLocation, ConfigData> imported = new LinkedHashMap<>();
|
||||
imported.put(new TestConfigDataLocation("a'"), new ConfigData(Arrays.asList(propertySource)));
|
||||
Map<ConfigDataResource, ConfigData> 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<String> initialLocations = Arrays.asList("initialimport");
|
||||
List<ConfigDataLocation> initialLocations = Arrays.asList(LOCATION_1);
|
||||
MockPropertySource initialPropertySource = new MockPropertySource();
|
||||
initialPropertySource.setProperty("spring.config.import", "secondimport");
|
||||
Map<ConfigDataLocation, ConfigData> initialImported = new LinkedHashMap<>();
|
||||
initialImported.put(new TestConfigDataLocation("a"), new ConfigData(Arrays.asList(initialPropertySource)));
|
||||
initialPropertySource.setProperty("spring.config.import", "location2");
|
||||
Map<ConfigDataResource, ConfigData> 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<String> secondLocations = Arrays.asList("secondimport");
|
||||
List<ConfigDataLocation> secondLocations = Arrays.asList(LOCATION_2);
|
||||
MockPropertySource secondPropertySource = new MockPropertySource();
|
||||
Map<ConfigDataLocation, ConfigData> secondImported = new LinkedHashMap<>();
|
||||
secondImported.put(new TestConfigDataLocation("b"), new ConfigData(Arrays.asList(secondPropertySource)));
|
||||
Map<ConfigDataResource, ConfigData> 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<String> locations = Arrays.asList("testimport");
|
||||
List<ConfigDataLocation> locations = Arrays.asList(LOCATION_1);
|
||||
MockPropertySource propertySource = new MockPropertySource();
|
||||
Map<ConfigDataLocation, ConfigData> imported = new LinkedHashMap<>();
|
||||
imported.put(new TestConfigDataLocation("a'"), new ConfigData(Arrays.asList(propertySource)));
|
||||
Map<ConfigDataResource, ConfigData> 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<String> locations = Arrays.asList("testimport");
|
||||
List<ConfigDataLocation> locations = Arrays.asList(LOCATION_1);
|
||||
MockPropertySource propertySource = new MockPropertySource();
|
||||
Map<ConfigDataLocation, ConfigData> imported = new LinkedHashMap<>();
|
||||
imported.put(new TestConfigDataLocation("a'"), new ConfigData(Arrays.asList(propertySource)));
|
||||
Map<ConfigDataResource, ConfigData> 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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -378,8 +378,8 @@ class ConfigDataEnvironmentPostProcessorIntegrationTests {
|
|||
List<String> 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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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<String> locations = Arrays.asList("test1", "test2");
|
||||
TestLocation resolvedLocation1 = new TestLocation();
|
||||
TestLocation resolvedLocation2 = new TestLocation();
|
||||
List<ConfigDataLocation> 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<ConfigData> 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<ConfigData> 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<String> locations1and2 = Arrays.asList("test1", "test2");
|
||||
List<String> locations2and3 = Arrays.asList("test2", "test3");
|
||||
TestLocation resolvedLocation1 = new TestLocation();
|
||||
TestLocation resolvedLocation2 = new TestLocation();
|
||||
TestLocation resolvedLocation3 = new TestLocation();
|
||||
List<ConfigDataLocation> resolvedLocations1and2 = Arrays.asList(resolvedLocation1, resolvedLocation2);
|
||||
List<ConfigDataLocation> resolvedLocations2and3 = Arrays.asList(resolvedLocation2, resolvedLocation3);
|
||||
ConfigDataLocation location1 = ConfigDataLocation.of("test1");
|
||||
ConfigDataLocation location2 = ConfigDataLocation.of("test2");
|
||||
ConfigDataLocation location3 = ConfigDataLocation.of("test3");
|
||||
List<ConfigDataLocation> locations1and2 = Arrays.asList(location1, location2);
|
||||
List<ConfigDataLocation> 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<ConfigData> loaded1and2 = importer.resolveAndLoad(this.activationContext,
|
||||
this.locationResolverContext, this.loaderContext, locations1and2).values();
|
||||
Collection<ConfigData> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<TestConfigDataLocation> {
|
||||
static class TestConfigDataLoader implements ConfigDataLoader<TestConfigDataResource> {
|
||||
|
||||
@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 {
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<PropertySource<?>> 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<ConfigDataLocation> {
|
||||
static class LoggingConfigDataLoader implements ConfigDataLoader<ConfigDataResource> {
|
||||
|
||||
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<ConfigDataLocation> {
|
||||
static class BootstrappingConfigDataLoader implements ConfigDataLoader<ConfigDataResource> {
|
||||
|
||||
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<ConfigDataLocation> {
|
||||
static class TestConfigDataLoader implements ConfigDataLoader<ConfigDataResource> {
|
||||
|
||||
@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<TestConfigDataLocation> {
|
||||
static class SpecificConfigDataLoader implements ConfigDataLoader<TestConfigDataResource> {
|
||||
|
||||
@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<OtherConfigDataLocation> {
|
||||
static class OtherConfigDataLoader implements ConfigDataLoader<OtherConfigDataResource> {
|
||||
|
||||
@Override
|
||||
public ConfigData load(ConfigDataLoaderContext context, OtherConfigDataLocation location) throws IOException {
|
||||
public ConfigData load(ConfigDataLoaderContext context, OtherConfigDataResource location) throws IOException {
|
||||
return createConfigData(this, location);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<ConfigDataLocation[]> ARRAY = Bindable.of(ConfigDataLocation[].class);
|
||||
|
||||
private static final Bindable<ValueObject> 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<ConfigDataLocation> locations;
|
||||
|
||||
ValueObject(List<ConfigDataLocation> locations) {
|
||||
this.locations = locations;
|
||||
}
|
||||
|
||||
ConfigDataLocation getLocation(int index) {
|
||||
return this.locations.get(index);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -16,19 +16,12 @@
|
|||
|
||||
package org.springframework.boot.context.config;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.boot.origin.Origin;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
|
|
@ -38,98 +31,37 @@ import static org.mockito.Mockito.mock;
|
|||
*/
|
||||
class ConfigDataLocationNotFoundExceptionTests {
|
||||
|
||||
private ConfigDataLocation location = mock(ConfigDataLocation.class);
|
||||
private Origin origin = mock(Origin.class);
|
||||
|
||||
private Throwable cause = new RuntimeException();
|
||||
private final ConfigDataLocation location = ConfigDataLocation.of("optional:test").withOrigin(this.origin);
|
||||
|
||||
private String message = "message";
|
||||
|
||||
private File exists;
|
||||
|
||||
private File missing;
|
||||
|
||||
@TempDir
|
||||
File temp;
|
||||
|
||||
@BeforeEach
|
||||
void setup() throws IOException {
|
||||
this.exists = new File(this.temp, "exists");
|
||||
this.missing = new File(this.temp, "missing");
|
||||
try (OutputStream out = new FileOutputStream(this.exists)) {
|
||||
out.write("test".getBytes());
|
||||
}
|
||||
}
|
||||
private final ConfigDataLocationNotFoundException exception = new ConfigDataLocationNotFoundException(
|
||||
this.location);
|
||||
|
||||
@Test
|
||||
void createWithLocationCreatesInstance() {
|
||||
ConfigDataLocationNotFoundException exception = new ConfigDataLocationNotFoundException(this.location);
|
||||
assertThat(exception.getLocation()).isSameAs(this.location);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithLocationAndCauseCreatesInstance() {
|
||||
ConfigDataLocationNotFoundException exception = new ConfigDataLocationNotFoundException(this.location,
|
||||
this.cause);
|
||||
assertThat(exception.getLocation()).isSameAs(this.location);
|
||||
assertThat(exception.getCause()).isSameAs(this.cause);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithMessageAndLocationCreatesInstance() {
|
||||
ConfigDataLocationNotFoundException exception = new ConfigDataLocationNotFoundException(this.message,
|
||||
this.location, this.cause);
|
||||
assertThat(exception.getLocation()).isSameAs(this.location);
|
||||
assertThat(exception.getCause()).isSameAs(this.cause);
|
||||
assertThat(exception.getMessage()).isEqualTo(this.message);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithMessageAndLocationAndCauseCreatesInstance() {
|
||||
ConfigDataLocationNotFoundException exception = new ConfigDataLocationNotFoundException(this.message,
|
||||
this.location);
|
||||
assertThat(exception.getLocation()).isSameAs(this.location);
|
||||
assertThat(exception.getMessage()).isEqualTo(this.message);
|
||||
void createWhenLocationIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new ConfigDataLocationNotFoundException(null))
|
||||
.withMessage("Location must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLocationReturnsLocation() {
|
||||
ConfigDataLocationNotFoundException exception = new ConfigDataLocationNotFoundException(this.location);
|
||||
assertThat(exception.getLocation()).isSameAs(this.location);
|
||||
assertThat(this.exception.getLocation()).isSameAs(this.location);
|
||||
}
|
||||
|
||||
@Test
|
||||
void throwIfDoesNotExistWhenPathExistsDoesNothing() {
|
||||
ConfigDataLocationNotFoundException.throwIfDoesNotExist(this.location, this.exists.toPath());
|
||||
void getOriginReturnsLocationOrigin() {
|
||||
assertThat(this.exception.getOrigin()).isSameAs(this.origin);
|
||||
}
|
||||
|
||||
@Test
|
||||
void throwIfDoesNotExistWhenPathDoesNotExistThrowsException() {
|
||||
assertThatExceptionOfType(ConfigDataLocationNotFoundException.class).isThrownBy(
|
||||
() -> ConfigDataLocationNotFoundException.throwIfDoesNotExist(this.location, this.missing.toPath()));
|
||||
void getReferenceDescriptionReturnsLocationString() {
|
||||
assertThat(this.exception.getReferenceDescription()).isEqualTo("location 'optional:test'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void throwIfDoesNotExistWhenFileExistsDoesNothing() {
|
||||
ConfigDataLocationNotFoundException.throwIfDoesNotExist(this.location, this.exists);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void throwIfDoesNotExistWhenFileDoesNotExistThrowsException() {
|
||||
assertThatExceptionOfType(ConfigDataLocationNotFoundException.class)
|
||||
.isThrownBy(() -> ConfigDataLocationNotFoundException.throwIfDoesNotExist(this.location, this.missing));
|
||||
}
|
||||
|
||||
@Test
|
||||
void throwIfDoesNotExistWhenResourceExistsDoesNothing() {
|
||||
ConfigDataLocationNotFoundException.throwIfDoesNotExist(this.location, new FileSystemResource(this.exists));
|
||||
}
|
||||
|
||||
@Test
|
||||
void throwIfDoesNotExistWhenResourceDoesNotExistThrowsException() {
|
||||
assertThatExceptionOfType(ConfigDataLocationNotFoundException.class)
|
||||
.isThrownBy(() -> ConfigDataLocationNotFoundException.throwIfDoesNotExist(this.location,
|
||||
new FileSystemResource(this.missing)));
|
||||
void getMessageReturnsMessage() {
|
||||
assertThat(this.exception).hasMessage("Config data location 'optional:test' cannot be found");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,19 +37,19 @@ class ConfigDataLocationResolverTests {
|
|||
|
||||
@Test
|
||||
void resolveProfileSpecificReturnsEmptyList() {
|
||||
assertThat(this.resolver.resolveProfileSpecific(this.context, null, true, null)).isEmpty();
|
||||
assertThat(this.resolver.resolveProfileSpecific(this.context, null, null)).isEmpty();
|
||||
}
|
||||
|
||||
static class TestConfigDataLocationResolver implements ConfigDataLocationResolver<ConfigDataLocation> {
|
||||
static class TestConfigDataLocationResolver implements ConfigDataLocationResolver<ConfigDataResource> {
|
||||
|
||||
@Override
|
||||
public boolean isResolvable(ConfigDataLocationResolverContext context, String location) {
|
||||
public boolean isResolvable(ConfigDataLocationResolverContext context, ConfigDataLocation location) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ConfigDataLocation> resolve(ConfigDataLocationResolverContext context, String location,
|
||||
boolean optional) {
|
||||
public List<ConfigDataResource> resolve(ConfigDataLocationResolverContext context,
|
||||
ConfigDataLocation location) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -71,8 +71,7 @@ class ConfigDataLocationResolversTests {
|
|||
@Test
|
||||
void createWhenInjectingBinderCreatesResolver() {
|
||||
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction.FAIL, this.binder, this.resourceLoader,
|
||||
Collections.singletonList(TestBoundResolver.class.getName()));
|
||||
this.binder, this.resourceLoader, Collections.singletonList(TestBoundResolver.class.getName()));
|
||||
assertThat(resolvers.getResolvers()).hasSize(1);
|
||||
assertThat(resolvers.getResolvers().get(0)).isExactlyInstanceOf(TestBoundResolver.class);
|
||||
assertThat(((TestBoundResolver) resolvers.getResolvers().get(0)).getBinder()).isSameAs(this.binder);
|
||||
|
|
@ -81,25 +80,23 @@ class ConfigDataLocationResolversTests {
|
|||
@Test
|
||||
void createWhenNotInjectingBinderCreatesResolver() {
|
||||
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction.FAIL, this.binder, this.resourceLoader,
|
||||
Collections.singletonList(TestResolver.class.getName()));
|
||||
this.binder, this.resourceLoader, Collections.singletonList(TestResolver.class.getName()));
|
||||
assertThat(resolvers.getResolvers()).hasSize(1);
|
||||
assertThat(resolvers.getResolvers().get(0)).isExactlyInstanceOf(TestResolver.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWhenResolverHasBootstrapParametersInjectsBootstrapContext() {
|
||||
new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext, ConfigDataLocationNotFoundAction.FAIL,
|
||||
this.binder, this.resourceLoader, Collections.singletonList(TestBootstrappingResolver.class.getName()));
|
||||
new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext, this.binder, this.resourceLoader,
|
||||
Collections.singletonList(TestBootstrappingResolver.class.getName()));
|
||||
assertThat(this.bootstrapContext.get(String.class)).isEqualTo("boot");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWhenNameIsNotConfigDataLocationResolverThrowsException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction.FAIL, this.binder, this.resourceLoader,
|
||||
Collections.singletonList(InputStream.class.getName())))
|
||||
.isThrownBy(() -> new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext, this.binder,
|
||||
this.resourceLoader, Collections.singletonList(InputStream.class.getName())))
|
||||
.withMessageContaining("Unable to instantiate").havingCause().withMessageContaining("not assignable");
|
||||
}
|
||||
|
||||
|
|
@ -110,73 +107,75 @@ class ConfigDataLocationResolversTests {
|
|||
names.add(LowestTestResolver.class.getName());
|
||||
names.add(HighestTestResolver.class.getName());
|
||||
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction.FAIL, this.binder, this.resourceLoader, names);
|
||||
this.binder, this.resourceLoader, names);
|
||||
assertThat(resolvers.getResolvers().get(0)).isExactlyInstanceOf(HighestTestResolver.class);
|
||||
assertThat(resolvers.getResolvers().get(1)).isExactlyInstanceOf(TestResolver.class);
|
||||
assertThat(resolvers.getResolvers().get(2)).isExactlyInstanceOf(LowestTestResolver.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveAllResolvesUsingFirstSupportedResolver() {
|
||||
void resolveResolvesUsingFirstSupportedResolver() {
|
||||
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction.FAIL, this.binder, this.resourceLoader,
|
||||
this.binder, this.resourceLoader,
|
||||
Arrays.asList(LowestTestResolver.class.getName(), HighestTestResolver.class.getName()));
|
||||
List<ConfigDataLocation> resolved = resolvers.resolveAll(this.context,
|
||||
Collections.singletonList("LowestTestResolver:test"), null);
|
||||
ConfigDataLocation location = ConfigDataLocation.of("LowestTestResolver:test");
|
||||
List<ConfigDataResolutionResult> resolved = resolvers.resolve(this.context, location, null);
|
||||
assertThat(resolved).hasSize(1);
|
||||
TestConfigDataLocation location = (TestConfigDataLocation) resolved.get(0);
|
||||
assertThat(location.getResolver()).isInstanceOf(LowestTestResolver.class);
|
||||
assertThat(location.getLocation()).isEqualTo("LowestTestResolver:test");
|
||||
assertThat(location.isProfileSpecific()).isFalse();
|
||||
TestConfigDataResource resource = (TestConfigDataResource) resolved.get(0).getResource();
|
||||
assertThat(resource.getResolver()).isInstanceOf(LowestTestResolver.class);
|
||||
assertThat(resource.getLocation()).isEqualTo(location);
|
||||
assertThat(resource.isProfileSpecific()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveAllWhenProfileMergesResolvedLocations() {
|
||||
void resolveWhenProfileMergesResolvedLocations() {
|
||||
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction.FAIL, this.binder, this.resourceLoader,
|
||||
this.binder, this.resourceLoader,
|
||||
Arrays.asList(LowestTestResolver.class.getName(), HighestTestResolver.class.getName()));
|
||||
List<ConfigDataLocation> resolved = resolvers.resolveAll(this.context,
|
||||
Collections.singletonList("LowestTestResolver:test"), this.profiles);
|
||||
ConfigDataLocation location = ConfigDataLocation.of("LowestTestResolver:test");
|
||||
List<ConfigDataResolutionResult> resolved = resolvers.resolve(this.context, location, this.profiles);
|
||||
assertThat(resolved).hasSize(2);
|
||||
TestConfigDataLocation location = (TestConfigDataLocation) resolved.get(0);
|
||||
assertThat(location.getResolver()).isInstanceOf(LowestTestResolver.class);
|
||||
assertThat(location.getLocation()).isEqualTo("LowestTestResolver:test");
|
||||
assertThat(location.isProfileSpecific()).isFalse();
|
||||
TestConfigDataLocation profileLocation = (TestConfigDataLocation) resolved.get(1);
|
||||
assertThat(profileLocation.getResolver()).isInstanceOf(LowestTestResolver.class);
|
||||
assertThat(profileLocation.getLocation()).isEqualTo("LowestTestResolver:test");
|
||||
assertThat(profileLocation.isProfileSpecific()).isTrue();
|
||||
TestConfigDataResource resource = (TestConfigDataResource) resolved.get(0).getResource();
|
||||
assertThat(resource.getResolver()).isInstanceOf(LowestTestResolver.class);
|
||||
assertThat(resource.getLocation()).isEqualTo(location);
|
||||
assertThat(resource.isProfileSpecific()).isFalse();
|
||||
TestConfigDataResource profileResource = (TestConfigDataResource) resolved.get(1).getResource();
|
||||
assertThat(profileResource.getResolver()).isInstanceOf(LowestTestResolver.class);
|
||||
assertThat(profileResource.getLocation()).isEqualTo(location);
|
||||
assertThat(profileResource.isProfileSpecific()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenNoResolverThrowsException() {
|
||||
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
|
||||
ConfigDataLocationNotFoundAction.FAIL, this.binder, this.resourceLoader,
|
||||
this.binder, this.resourceLoader,
|
||||
Arrays.asList(LowestTestResolver.class.getName(), HighestTestResolver.class.getName()));
|
||||
ConfigDataLocation location = ConfigDataLocation.of("Missing:test");
|
||||
assertThatExceptionOfType(UnsupportedConfigDataLocationException.class)
|
||||
.isThrownBy(() -> resolvers.resolveAll(this.context, Collections.singletonList("Missing:test"), null))
|
||||
.satisfies((ex) -> assertThat(ex.getLocation()).isEqualTo("Missing:test"));
|
||||
.isThrownBy(() -> resolvers.resolve(this.context, location, null))
|
||||
.satisfies((ex) -> assertThat(ex.getLocation()).isEqualTo(location));
|
||||
}
|
||||
|
||||
static class TestResolver implements ConfigDataLocationResolver<TestConfigDataLocation> {
|
||||
static class TestResolver implements ConfigDataLocationResolver<TestConfigDataResource> {
|
||||
|
||||
@Override
|
||||
public boolean isResolvable(ConfigDataLocationResolverContext context, String location) {
|
||||
public boolean isResolvable(ConfigDataLocationResolverContext context, ConfigDataLocation location) {
|
||||
String name = getClass().getName();
|
||||
name = name.substring(name.lastIndexOf("$") + 1);
|
||||
return location.startsWith(name + ":");
|
||||
return location.hasPrefix(name + ":");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TestConfigDataLocation> resolve(ConfigDataLocationResolverContext context, String location,
|
||||
boolean optional) {
|
||||
return Collections.singletonList(new TestConfigDataLocation(this, location, false));
|
||||
public List<TestConfigDataResource> resolve(ConfigDataLocationResolverContext context,
|
||||
ConfigDataLocation location)
|
||||
throws ConfigDataLocationNotFoundException, ConfigDataResourceNotFoundException {
|
||||
return Collections.singletonList(new TestConfigDataResource(this, location, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TestConfigDataLocation> resolveProfileSpecific(ConfigDataLocationResolverContext context,
|
||||
String location, boolean optional, Profiles profiles) {
|
||||
return Collections.singletonList(new TestConfigDataLocation(this, location, true));
|
||||
public List<TestConfigDataResource> resolveProfileSpecific(ConfigDataLocationResolverContext context,
|
||||
ConfigDataLocation location, Profiles profiles) throws ConfigDataLocationNotFoundException {
|
||||
return Collections.singletonList(new TestConfigDataResource(this, location, true));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -218,15 +217,15 @@ class ConfigDataLocationResolversTests {
|
|||
|
||||
}
|
||||
|
||||
static class TestConfigDataLocation extends ConfigDataLocation {
|
||||
static class TestConfigDataResource extends ConfigDataResource {
|
||||
|
||||
private final TestResolver resolver;
|
||||
|
||||
private final String location;
|
||||
private final ConfigDataLocation location;
|
||||
|
||||
private final boolean profileSpecific;
|
||||
|
||||
TestConfigDataLocation(TestResolver resolver, String location, boolean profileSpecific) {
|
||||
TestConfigDataResource(TestResolver resolver, ConfigDataLocation location, boolean profileSpecific) {
|
||||
this.resolver = resolver;
|
||||
this.location = location;
|
||||
this.profileSpecific = profileSpecific;
|
||||
|
|
@ -236,7 +235,7 @@ class ConfigDataLocationResolversTests {
|
|||
return this.resolver;
|
||||
}
|
||||
|
||||
String getLocation() {
|
||||
ConfigDataLocation getLocation() {
|
||||
return this.location;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* 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.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.origin.Origin;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link ConfigDataLocation}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ConfigDataLocationTests {
|
||||
|
||||
@Test
|
||||
void isOptionalWhenNotPrefixedWithOptionalReturnsFalse() {
|
||||
ConfigDataLocation location = ConfigDataLocation.of("test");
|
||||
assertThat(location.isOptional()).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isOptionalWhenPrefixedWithOptionalReturnsTrue() {
|
||||
ConfigDataLocation location = ConfigDataLocation.of("optional:test");
|
||||
assertThat(location.isOptional()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getValueWhenNotPrefixedWithOptionalReturnsValue() {
|
||||
ConfigDataLocation location = ConfigDataLocation.of("test");
|
||||
assertThat(location.getValue()).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getValueWhenPrefixedWithOptionalReturnsValueWithoutPrefix() {
|
||||
ConfigDataLocation location = ConfigDataLocation.of("optional:test");
|
||||
assertThat(location.getValue()).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasPrefixWhenPrefixedReturnsTrue() {
|
||||
ConfigDataLocation location = ConfigDataLocation.of("optional:test:path");
|
||||
assertThat(location.hasPrefix("test:")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void hasPrefixWhenNotPrefixedReturnsFalse() {
|
||||
ConfigDataLocation location = ConfigDataLocation.of("optional:file:path");
|
||||
assertThat(location.hasPrefix("test:")).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNonPrefixedValueWhenPrefixedReturnsNonPrefixed() {
|
||||
ConfigDataLocation location = ConfigDataLocation.of("optional:test:path");
|
||||
assertThat(location.getNonPrefixedValue("test:")).isEqualTo("path");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getNonPrefixedValueWhenNotPrefixedReturnsOriginalValue() {
|
||||
ConfigDataLocation location = ConfigDataLocation.of("optional:file:path");
|
||||
assertThat(location.getNonPrefixedValue("test:")).isEqualTo("file:path");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getOriginWhenNoOriginReturnsNull() {
|
||||
ConfigDataLocation location = ConfigDataLocation.of("test");
|
||||
assertThat(location.getOrigin()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getOriginWhenWithOriginReturnsOrigin() {
|
||||
Origin origin = mock(Origin.class);
|
||||
ConfigDataLocation location = ConfigDataLocation.of("test").withOrigin(origin);
|
||||
assertThat(location.getOrigin()).isSameAs(origin);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsAndHashCode() {
|
||||
ConfigDataLocation l1 = ConfigDataLocation.of("a");
|
||||
ConfigDataLocation l2 = ConfigDataLocation.of("a");
|
||||
ConfigDataLocation l3 = ConfigDataLocation.of("optional:a");
|
||||
ConfigDataLocation l4 = ConfigDataLocation.of("b");
|
||||
assertThat(l1.hashCode()).isEqualTo(l2.hashCode()).isEqualTo(l3.hashCode());
|
||||
assertThat(l1).isEqualTo(l2).isEqualTo(l3).isNotEqualTo(l4);
|
||||
}
|
||||
|
||||
@Test
|
||||
void toStringReturnsOriginalString() {
|
||||
ConfigDataLocation location = ConfigDataLocation.of("optional:test");
|
||||
assertThat(location).hasToString("optional:test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void withOriginSetsOrigin() {
|
||||
Origin origin = mock(Origin.class);
|
||||
ConfigDataLocation location = ConfigDataLocation.of("test").withOrigin(origin);
|
||||
assertThat(location.getOrigin()).isSameAs(origin);
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofWhenNullValueReturnsNull() {
|
||||
assertThat(ConfigDataLocation.of(null)).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofWhenEmptyValueReturnsNull() {
|
||||
assertThat(ConfigDataLocation.of("")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofWhenEmptyOptionalValueReturnsNull() {
|
||||
assertThat(ConfigDataLocation.of("optional:")).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void ofReturnsLocation() {
|
||||
assertThat(ConfigDataLocation.of("test")).hasToString("test");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -43,13 +43,16 @@ class ConfigDataPropertiesTests {
|
|||
|
||||
private static final Profiles NULL_PROFILES = null;
|
||||
|
||||
private static final List<String> NO_IMPORTS = Collections.emptyList();
|
||||
private static final List<ConfigDataLocation> NO_IMPORTS = Collections.emptyList();
|
||||
|
||||
@Test
|
||||
void getImportsReturnsImports() {
|
||||
List<String> imports = Arrays.asList("one", "two", "three");
|
||||
ConfigDataLocation l1 = ConfigDataLocation.of("one");
|
||||
ConfigDataLocation l2 = ConfigDataLocation.of("two");
|
||||
ConfigDataLocation l3 = ConfigDataLocation.of("three");
|
||||
List<ConfigDataLocation> imports = Arrays.asList(l1, l2, l3);
|
||||
ConfigDataProperties properties = new ConfigDataProperties(imports, null);
|
||||
assertThat(properties.getImports()).containsExactly("one", "two", "three");
|
||||
assertThat(properties.getImports()).containsExactly(l1, l2, l3);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -164,7 +167,8 @@ class ConfigDataPropertiesTests {
|
|||
ConfigDataProperties properties = ConfigDataProperties.get(binder);
|
||||
ConfigDataActivationContext context = new ConfigDataActivationContext(CloudPlatform.KUBERNETES,
|
||||
createTestProfiles());
|
||||
assertThat(properties.getImports()).containsExactly("one", "two", "three");
|
||||
assertThat(properties.getImports()).containsExactly(ConfigDataLocation.of("one"), ConfigDataLocation.of("two"),
|
||||
ConfigDataLocation.of("three"));
|
||||
assertThat(properties.isActive(context)).isTrue();
|
||||
}
|
||||
|
||||
|
|
@ -195,7 +199,7 @@ class ConfigDataPropertiesTests {
|
|||
source.put("spring.config.import", "one,two,three");
|
||||
Binder binder = new Binder(source);
|
||||
ConfigDataProperties properties = ConfigDataProperties.get(binder);
|
||||
assertThat(properties.getImportOrigin("two"))
|
||||
assertThat(properties.getImports().get(1).getOrigin())
|
||||
.hasToString("\"spring.config.import\" from property source \"source\"");
|
||||
}
|
||||
|
||||
|
|
@ -207,7 +211,7 @@ class ConfigDataPropertiesTests {
|
|||
source.put("spring.config.import[2]", "three");
|
||||
Binder binder = new Binder(source);
|
||||
ConfigDataProperties properties = ConfigDataProperties.get(binder);
|
||||
assertThat(properties.getImportOrigin("two"))
|
||||
assertThat(properties.getImports().get(1).getOrigin())
|
||||
.hasToString("\"spring.config.import[1]\" from property source \"source\"");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,167 @@
|
|||
/*
|
||||
* 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.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests for {@link ConfigDataResourceNotFoundException}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class ConfigDataResourceNotFoundExceptionTests {
|
||||
|
||||
private ConfigDataResource resource = new TestConfigDataResource();
|
||||
|
||||
private ConfigDataLocation location = ConfigDataLocation.of("optional:test");
|
||||
|
||||
private Throwable cause = new RuntimeException();
|
||||
|
||||
private File exists;
|
||||
|
||||
private File missing;
|
||||
|
||||
@TempDir
|
||||
File temp;
|
||||
|
||||
@BeforeEach
|
||||
void setup() throws IOException {
|
||||
this.exists = new File(this.temp, "exists");
|
||||
this.missing = new File(this.temp, "missing");
|
||||
try (OutputStream out = new FileOutputStream(this.exists)) {
|
||||
out.write("test".getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWhenResourceIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new ConfigDataResourceNotFoundException(null))
|
||||
.withMessage("Resource must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithResourceCreatesInstance() {
|
||||
ConfigDataResourceNotFoundException exception = new ConfigDataResourceNotFoundException(this.resource);
|
||||
assertThat(exception.getResource()).isSameAs(this.resource);
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWithResourceAndCauseCreatesInstance() {
|
||||
ConfigDataResourceNotFoundException exception = new ConfigDataResourceNotFoundException(this.resource,
|
||||
this.cause);
|
||||
assertThat(exception.getResource()).isSameAs(this.resource);
|
||||
assertThat(exception.getCause()).isSameAs(this.cause);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getResourceReturnsResource() {
|
||||
ConfigDataResourceNotFoundException exception = new ConfigDataResourceNotFoundException(this.resource);
|
||||
assertThat(exception.getResource()).isSameAs(this.resource);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLocationWhenHasNoLocationReturnsNull() {
|
||||
ConfigDataResourceNotFoundException exception = new ConfigDataResourceNotFoundException(this.resource);
|
||||
assertThat(exception.getLocation()).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLocationWhenHasLocationReturnsLocation() {
|
||||
ConfigDataResourceNotFoundException exception = new ConfigDataResourceNotFoundException(this.resource)
|
||||
.withLocation(this.location);
|
||||
assertThat(exception.getLocation()).isSameAs(this.location);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getReferenceDescriptionWhenHasNoLocationReturnsDescription() {
|
||||
ConfigDataResourceNotFoundException exception = new ConfigDataResourceNotFoundException(this.resource);
|
||||
assertThat(exception.getReferenceDescription()).isEqualTo("resource 'mytestresource'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getReferenceDescriptionWhenHasLocationReturnsDescription() {
|
||||
ConfigDataResourceNotFoundException exception = new ConfigDataResourceNotFoundException(this.resource)
|
||||
.withLocation(this.location);
|
||||
assertThat(exception.getReferenceDescription())
|
||||
.isEqualTo("resource 'mytestresource' via location 'optional:test'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void withLocationReturnsNewInstanceWithLocation() {
|
||||
ConfigDataResourceNotFoundException exception = new ConfigDataResourceNotFoundException(this.resource)
|
||||
.withLocation(this.location);
|
||||
assertThat(exception.getLocation()).isSameAs(this.location);
|
||||
}
|
||||
|
||||
@Test
|
||||
void throwIfDoesNotExistWhenPathExistsDoesNothing() {
|
||||
ConfigDataResourceNotFoundException.throwIfDoesNotExist(this.resource, this.exists.toPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
void throwIfDoesNotExistWhenPathDoesNotExistThrowsException() {
|
||||
assertThatExceptionOfType(ConfigDataResourceNotFoundException.class).isThrownBy(
|
||||
() -> ConfigDataResourceNotFoundException.throwIfDoesNotExist(this.resource, this.missing.toPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void throwIfDoesNotExistWhenFileExistsDoesNothing() {
|
||||
ConfigDataResourceNotFoundException.throwIfDoesNotExist(this.resource, this.exists);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void throwIfDoesNotExistWhenFileDoesNotExistThrowsException() {
|
||||
assertThatExceptionOfType(ConfigDataResourceNotFoundException.class)
|
||||
.isThrownBy(() -> ConfigDataResourceNotFoundException.throwIfDoesNotExist(this.resource, this.missing));
|
||||
}
|
||||
|
||||
@Test
|
||||
void throwIfDoesNotExistWhenResourceExistsDoesNothing() {
|
||||
ConfigDataResourceNotFoundException.throwIfDoesNotExist(this.resource, new FileSystemResource(this.exists));
|
||||
}
|
||||
|
||||
@Test
|
||||
void throwIfDoesNotExistWhenResourceDoesNotExistThrowsException() {
|
||||
assertThatExceptionOfType(ConfigDataResourceNotFoundException.class)
|
||||
.isThrownBy(() -> ConfigDataResourceNotFoundException.throwIfDoesNotExist(this.resource,
|
||||
new FileSystemResource(this.missing)));
|
||||
}
|
||||
|
||||
static class TestConfigDataResource extends ConfigDataResource {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "mytestresource";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@ public class ConfigTreeConfigDataLoaderTests {
|
|||
File file = this.directory.resolve("hello").toFile();
|
||||
file.getParentFile().mkdirs();
|
||||
FileCopyUtils.copy("world".getBytes(StandardCharsets.UTF_8), file);
|
||||
ConfigTreeConfigDataLocation location = new ConfigTreeConfigDataLocation(this.directory.toString());
|
||||
ConfigTreeConfigDataResource location = new ConfigTreeConfigDataResource(this.directory.toString());
|
||||
ConfigData configData = this.loader.load(this.loaderContext, location);
|
||||
assertThat(configData.getPropertySources().size()).isEqualTo(1);
|
||||
PropertySource<?> source = configData.getPropertySources().get(0);
|
||||
|
|
@ -62,8 +62,8 @@ public class ConfigTreeConfigDataLoaderTests {
|
|||
@Test
|
||||
void loadWhenPathDoesNotExistThrowsException() {
|
||||
File missing = this.directory.resolve("missing").toFile();
|
||||
ConfigTreeConfigDataLocation location = new ConfigTreeConfigDataLocation(missing.toString());
|
||||
assertThatExceptionOfType(ConfigDataLocationNotFoundException.class)
|
||||
ConfigTreeConfigDataResource location = new ConfigTreeConfigDataResource(missing.toString());
|
||||
assertThatExceptionOfType(ConfigDataResourceNotFoundException.class)
|
||||
.isThrownBy(() -> this.loader.load(this.loaderContext, location));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,19 +38,19 @@ class ConfigTreeConfigDataLocationResolverTests {
|
|||
|
||||
@Test
|
||||
void isResolvableWhenPrefixMatchesReturnsTrue() {
|
||||
assertThat(this.resolver.isResolvable(this.context, "configtree:/etc/config")).isTrue();
|
||||
assertThat(this.resolver.isResolvable(this.context, ConfigDataLocation.of("configtree:/etc/config"))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isResolvableWhenPrefixDoesNotMatchReturnsFalse() {
|
||||
assertThat(this.resolver.isResolvable(this.context, "http://etc/config")).isFalse();
|
||||
assertThat(this.resolver.isResolvable(this.context, "/etc/config")).isFalse();
|
||||
assertThat(this.resolver.isResolvable(this.context, ConfigDataLocation.of("http://etc/config"))).isFalse();
|
||||
assertThat(this.resolver.isResolvable(this.context, ConfigDataLocation.of("/etc/config"))).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveReturnsConfigVolumeMountLocation() {
|
||||
List<ConfigTreeConfigDataLocation> locations = this.resolver.resolve(this.context, "configtree:/etc/config",
|
||||
false);
|
||||
List<ConfigTreeConfigDataResource> locations = this.resolver.resolve(this.context,
|
||||
ConfigDataLocation.of("configtree:/etc/config"));
|
||||
assertThat(locations.size()).isEqualTo(1);
|
||||
assertThat(locations).extracting(Object::toString)
|
||||
.containsExactly("config tree [" + new File("/etc/config").getAbsolutePath() + "]");
|
||||
|
|
|
|||
|
|
@ -24,36 +24,36 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Tests for {@link ConfigTreeConfigDataLocation}.
|
||||
* Tests for {@link ConfigTreeConfigDataResource}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class ConfigTreeConfigDataLocationTests {
|
||||
public class ConfigTreeConfigDataResourceTests {
|
||||
|
||||
@Test
|
||||
void constructorWhenPathIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new ConfigTreeConfigDataLocation(null))
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new ConfigTreeConfigDataResource(null))
|
||||
.withMessage("Path must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWhenPathIsTheSameReturnsTrue() {
|
||||
ConfigTreeConfigDataLocation location = new ConfigTreeConfigDataLocation("/etc/config");
|
||||
ConfigTreeConfigDataLocation other = new ConfigTreeConfigDataLocation("/etc/config");
|
||||
ConfigTreeConfigDataResource location = new ConfigTreeConfigDataResource("/etc/config");
|
||||
ConfigTreeConfigDataResource other = new ConfigTreeConfigDataResource("/etc/config");
|
||||
assertThat(location).isEqualTo(other);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWhenPathIsDifferentReturnsFalse() {
|
||||
ConfigTreeConfigDataLocation location = new ConfigTreeConfigDataLocation("/etc/config");
|
||||
ConfigTreeConfigDataLocation other = new ConfigTreeConfigDataLocation("other-location");
|
||||
ConfigTreeConfigDataResource location = new ConfigTreeConfigDataResource("/etc/config");
|
||||
ConfigTreeConfigDataResource other = new ConfigTreeConfigDataResource("other-location");
|
||||
assertThat(location).isNotEqualTo(other);
|
||||
}
|
||||
|
||||
@Test
|
||||
void toStringReturnsDescriptiveString() {
|
||||
ConfigTreeConfigDataLocation location = new ConfigTreeConfigDataLocation("/etc/config");
|
||||
ConfigTreeConfigDataResource location = new ConfigTreeConfigDataResource("/etc/config");
|
||||
assertThat(location.toString()).isEqualTo("config tree [" + new File("/etc/config").getAbsolutePath() + "]");
|
||||
}
|
||||
|
||||
|
|
@ -40,7 +40,7 @@ class InactiveConfigDataAccessExceptionTests {
|
|||
|
||||
private MockPropertySource propertySource = new MockPropertySource();
|
||||
|
||||
private ConfigDataLocation location = new TestConfigDataLocation();
|
||||
private ConfigDataResource resource = new TestConfigDataResource();
|
||||
|
||||
private String propertyName = "spring";
|
||||
|
||||
|
|
@ -49,7 +49,7 @@ class InactiveConfigDataAccessExceptionTests {
|
|||
@Test
|
||||
void createHasCorrectMessage() {
|
||||
InactiveConfigDataAccessException exception = new InactiveConfigDataAccessException(this.propertySource,
|
||||
this.location, this.propertyName, this.origin);
|
||||
this.resource, this.propertyName, this.origin);
|
||||
assertThat(exception).hasMessage("Inactive property source 'mockProperties' imported from location 'test' "
|
||||
+ "cannot contain property 'spring' [origin: \"spring\" from property source \"mockProperties\"]");
|
||||
}
|
||||
|
|
@ -65,7 +65,7 @@ class InactiveConfigDataAccessExceptionTests {
|
|||
@Test
|
||||
void createWhenNoOriginHasCorrectMessage() {
|
||||
InactiveConfigDataAccessException exception = new InactiveConfigDataAccessException(this.propertySource,
|
||||
this.location, this.propertyName, null);
|
||||
this.resource, this.propertyName, null);
|
||||
assertThat(exception).hasMessage("Inactive property source 'mockProperties' imported from location 'test' "
|
||||
+ "cannot contain property 'spring'");
|
||||
}
|
||||
|
|
@ -73,28 +73,28 @@ class InactiveConfigDataAccessExceptionTests {
|
|||
@Test
|
||||
void getPropertySourceReturnsPropertySource() {
|
||||
InactiveConfigDataAccessException exception = new InactiveConfigDataAccessException(this.propertySource,
|
||||
this.location, this.propertyName, this.origin);
|
||||
this.resource, this.propertyName, this.origin);
|
||||
assertThat(exception.getPropertySource()).isSameAs(this.propertySource);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLocationReturnsLocation() {
|
||||
InactiveConfigDataAccessException exception = new InactiveConfigDataAccessException(this.propertySource,
|
||||
this.location, this.propertyName, this.origin);
|
||||
assertThat(exception.getLocation()).isSameAs(this.location);
|
||||
this.resource, this.propertyName, this.origin);
|
||||
assertThat(exception.getLocation()).isSameAs(this.resource);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPropertyNameReturnsPropertyName() {
|
||||
InactiveConfigDataAccessException exception = new InactiveConfigDataAccessException(this.propertySource,
|
||||
this.location, this.propertyName, this.origin);
|
||||
this.resource, this.propertyName, this.origin);
|
||||
assertThat(exception.getPropertyName()).isSameAs(this.propertyName);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getOriginReturnsOrigin() {
|
||||
InactiveConfigDataAccessException exception = new InactiveConfigDataAccessException(this.propertySource,
|
||||
this.location, this.propertyName, this.origin);
|
||||
this.resource, this.propertyName, this.origin);
|
||||
assertThat(exception.getOrigin()).isSameAs(this.origin);
|
||||
}
|
||||
|
||||
|
|
@ -121,7 +121,7 @@ class InactiveConfigDataAccessExceptionTests {
|
|||
ConfigurationPropertySource configurationPropertySource = ConfigurationPropertySource.from(this.propertySource);
|
||||
given(contributor.getConfigurationPropertySource()).willReturn(configurationPropertySource);
|
||||
given(contributor.getPropertySource()).willReturn((PropertySource) this.propertySource);
|
||||
given(contributor.getLocation()).willReturn(this.location);
|
||||
given(contributor.getResource()).willReturn(this.resource);
|
||||
assertThatExceptionOfType(InactiveConfigDataAccessException.class)
|
||||
.isThrownBy(() -> InactiveConfigDataAccessException.throwIfPropertyFound(contributor,
|
||||
ConfigurationPropertyName.of("spring")))
|
||||
|
|
@ -129,7 +129,7 @@ class InactiveConfigDataAccessExceptionTests {
|
|||
+ "cannot contain property 'spring' [origin: \"spring\" from property source \"mockProperties\"]");
|
||||
}
|
||||
|
||||
private static class TestConfigDataLocation extends ConfigDataLocation {
|
||||
private static class TestConfigDataResource extends ConfigDataResource {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ import static org.mockito.Mockito.verify;
|
|||
*/
|
||||
class InvalidConfigDataPropertyExceptionTests {
|
||||
|
||||
private ConfigDataLocation location = new TestConfigDataLocation();
|
||||
private ConfigDataResource resource = new TestConfigDataResource();
|
||||
|
||||
private ConfigurationPropertyName replacement = ConfigurationPropertyName.of("replacement");
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ class InvalidConfigDataPropertyExceptionTests {
|
|||
|
||||
@Test
|
||||
void createHasCorrectMessage() {
|
||||
assertThat(new InvalidConfigDataPropertyException(this.property, this.replacement, this.location)).hasMessage(
|
||||
assertThat(new InvalidConfigDataPropertyException(this.property, this.replacement, this.resource)).hasMessage(
|
||||
"Property 'invalid' imported from location 'test' is invalid and should be replaced with 'replacement' [origin: origin]");
|
||||
}
|
||||
|
||||
|
|
@ -62,35 +62,35 @@ class InvalidConfigDataPropertyExceptionTests {
|
|||
|
||||
@Test
|
||||
void createWhenNoReplacementHasCorrectMessage() {
|
||||
assertThat(new InvalidConfigDataPropertyException(this.property, null, this.location))
|
||||
assertThat(new InvalidConfigDataPropertyException(this.property, null, this.resource))
|
||||
.hasMessage("Property 'invalid' imported from location 'test' is invalid [origin: origin]");
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWhenNoOriginHasCorrectMessage() {
|
||||
ConfigurationProperty property = new ConfigurationProperty(this.invalid, "bad", null);
|
||||
assertThat(new InvalidConfigDataPropertyException(property, this.replacement, this.location)).hasMessage(
|
||||
assertThat(new InvalidConfigDataPropertyException(property, this.replacement, this.resource)).hasMessage(
|
||||
"Property 'invalid' imported from location 'test' is invalid and should be replaced with 'replacement'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPropertyReturnsProperty() {
|
||||
InvalidConfigDataPropertyException exception = new InvalidConfigDataPropertyException(this.property,
|
||||
this.replacement, this.location);
|
||||
this.replacement, this.resource);
|
||||
assertThat(exception.getProperty()).isEqualTo(this.property);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLocationReturnsLocation() {
|
||||
InvalidConfigDataPropertyException exception = new InvalidConfigDataPropertyException(this.property,
|
||||
this.replacement, this.location);
|
||||
assertThat(exception.getLocation()).isEqualTo(this.location);
|
||||
this.replacement, this.resource);
|
||||
assertThat(exception.getLocation()).isEqualTo(this.resource);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getReplacementReturnsReplacement() {
|
||||
InvalidConfigDataPropertyException exception = new InvalidConfigDataPropertyException(this.property,
|
||||
this.replacement, this.location);
|
||||
this.replacement, this.resource);
|
||||
assertThat(exception.getReplacement()).isEqualTo(this.replacement);
|
||||
}
|
||||
|
||||
|
|
@ -123,7 +123,7 @@ class InvalidConfigDataPropertyExceptionTests {
|
|||
+ "'spring.config.activate.on-profile' [origin: \"spring.profiles\" from property source \"mockProperties\"]");
|
||||
}
|
||||
|
||||
private static class TestConfigDataLocation extends ConfigDataLocation {
|
||||
private static class TestConfigDataResource extends ConfigDataResource {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
|
|
|
|||
|
|
@ -1,83 +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.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.env.PropertySourceLoader;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatObject;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link OptionalConfigDataLocation}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class OptionalConfigDataLocationTests {
|
||||
|
||||
private ConfigDataLocation location;
|
||||
|
||||
@BeforeEach
|
||||
void setup() {
|
||||
this.location = new ResourceConfigDataLocation("classpath:application.properties",
|
||||
new ClassPathResource("application.properties"), null, mock(PropertySourceLoader.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void createWrapsLocation() {
|
||||
OptionalConfigDataLocation optionalLocation = new OptionalConfigDataLocation(this.location);
|
||||
assertThat(optionalLocation.getLocation()).isSameAs(this.location);
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsAndHashCode() {
|
||||
OptionalConfigDataLocation optionalLocation1 = new OptionalConfigDataLocation(this.location);
|
||||
OptionalConfigDataLocation optionalLocation2 = new OptionalConfigDataLocation(this.location);
|
||||
assertThat(optionalLocation1.hashCode()).isEqualTo(optionalLocation2.hashCode());
|
||||
assertThat(optionalLocation1).isEqualTo(optionalLocation1).isEqualTo(optionalLocation2)
|
||||
.isNotEqualTo(this.location);
|
||||
}
|
||||
|
||||
@Test
|
||||
void toStringReturnsLocationString() {
|
||||
OptionalConfigDataLocation optionalLocation = new OptionalConfigDataLocation(this.location);
|
||||
assertThat(optionalLocation).hasToString(this.location.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void wrapAllWrapsList() {
|
||||
List<ConfigDataLocation> locations = Collections.singletonList(this.location);
|
||||
List<ConfigDataLocation> optionalLocations = OptionalConfigDataLocation.wrapAll(locations);
|
||||
assertThat(optionalLocations).hasSize(1);
|
||||
assertThat(optionalLocations.get(0)).isInstanceOf(OptionalConfigDataLocation.class).extracting("location")
|
||||
.isSameAs(this.location);
|
||||
}
|
||||
|
||||
@Test
|
||||
void unwrapUnwrapps() {
|
||||
ConfigDataLocation optionalLocation = new OptionalConfigDataLocation(this.location);
|
||||
assertThatObject(OptionalConfigDataLocation.unwrap(optionalLocation)).isSameAs(this.location);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -29,36 +29,43 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link ResourceConfigDataLoader}.
|
||||
* Tests for {@link StandardConfigDataLoader}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class ResourceConfigDataLoaderTests {
|
||||
public class StandardConfigDataLoaderTests {
|
||||
|
||||
private ResourceConfigDataLoader loader = new ResourceConfigDataLoader();
|
||||
private StandardConfigDataLoader loader = new StandardConfigDataLoader();
|
||||
|
||||
private ConfigDataLoaderContext loaderContext = mock(ConfigDataLoaderContext.class);
|
||||
|
||||
@Test
|
||||
void loadWhenLocationResultsInMultiplePropertySourcesAddsAllToConfigData() throws IOException {
|
||||
ResourceConfigDataLocation location = new ResourceConfigDataLocation("application.yml",
|
||||
new ClassPathResource("configdata/yaml/application.yml"), null, new YamlPropertySourceLoader());
|
||||
ClassPathResource resource = new ClassPathResource("configdata/yaml/application.yml");
|
||||
StandardConfigDataReference reference = new StandardConfigDataReference(
|
||||
ConfigDataLocation.of("classpath:configdata/yaml/application.yml"), null,
|
||||
"classpath:configdata/yaml/application", null, "yml", new YamlPropertySourceLoader());
|
||||
StandardConfigDataResource location = new StandardConfigDataResource(reference, resource);
|
||||
ConfigData configData = this.loader.load(this.loaderContext, location);
|
||||
assertThat(configData.getPropertySources().size()).isEqualTo(2);
|
||||
PropertySource<?> source1 = configData.getPropertySources().get(0);
|
||||
PropertySource<?> source2 = configData.getPropertySources().get(1);
|
||||
assertThat(source1.getName()).isEqualTo("application.yml (document #0)");
|
||||
assertThat(source1.getName()).isEqualTo("Config resource 'classpath:configdata/yaml/application.yml' "
|
||||
+ "via location 'classpath:configdata/yaml/application.yml' (document #0)");
|
||||
assertThat(source1.getProperty("foo")).isEqualTo("bar");
|
||||
assertThat(source2.getName()).isEqualTo("application.yml (document #1)");
|
||||
assertThat(source2.getName()).isEqualTo("Config resource 'classpath:configdata/yaml/application.yml' "
|
||||
+ "via location 'classpath:configdata/yaml/application.yml' (document #1)");
|
||||
assertThat(source2.getProperty("hello")).isEqualTo("world");
|
||||
}
|
||||
|
||||
@Test
|
||||
void loadWhenPropertySourceIsEmptyAddsNothingToConfigData() throws IOException {
|
||||
ResourceConfigDataLocation location = new ResourceConfigDataLocation("testproperties.properties",
|
||||
new ClassPathResource("config/0-empty/testproperties.properties"), null,
|
||||
new PropertiesPropertySourceLoader());
|
||||
ClassPathResource resource = new ClassPathResource("config/0-empty/testproperties.properties");
|
||||
StandardConfigDataReference reference = new StandardConfigDataReference(
|
||||
ConfigDataLocation.of("classpath:config/0-empty/testproperties.properties"), null,
|
||||
"config/0-empty/testproperties", null, "properties", new PropertiesPropertySourceLoader());
|
||||
StandardConfigDataResource location = new StandardConfigDataResource(reference, resource);
|
||||
ConfigData configData = this.loader.load(this.loaderContext, location);
|
||||
assertThat(configData.getPropertySources().size()).isEqualTo(0);
|
||||
}
|
||||
|
|
@ -38,14 +38,14 @@ import static org.mockito.BDDMockito.given;
|
|||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link ResourceConfigDataLocationResolver}.
|
||||
* Tests for {@link StandardConfigDataLocationResolver}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class ResourceConfigDataLocationResolverTests {
|
||||
public class StandardConfigDataLocationResolverTests {
|
||||
|
||||
private ResourceConfigDataLocationResolver resolver;
|
||||
private StandardConfigDataLocationResolver resolver;
|
||||
|
||||
private ConfigDataLocationResolverContext context = mock(ConfigDataLocationResolverContext.class);
|
||||
|
||||
|
|
@ -59,19 +59,19 @@ public class ResourceConfigDataLocationResolverTests {
|
|||
void setup() {
|
||||
this.environment = new MockEnvironment();
|
||||
this.environmentBinder = Binder.get(this.environment);
|
||||
this.resolver = new ResourceConfigDataLocationResolver(new DeferredLog(), this.environmentBinder,
|
||||
this.resolver = new StandardConfigDataLocationResolver(new DeferredLog(), this.environmentBinder,
|
||||
this.resourceLoader);
|
||||
}
|
||||
|
||||
@Test
|
||||
void isResolvableAlwaysReturnsTrue() {
|
||||
assertThat(this.resolver.isResolvable(this.context, "test")).isTrue();
|
||||
assertThat(this.resolver.isResolvable(this.context, ConfigDataLocation.of("test"))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenLocationIsDirectoryResolvesAllMatchingFilesInDirectory() {
|
||||
String location = "classpath:/configdata/properties/";
|
||||
List<ResourceConfigDataLocation> locations = this.resolver.resolve(this.context, location, true);
|
||||
ConfigDataLocation location = ConfigDataLocation.of("classpath:/configdata/properties/");
|
||||
List<StandardConfigDataResource> locations = this.resolver.resolve(this.context, location);
|
||||
assertThat(locations.size()).isEqualTo(1);
|
||||
assertThat(locations).extracting(Object::toString)
|
||||
.containsExactly("class path resource [configdata/properties/application.properties]");
|
||||
|
|
@ -79,8 +79,9 @@ public class ResourceConfigDataLocationResolverTests {
|
|||
|
||||
@Test
|
||||
void resolveWhenLocationIsFileResolvesFile() {
|
||||
String location = "file:src/test/resources/configdata/properties/application.properties";
|
||||
List<ResourceConfigDataLocation> locations = this.resolver.resolve(this.context, location, true);
|
||||
ConfigDataLocation location = ConfigDataLocation
|
||||
.of("file:src/test/resources/configdata/properties/application.properties");
|
||||
List<StandardConfigDataResource> locations = this.resolver.resolve(this.context, location);
|
||||
assertThat(locations.size()).isEqualTo(1);
|
||||
assertThat(locations).extracting(Object::toString).containsExactly(
|
||||
filePath("src", "test", "resources", "configdata", "properties", "application.properties"));
|
||||
|
|
@ -88,23 +89,24 @@ public class ResourceConfigDataLocationResolverTests {
|
|||
|
||||
@Test
|
||||
void resolveWhenLocationIsFileAndNoMatchingLoaderThrowsException() {
|
||||
String location = "file:src/test/resources/configdata/properties/application.unknown";
|
||||
assertThatIllegalStateException().isThrownBy(() -> this.resolver.resolve(this.context, location, true))
|
||||
ConfigDataLocation location = ConfigDataLocation
|
||||
.of("file:src/test/resources/configdata/properties/application.unknown");
|
||||
assertThatIllegalStateException().isThrownBy(() -> this.resolver.resolve(this.context, location))
|
||||
.withMessageStartingWith("Unable to load config data from")
|
||||
.satisfies((ex) -> assertThat(ex.getCause()).hasMessageStartingWith("File extension is not known"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenLocationWildcardIsSpecifiedForClasspathLocationThrowsException() {
|
||||
String location = "classpath*:application.properties";
|
||||
assertThatIllegalStateException().isThrownBy(() -> this.resolver.resolve(this.context, location, true))
|
||||
ConfigDataLocation location = ConfigDataLocation.of("classpath*:application.properties");
|
||||
assertThatIllegalStateException().isThrownBy(() -> this.resolver.resolve(this.context, location))
|
||||
.withMessageContaining("Classpath wildcard patterns cannot be used as a search location");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenLocationWildcardIsNotBeforeLastSlashThrowsException() {
|
||||
String location = "file:src/test/resources/*/config/";
|
||||
assertThatIllegalStateException().isThrownBy(() -> this.resolver.resolve(this.context, location, true))
|
||||
ConfigDataLocation location = ConfigDataLocation.of("file:src/test/resources/*/config/");
|
||||
assertThatIllegalStateException().isThrownBy(() -> this.resolver.resolve(this.context, location))
|
||||
.withMessageStartingWith("Search location '").withMessageEndingWith("' must end with '*/'");
|
||||
}
|
||||
|
||||
|
|
@ -113,24 +115,24 @@ public class ResourceConfigDataLocationResolverTests {
|
|||
this.environment.setProperty("spring.config.name", "*/application");
|
||||
assertThatIllegalStateException()
|
||||
.isThrownBy(
|
||||
() -> new ResourceConfigDataLocationResolver(null, this.environmentBinder, this.resourceLoader))
|
||||
() -> new StandardConfigDataLocationResolver(null, this.environmentBinder, this.resourceLoader))
|
||||
.withMessageStartingWith("Config name '").withMessageEndingWith("' cannot contain '*'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenLocationHasMultipleWildcardsThrowsException() {
|
||||
String location = "file:src/test/resources/config/**/";
|
||||
assertThatIllegalStateException().isThrownBy(() -> this.resolver.resolve(this.context, location, true))
|
||||
ConfigDataLocation location = ConfigDataLocation.of("file:src/test/resources/config/**/");
|
||||
assertThatIllegalStateException().isThrownBy(() -> this.resolver.resolve(this.context, location))
|
||||
.withMessageStartingWith("Search location '")
|
||||
.withMessageEndingWith("' cannot contain multiple wildcards");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenLocationIsWildcardDirectoriesRestrictsToOneLevelDeep() {
|
||||
String location = "file:src/test/resources/config/*/";
|
||||
ConfigDataLocation location = ConfigDataLocation.of("file:src/test/resources/config/*/");
|
||||
this.environment.setProperty("spring.config.name", "testproperties");
|
||||
this.resolver = new ResourceConfigDataLocationResolver(null, this.environmentBinder, this.resourceLoader);
|
||||
List<ResourceConfigDataLocation> locations = this.resolver.resolve(this.context, location, true);
|
||||
this.resolver = new StandardConfigDataLocationResolver(null, this.environmentBinder, this.resourceLoader);
|
||||
List<StandardConfigDataResource> locations = this.resolver.resolve(this.context, location);
|
||||
assertThat(locations.size()).isEqualTo(3);
|
||||
assertThat(locations).extracting(Object::toString)
|
||||
.contains(filePath("src", "test", "resources", "config", "1-first", "testproperties.properties"))
|
||||
|
|
@ -140,10 +142,10 @@ public class ResourceConfigDataLocationResolverTests {
|
|||
|
||||
@Test
|
||||
void resolveWhenLocationIsWildcardDirectoriesSortsAlphabeticallyBasedOnAbsolutePath() {
|
||||
String location = "file:src/test/resources/config/*/";
|
||||
ConfigDataLocation location = ConfigDataLocation.of("file:src/test/resources/config/*/");
|
||||
this.environment.setProperty("spring.config.name", "testproperties");
|
||||
this.resolver = new ResourceConfigDataLocationResolver(null, this.environmentBinder, this.resourceLoader);
|
||||
List<ResourceConfigDataLocation> locations = this.resolver.resolve(this.context, location, true);
|
||||
this.resolver = new StandardConfigDataLocationResolver(null, this.environmentBinder, this.resourceLoader);
|
||||
List<StandardConfigDataResource> locations = this.resolver.resolve(this.context, location);
|
||||
assertThat(locations).extracting(Object::toString).containsExactly(
|
||||
filePath("src", "test", "resources", "config", "0-empty", "testproperties.properties"),
|
||||
filePath("src", "test", "resources", "config", "1-first", "testproperties.properties"),
|
||||
|
|
@ -152,8 +154,9 @@ public class ResourceConfigDataLocationResolverTests {
|
|||
|
||||
@Test
|
||||
void resolveWhenLocationIsWildcardFilesLoadsAllFilesThatMatch() {
|
||||
String location = "file:src/test/resources/config/*/testproperties.properties";
|
||||
List<ResourceConfigDataLocation> locations = this.resolver.resolve(this.context, location, true);
|
||||
ConfigDataLocation location = ConfigDataLocation
|
||||
.of("file:src/test/resources/config/*/testproperties.properties");
|
||||
List<StandardConfigDataResource> locations = this.resolver.resolve(this.context, location);
|
||||
assertThat(locations.size()).isEqualTo(3);
|
||||
assertThat(locations).extracting(Object::toString)
|
||||
.contains(filePath("src", "test", "resources", "config", "1-first", "testproperties.properties"))
|
||||
|
|
@ -165,15 +168,17 @@ public class ResourceConfigDataLocationResolverTests {
|
|||
@Test
|
||||
void resolveWhenLocationIsRelativeAndFileResolves() {
|
||||
this.environment.setProperty("spring.config.name", "other");
|
||||
String location = "other.properties";
|
||||
this.resolver = new ResourceConfigDataLocationResolver(new DeferredLog(), this.environmentBinder,
|
||||
ConfigDataLocation location = ConfigDataLocation.of("other.properties");
|
||||
this.resolver = new StandardConfigDataLocationResolver(new DeferredLog(), this.environmentBinder,
|
||||
this.resourceLoader);
|
||||
ClassPathResource parentResource = new ClassPathResource("configdata/properties/application.properties");
|
||||
ResourceConfigDataLocation parent = new ResourceConfigDataLocation(
|
||||
"classpath:/configdata/properties/application.properties", parentResource, null,
|
||||
StandardConfigDataReference parentReference = new StandardConfigDataReference(
|
||||
ConfigDataLocation.of("classpath:configdata/properties/application.properties"), null,
|
||||
"classpath:configdata/properties/application", null, "properties",
|
||||
new PropertiesPropertySourceLoader());
|
||||
ClassPathResource parentResource = new ClassPathResource("configdata/properties/application.properties");
|
||||
StandardConfigDataResource parent = new StandardConfigDataResource(parentReference, parentResource);
|
||||
given(this.context.getParent()).willReturn(parent);
|
||||
List<ResourceConfigDataLocation> locations = this.resolver.resolve(this.context, location, true);
|
||||
List<StandardConfigDataResource> locations = this.resolver.resolve(this.context, location);
|
||||
assertThat(locations.size()).isEqualTo(1);
|
||||
assertThat(locations).extracting(Object::toString)
|
||||
.contains("class path resource [configdata/properties/other.properties]");
|
||||
|
|
@ -182,14 +187,16 @@ public class ResourceConfigDataLocationResolverTests {
|
|||
@Test
|
||||
void resolveWhenLocationIsRelativeAndDirectoryResolves() {
|
||||
this.environment.setProperty("spring.config.name", "testproperties");
|
||||
String location = "nested/3-third/";
|
||||
this.resolver = new ResourceConfigDataLocationResolver(new DeferredLog(), this.environmentBinder,
|
||||
ConfigDataLocation location = ConfigDataLocation.of("nested/3-third/");
|
||||
this.resolver = new StandardConfigDataLocationResolver(new DeferredLog(), this.environmentBinder,
|
||||
this.resourceLoader);
|
||||
StandardConfigDataReference parentReference = new StandardConfigDataReference(
|
||||
ConfigDataLocation.of("optional:classpath:configdata/"), null, "classpath:config/specific", null,
|
||||
"properties", new PropertiesPropertySourceLoader());
|
||||
ClassPathResource parentResource = new ClassPathResource("config/specific.properties");
|
||||
ResourceConfigDataLocation parent = new ResourceConfigDataLocation("classpath:/config/specific.properties",
|
||||
parentResource, null, new PropertiesPropertySourceLoader());
|
||||
StandardConfigDataResource parent = new StandardConfigDataResource(parentReference, parentResource);
|
||||
given(this.context.getParent()).willReturn(parent);
|
||||
List<ResourceConfigDataLocation> locations = this.resolver.resolve(this.context, location, true);
|
||||
List<StandardConfigDataResource> locations = this.resolver.resolve(this.context, location);
|
||||
assertThat(locations.size()).isEqualTo(1);
|
||||
assertThat(locations).extracting(Object::toString)
|
||||
.contains("class path resource [config/nested/3-third/testproperties.properties]");
|
||||
|
|
@ -197,34 +204,36 @@ public class ResourceConfigDataLocationResolverTests {
|
|||
|
||||
@Test
|
||||
void resolveWhenLocationIsRelativeAndNoMatchingLoaderThrowsException() {
|
||||
String location = "application.other";
|
||||
ClassPathResource parentResource = new ClassPathResource("configdata/application.properties");
|
||||
ResourceConfigDataLocation parent = new ResourceConfigDataLocation(
|
||||
"classpath:/configdata/application.properties", parentResource, null,
|
||||
new PropertiesPropertySourceLoader());
|
||||
ConfigDataLocation location = ConfigDataLocation.of("application.other");
|
||||
StandardConfigDataReference parentReference = new StandardConfigDataReference(
|
||||
ConfigDataLocation.of("classpath:configdata/properties/application.properties"), null,
|
||||
"configdata/properties/application", null, "properties", new PropertiesPropertySourceLoader());
|
||||
ClassPathResource parentResource = new ClassPathResource("configdata/properties/application.properties");
|
||||
StandardConfigDataResource parent = new StandardConfigDataResource(parentReference, parentResource);
|
||||
given(this.context.getParent()).willReturn(parent);
|
||||
assertThatIllegalStateException().isThrownBy(() -> this.resolver.resolve(this.context, location, true))
|
||||
assertThatIllegalStateException().isThrownBy(() -> this.resolver.resolve(this.context, location))
|
||||
.withMessageStartingWith("Unable to load config data from 'application.other'")
|
||||
.satisfies((ex) -> assertThat(ex.getCause()).hasMessageStartingWith("File extension is not known"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveWhenLocationUsesOptionalExtensionSyntaxResolves() throws Exception {
|
||||
String location = "classpath:/application-props-no-extension[.properties]";
|
||||
List<ResourceConfigDataLocation> locations = this.resolver.resolve(this.context, location, true);
|
||||
ConfigDataLocation location = ConfigDataLocation.of("classpath:/application-props-no-extension[.properties]");
|
||||
List<StandardConfigDataResource> locations = this.resolver.resolve(this.context, location);
|
||||
assertThat(locations.size()).isEqualTo(1);
|
||||
ResourceConfigDataLocation resolved = locations.get(0);
|
||||
StandardConfigDataResource resolved = locations.get(0);
|
||||
assertThat(resolved.getResource().getFilename()).endsWith("application-props-no-extension");
|
||||
PropertySource<?> propertySource = resolved.load().get(0);
|
||||
ConfigData loaded = new StandardConfigDataLoader().load(null, resolved);
|
||||
PropertySource<?> propertySource = loaded.getPropertySources().get(0);
|
||||
assertThat(propertySource.getProperty("withnotext")).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void resolveProfileSpecificReturnsProfileSpecificFiles() {
|
||||
String location = "classpath:/configdata/properties/";
|
||||
ConfigDataLocation location = ConfigDataLocation.of("classpath:/configdata/properties/");
|
||||
Profiles profiles = mock(Profiles.class);
|
||||
given(profiles.iterator()).willReturn(Collections.singletonList("dev").iterator());
|
||||
List<ResourceConfigDataLocation> locations = this.resolver.resolveProfileSpecific(this.context, location, true,
|
||||
List<StandardConfigDataResource> locations = this.resolver.resolveProfileSpecific(this.context, location,
|
||||
profiles);
|
||||
assertThat(locations.size()).isEqualTo(1);
|
||||
assertThat(locations).extracting(Object::toString)
|
||||
|
|
@ -233,11 +242,11 @@ public class ResourceConfigDataLocationResolverTests {
|
|||
|
||||
@Test
|
||||
void resolveProfileSpecificWhenLocationIsFileReturnsEmptyList() {
|
||||
String location = "classpath:/configdata/properties/application.properties";
|
||||
ConfigDataLocation location = ConfigDataLocation.of("classpath:/configdata/properties/application.properties");
|
||||
Profiles profiles = mock(Profiles.class);
|
||||
given(profiles.iterator()).willReturn(Collections.emptyIterator());
|
||||
given(profiles.getActive()).willReturn(Collections.singletonList("dev"));
|
||||
List<ResourceConfigDataLocation> locations = this.resolver.resolveProfileSpecific(this.context, location, true,
|
||||
List<StandardConfigDataResource> locations = this.resolver.resolveProfileSpecific(this.context, location,
|
||||
profiles);
|
||||
assertThat(locations).isEmpty();
|
||||
}
|
||||
|
|
@ -18,7 +18,6 @@ package org.springframework.boot.context.config;
|
|||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.env.PropertySourceLoader;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
|
||||
|
|
@ -27,47 +26,34 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
|
|||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Tests for {@link ResourceConfigDataLocation}.
|
||||
* Tests for {@link StandardConfigDataResource}.
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class ResourceConfigDataLocationTests {
|
||||
public class StandardConfigDataResourceTests {
|
||||
|
||||
private final String location = "location";
|
||||
StandardConfigDataReference reference = mock(StandardConfigDataReference.class);
|
||||
|
||||
private final Resource resource = mock(Resource.class);
|
||||
|
||||
private final PropertySourceLoader propertySourceLoader = mock(PropertySourceLoader.class);
|
||||
|
||||
@Test
|
||||
void constructorWhenNameIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new ResourceConfigDataLocation(null, this.resource, null, this.propertySourceLoader))
|
||||
.withMessage("Name must not be null");
|
||||
void createWhenReferenceIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new StandardConfigDataResource(null, this.resource))
|
||||
.withMessage("Reference must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorWhenResourceIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new ResourceConfigDataLocation(this.location, null, null, this.propertySourceLoader))
|
||||
void createWhenResourceIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException().isThrownBy(() -> new StandardConfigDataResource(this.reference, null))
|
||||
.withMessage("Resource must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void constructorWhenLoaderIsNullThrowsException() {
|
||||
assertThatIllegalArgumentException()
|
||||
.isThrownBy(() -> new ResourceConfigDataLocation(this.location, this.resource, null, null))
|
||||
.withMessage("PropertySourceLoader must not be null");
|
||||
}
|
||||
|
||||
@Test
|
||||
void equalsWhenResourceIsTheSameReturnsTrue() {
|
||||
Resource resource = new ClassPathResource("config/");
|
||||
ResourceConfigDataLocation location = new ResourceConfigDataLocation("my-location", resource, null,
|
||||
this.propertySourceLoader);
|
||||
ResourceConfigDataLocation other = new ResourceConfigDataLocation("other-location", resource, null,
|
||||
this.propertySourceLoader);
|
||||
StandardConfigDataResource location = new StandardConfigDataResource(this.reference, resource);
|
||||
StandardConfigDataResource other = new StandardConfigDataResource(this.reference, resource);
|
||||
assertThat(location).isEqualTo(other);
|
||||
}
|
||||
|
||||
|
|
@ -75,10 +61,8 @@ public class ResourceConfigDataLocationTests {
|
|||
void equalsWhenResourceIsDifferentReturnsFalse() {
|
||||
Resource resource1 = new ClassPathResource("config/");
|
||||
Resource resource2 = new ClassPathResource("configdata/");
|
||||
ResourceConfigDataLocation location = new ResourceConfigDataLocation("my-location", resource1, null,
|
||||
this.propertySourceLoader);
|
||||
ResourceConfigDataLocation other = new ResourceConfigDataLocation("other-location", resource2, null,
|
||||
this.propertySourceLoader);
|
||||
StandardConfigDataResource location = new StandardConfigDataResource(this.reference, resource1);
|
||||
StandardConfigDataResource other = new StandardConfigDataResource(this.reference, resource2);
|
||||
assertThat(location).isNotEqualTo(other);
|
||||
}
|
||||
|
||||
|
|
@ -37,27 +37,27 @@ import org.springframework.core.env.MapPropertySource;
|
|||
*/
|
||||
class TestConfigDataBootstrap {
|
||||
|
||||
static class LocationResolver implements ConfigDataLocationResolver<Location> {
|
||||
static class LocationResolver implements ConfigDataLocationResolver<Resource> {
|
||||
|
||||
@Override
|
||||
public boolean isResolvable(ConfigDataLocationResolverContext context, String location) {
|
||||
return location.startsWith("testbootstrap:");
|
||||
public boolean isResolvable(ConfigDataLocationResolverContext context, ConfigDataLocation location) {
|
||||
return location.hasPrefix("testbootstrap:");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Location> resolve(ConfigDataLocationResolverContext context, String location, boolean optional) {
|
||||
public List<Resource> resolve(ConfigDataLocationResolverContext context, ConfigDataLocation location) {
|
||||
context.getBootstrapContext().registerIfAbsent(ResolverHelper.class,
|
||||
InstanceSupplier.from(() -> new ResolverHelper(location)));
|
||||
ResolverHelper helper = context.getBootstrapContext().get(ResolverHelper.class);
|
||||
return Collections.singletonList(new Location(helper));
|
||||
return Collections.singletonList(new Resource(helper));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class Loader implements ConfigDataLoader<Location> {
|
||||
static class Loader implements ConfigDataLoader<Resource> {
|
||||
|
||||
@Override
|
||||
public ConfigData load(ConfigDataLoaderContext context, Location location) throws IOException {
|
||||
public ConfigData load(ConfigDataLoaderContext context, Resource location) throws IOException {
|
||||
context.getBootstrapContext().registerIfAbsent(LoaderHelper.class,
|
||||
(bootstrapContext) -> new LoaderHelper(location, () -> bootstrapContext.get(Binder.class)));
|
||||
LoaderHelper helper = context.getBootstrapContext().get(LoaderHelper.class);
|
||||
|
|
@ -68,11 +68,11 @@ class TestConfigDataBootstrap {
|
|||
|
||||
}
|
||||
|
||||
static class Location extends ConfigDataLocation {
|
||||
static class Resource extends ConfigDataResource {
|
||||
|
||||
private final ResolverHelper resolverHelper;
|
||||
|
||||
Location(ResolverHelper resolverHelper) {
|
||||
Resource(ResolverHelper resolverHelper) {
|
||||
this.resolverHelper = resolverHelper;
|
||||
}
|
||||
|
||||
|
|
@ -89,13 +89,13 @@ class TestConfigDataBootstrap {
|
|||
|
||||
static class ResolverHelper {
|
||||
|
||||
private final String location;
|
||||
private final ConfigDataLocation location;
|
||||
|
||||
ResolverHelper(String location) {
|
||||
ResolverHelper(ConfigDataLocation location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
String getLocation() {
|
||||
ConfigDataLocation getLocation() {
|
||||
return this.location;
|
||||
}
|
||||
|
||||
|
|
@ -103,16 +103,16 @@ class TestConfigDataBootstrap {
|
|||
|
||||
static class LoaderHelper implements ApplicationListener<BootstrapContextClosedEvent> {
|
||||
|
||||
private final Location location;
|
||||
private final Resource location;
|
||||
|
||||
private final Supplier<Binder> binder;
|
||||
|
||||
LoaderHelper(Location location, Supplier<Binder> binder) {
|
||||
LoaderHelper(Resource location, Supplier<Binder> binder) {
|
||||
this.location = location;
|
||||
this.binder = binder;
|
||||
}
|
||||
|
||||
Location getLocation() {
|
||||
Resource getLocation() {
|
||||
return this.location;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,14 +30,16 @@ class UnsupportedConfigDataLocationExceptionTests {
|
|||
|
||||
@Test
|
||||
void createSetsMessage() {
|
||||
UnsupportedConfigDataLocationException exception = new UnsupportedConfigDataLocationException("test");
|
||||
UnsupportedConfigDataLocationException exception = new UnsupportedConfigDataLocationException(
|
||||
ConfigDataLocation.of("test"));
|
||||
assertThat(exception).hasMessage("Unsupported config data location 'test'");
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLocationReturnsLocation() {
|
||||
UnsupportedConfigDataLocationException exception = new UnsupportedConfigDataLocationException("test");
|
||||
assertThat(exception.getLocation()).isEqualTo("test");
|
||||
ConfigDataLocation location = ConfigDataLocation.of("test");
|
||||
UnsupportedConfigDataLocationException exception = new UnsupportedConfigDataLocationException(location);
|
||||
assertThat(exception.getLocation()).isEqualTo(location);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import org.springframework.core.env.PropertySource;
|
|||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class SubversionConfigDataLoader implements ConfigDataLoader<SubversionConfigDataLocation> {
|
||||
class SubversionConfigDataLoader implements ConfigDataLoader<SubversionConfigDataResource> {
|
||||
|
||||
private static final ApplicationListener<BootstrapContextClosedEvent> closeListener = SubversionConfigDataLoader::onBootstrapContextClosed;
|
||||
|
||||
|
|
@ -50,12 +50,12 @@ class SubversionConfigDataLoader implements ConfigDataLoader<SubversionConfigDat
|
|||
}
|
||||
|
||||
@Override
|
||||
public ConfigData load(ConfigDataLoaderContext context, SubversionConfigDataLocation location)
|
||||
public ConfigData load(ConfigDataLoaderContext context, SubversionConfigDataResource resource)
|
||||
throws IOException, ConfigDataLocationNotFoundException {
|
||||
context.getBootstrapContext().registerIfAbsent(SubversionServerCertificate.class,
|
||||
InstanceSupplier.of(location.getServerCertificate()));
|
||||
InstanceSupplier.of(resource.getServerCertificate()));
|
||||
SubversionClient client = context.getBootstrapContext().get(SubversionClient.class);
|
||||
String loaded = client.load(location.getLocation());
|
||||
String loaded = client.load(resource.getLocation());
|
||||
PropertySource<?> propertySource = new MapPropertySource("svn", Collections.singletonMap("svn", loaded));
|
||||
return new ConfigData(Collections.singleton(propertySource));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,30 +19,33 @@ package smoketest.bootstrapregistry.external.svn;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.boot.context.config.ConfigDataLocation;
|
||||
import org.springframework.boot.context.config.ConfigDataLocationNotFoundException;
|
||||
import org.springframework.boot.context.config.ConfigDataLocationResolver;
|
||||
import org.springframework.boot.context.config.ConfigDataLocationResolverContext;
|
||||
import org.springframework.boot.context.config.ConfigDataResourceNotFoundException;
|
||||
|
||||
/**
|
||||
* {@link ConfigDataLocationResolver} for subversion.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class SubversionConfigDataLocationResolver implements ConfigDataLocationResolver<SubversionConfigDataLocation> {
|
||||
class SubversionConfigDataLocationResolver implements ConfigDataLocationResolver<SubversionConfigDataResource> {
|
||||
|
||||
private static final String PREFIX = "svn:";
|
||||
|
||||
@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<SubversionConfigDataLocation> resolve(ConfigDataLocationResolverContext context, String location,
|
||||
boolean optional) throws ConfigDataLocationNotFoundException {
|
||||
public List<SubversionConfigDataResource> resolve(ConfigDataLocationResolverContext context,
|
||||
ConfigDataLocation location)
|
||||
throws ConfigDataLocationNotFoundException, ConfigDataResourceNotFoundException {
|
||||
String serverCertificate = context.getBinder().bind("spring.svn.server.certificate", String.class).orElse(null);
|
||||
return Collections.singletonList(
|
||||
new SubversionConfigDataLocation(location.substring(PREFIX.length()), serverCertificate));
|
||||
new SubversionConfigDataResource(location.getNonPrefixedValue(PREFIX), serverCertificate));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,20 +16,20 @@
|
|||
|
||||
package smoketest.bootstrapregistry.external.svn;
|
||||
|
||||
import org.springframework.boot.context.config.ConfigDataLocation;
|
||||
import org.springframework.boot.context.config.ConfigDataResource;
|
||||
|
||||
/**
|
||||
* A subversion {@link ConfigDataLocation}.
|
||||
* A subversion {@link ConfigDataResource}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class SubversionConfigDataLocation extends ConfigDataLocation {
|
||||
class SubversionConfigDataResource extends ConfigDataResource {
|
||||
|
||||
private final String location;
|
||||
|
||||
private final SubversionServerCertificate serverCertificate;
|
||||
|
||||
SubversionConfigDataLocation(String location, String serverCertificate) {
|
||||
SubversionConfigDataResource(String location, String serverCertificate) {
|
||||
this.location = location;
|
||||
this.serverCertificate = SubversionServerCertificate.of(serverCertificate);
|
||||
}
|
||||
|
|
@ -50,7 +50,7 @@ class SubversionConfigDataLocation extends ConfigDataLocation {
|
|||
if (obj == null || getClass() != obj.getClass()) {
|
||||
return false;
|
||||
}
|
||||
SubversionConfigDataLocation other = (SubversionConfigDataLocation) obj;
|
||||
SubversionConfigDataResource other = (SubversionConfigDataResource) obj;
|
||||
return this.location.equals(other.location);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue