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-23711pull/23706/head
parent
f89b99bdbc
commit
1cf9fc107e
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
10
spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionConfigDataLocation.java → spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionConfigDataResource.java
vendored
10
spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionConfigDataLocation.java → spring-boot-tests/spring-boot-smoke-tests/spring-boot-smoke-test-bootstrap-registry/src/main/java/smoketest/bootstrapregistry/external/svn/SubversionConfigDataResource.java
vendored
Loading…
Reference in New Issue