Merge branch '2.4.x'

Closes gh-26585
pull/26586/head
Phillip Webb 4 years ago
commit 5c28f8f6a8

@ -233,7 +233,8 @@ class ConfigDataEnvironment {
contributors = processWithoutProfiles(contributors, importer, activationContext);
activationContext = withProfiles(contributors, activationContext);
contributors = processWithProfiles(contributors, importer, activationContext);
applyToEnvironment(contributors, activationContext, importer.getLoadedLocations());
applyToEnvironment(contributors, activationContext, importer.getLoadedLocations(),
importer.getOptionalLocations());
}
private ConfigDataEnvironmentContributors processInitial(ConfigDataEnvironmentContributors contributors,
@ -322,9 +323,10 @@ class ConfigDataEnvironment {
}
private void applyToEnvironment(ConfigDataEnvironmentContributors contributors,
ConfigDataActivationContext activationContext, Set<ConfigDataLocation> loadedLocations) {
ConfigDataActivationContext activationContext, Set<ConfigDataLocation> loadedLocations,
Set<ConfigDataLocation> optionalLocations) {
checkForInvalidProperties(contributors);
checkMandatoryLocations(contributors, activationContext, loadedLocations);
checkMandatoryLocations(contributors, activationContext, loadedLocations, optionalLocations);
MutablePropertySources propertySources = this.environment.getPropertySources();
this.logger.trace("Applying config data environment contributions");
for (ConfigDataEnvironmentContributor contributor : contributors) {
@ -359,7 +361,8 @@ class ConfigDataEnvironment {
}
private void checkMandatoryLocations(ConfigDataEnvironmentContributors contributors,
ConfigDataActivationContext activationContext, Set<ConfigDataLocation> loadedLocations) {
ConfigDataActivationContext activationContext, Set<ConfigDataLocation> loadedLocations,
Set<ConfigDataLocation> optionalLocations) {
Set<ConfigDataLocation> mandatoryLocations = new LinkedHashSet<>();
for (ConfigDataEnvironmentContributor contributor : contributors) {
if (contributor.isActive(activationContext)) {
@ -372,6 +375,7 @@ class ConfigDataEnvironment {
}
}
mandatoryLocations.removeAll(loadedLocations);
mandatoryLocations.removeAll(optionalLocations);
if (!mandatoryLocations.isEmpty()) {
for (ConfigDataLocation mandatoryLocation : mandatoryLocations) {
this.notFoundAction.handle(this.logger, new ConfigDataLocationNotFoundException(mandatoryLocation));

@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
@ -51,6 +51,8 @@ class ConfigDataImporter {
private final Set<ConfigDataLocation> loadedLocations = new HashSet<>();
private final Set<ConfigDataLocation> optionalLocations = new HashSet<>();
/**
* Create a new {@link ConfigDataImporter} instance.
* @param logFactory the log factory
@ -103,7 +105,7 @@ class ConfigDataImporter {
return this.resolvers.resolve(locationResolverContext, location, profiles);
}
catch (ConfigDataNotFoundException ex) {
handle(ex, location);
handle(ex, location, null);
return Collections.emptyList();
}
}
@ -115,6 +117,9 @@ class ConfigDataImporter {
ConfigDataResolutionResult candidate = candidates.get(i);
ConfigDataLocation location = candidate.getLocation();
ConfigDataResource resource = candidate.getResource();
if (resource.isOptional()) {
this.optionalLocations.add(location);
}
if (this.loaded.contains(resource)) {
this.loadedLocations.add(location);
}
@ -128,26 +133,33 @@ class ConfigDataImporter {
}
}
catch (ConfigDataNotFoundException ex) {
handle(ex, location);
handle(ex, location, resource);
}
}
}
return Collections.unmodifiableMap(result);
}
private void handle(ConfigDataNotFoundException ex, ConfigDataLocation location) {
private void handle(ConfigDataNotFoundException ex, ConfigDataLocation location, ConfigDataResource resource) {
if (ex instanceof ConfigDataResourceNotFoundException) {
ex = ((ConfigDataResourceNotFoundException) ex).withLocation(location);
}
getNotFoundAction(location).handle(this.logger, ex);
getNotFoundAction(location, resource).handle(this.logger, ex);
}
private ConfigDataNotFoundAction getNotFoundAction(ConfigDataLocation location) {
return (!location.isOptional()) ? this.notFoundAction : ConfigDataNotFoundAction.IGNORE;
private ConfigDataNotFoundAction getNotFoundAction(ConfigDataLocation location, ConfigDataResource resource) {
if (location.isOptional() || (resource != null && resource.isOptional())) {
return ConfigDataNotFoundAction.IGNORE;
}
return this.notFoundAction;
}
Set<ConfigDataLocation> getLoadedLocations() {
return this.loadedLocations;
}
Set<ConfigDataLocation> getOptionalLocations() {
return this.optionalLocations;
}
}

@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors.
* Copyright 2012-2021 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.
@ -27,4 +27,26 @@ package org.springframework.boot.context.config;
*/
public abstract class ConfigDataResource {
private final boolean optional;
/**
* Create a new non-optional {@link ConfigDataResource} instance.
*/
public ConfigDataResource() {
this(false);
}
/**
* Create a new {@link ConfigDataResource} instance.
* @param optional if the resource is optional
* @since 2.4.6
*/
protected ConfigDataResource(boolean optional) {
this.optional = optional;
}
boolean isOptional() {
return this.optional;
}
}

@ -46,6 +46,7 @@ import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationProperty;
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
import org.springframework.boot.origin.Origin;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
@ -581,6 +582,12 @@ class ConfigDataEnvironmentPostProcessorIntegrationTests {
+ StringUtils.cleanPath(location.getAbsolutePath())));
}
@Test
void runWhenResolvedIsOptionalDoesNotThrowException() {
ApplicationContext context = this.application.run("--spring.config.location=test:optionalresult");
assertThat(context.getEnvironment().containsProperty("spring")).isFalse();
}
@Test
@Disabled("Disabled until spring.profiles suppport is dropped")
void runWhenUsingInvalidPropertyThrowsException() {
@ -752,4 +759,44 @@ class ConfigDataEnvironmentPostProcessorIntegrationTests {
}
static class LocationResolver implements ConfigDataLocationResolver<TestConfigDataResource> {
@Override
public boolean isResolvable(ConfigDataLocationResolverContext context, ConfigDataLocation location) {
return location.hasPrefix("test:");
}
@Override
public List<TestConfigDataResource> resolve(ConfigDataLocationResolverContext context,
ConfigDataLocation location)
throws ConfigDataLocationNotFoundException, ConfigDataResourceNotFoundException {
return Collections.singletonList(new TestConfigDataResource(location));
}
}
static class Loader implements ConfigDataLoader<TestConfigDataResource> {
@Override
public ConfigData load(ConfigDataLoaderContext context, TestConfigDataResource resource)
throws IOException, ConfigDataResourceNotFoundException {
if (resource.isOptional()) {
return null;
}
MapPropertySource propertySource = new MapPropertySource("loaded",
Collections.singletonMap("spring", "boot"));
return new ConfigData(Collections.singleton(propertySource));
}
}
static class TestConfigDataResource extends ConfigDataResource {
TestConfigDataResource(ConfigDataLocation location) {
super(location.toString().contains("optionalresult"));
}
}
}

@ -168,8 +168,27 @@ class ConfigDataLocationResolversTests {
.satisfies((ex) -> assertThat(ex.getLocation()).isEqualTo(location));
}
@Test
void resolveWhenOptional() {
ConfigDataLocationResolvers resolvers = new ConfigDataLocationResolvers(this.logFactory, this.bootstrapContext,
this.binder, this.resourceLoader, Arrays.asList(OptionalResourceTestResolver.class.getName()));
ConfigDataLocation location = ConfigDataLocation.of("OptionalResourceTestResolver:test");
List<ConfigDataResolutionResult> resolved = resolvers.resolve(this.context, location, null);
assertThat(resolved.get(0).getResource().isOptional()).isTrue();
}
static class TestResolver implements ConfigDataLocationResolver<TestConfigDataResource> {
private final boolean optionalResource;
TestResolver() {
this(false);
}
TestResolver(boolean optionalResource) {
this.optionalResource = optionalResource;
}
@Override
public boolean isResolvable(ConfigDataLocationResolverContext context, ConfigDataLocation location) {
String name = getClass().getName();
@ -180,13 +199,13 @@ class ConfigDataLocationResolversTests {
@Override
public List<TestConfigDataResource> resolve(ConfigDataLocationResolverContext context,
ConfigDataLocation location) {
return Collections.singletonList(new TestConfigDataResource(this, location, false));
return Collections.singletonList(new TestConfigDataResource(this.optionalResource, this, location, false));
}
@Override
public List<TestConfigDataResource> resolveProfileSpecific(ConfigDataLocationResolverContext context,
ConfigDataLocation location, Profiles profiles) {
return Collections.singletonList(new TestConfigDataResource(this, location, true));
return Collections.singletonList(new TestConfigDataResource(this.optionalResource, this, location, true));
}
}
@ -249,6 +268,14 @@ class ConfigDataLocationResolversTests {
}
static class OptionalResourceTestResolver extends TestResolver {
OptionalResourceTestResolver() {
super(true);
}
}
static class TestConfigDataResource extends ConfigDataResource {
private final TestResolver resolver;
@ -257,7 +284,9 @@ class ConfigDataLocationResolversTests {
private final boolean profileSpecific;
TestConfigDataResource(TestResolver resolver, ConfigDataLocation location, boolean profileSpecific) {
TestConfigDataResource(boolean optional, TestResolver resolver, ConfigDataLocation location,
boolean profileSpecific) {
super(optional);
this.resolver = resolver;
this.location = location;
this.profileSpecific = profileSpecific;

@ -3,9 +3,11 @@ org.springframework.boot.context.config.TestPropertySourceLoader1,\
org.springframework.boot.context.config.TestPropertySourceLoader2
org.springframework.boot.context.config.ConfigDataLocationResolver=\
org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessorIntegrationTests.LocationResolver,\
org.springframework.boot.context.config.TestConfigDataBootstrap.LocationResolver,\
org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessorImportCombinedWithProfileSpecificIntegrationTests.LocationResolver
org.springframework.boot.context.config.ConfigDataLoader=\
org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessorIntegrationTests.Loader,\
org.springframework.boot.context.config.TestConfigDataBootstrap.Loader,\
org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessorImportCombinedWithProfileSpecificIntegrationTests.Loader

Loading…
Cancel
Save