From d123c924a0e1fbe6b4e8f27baf98b5bfd1a64a86 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Thu, 20 Aug 2020 17:26:01 -0700 Subject: [PATCH] Add BootstrapRegisty support for config data Expose the `BootstrapRegisty` to both `ConfigDataLocationResolver` and `ConfigDataLoader` implementations. The registry is exposed via the context interfaces and may be used to reuse instances that are expensive to create. It may also be used to ultimately register beans with the `ApplicationContext`. Closes gh-22956 --- ...nfigDataApplicationContextInitializer.java | 5 +- .../context/config/ConfigDataEnvironment.java | 11 +- .../ConfigDataEnvironmentContributors.java | 54 ++++++-- .../ConfigDataEnvironmentPostProcessor.java | 30 +++-- .../context/config/ConfigDataImporter.java | 11 +- .../boot/context/config/ConfigDataLoader.java | 6 +- .../config/ConfigDataLoaderContext.java | 37 ++++++ .../context/config/ConfigDataLoaders.java | 11 +- .../ConfigDataLocationResolverContext.java | 9 ++ .../config/ConfigTreeConfigDataLoader.java | 2 +- .../config/ResourceConfigDataLoader.java | 2 +- .../boot/env/DefaultBootstrapRegisty.java | 7 ++ ...nmentPostProcessorApplicationListener.java | 7 +- ...onfigDataEnvironmentContributorsTests.java | 97 +++++++++++---- ...ssorBootstrapRegistryIntegrationTests.java | 62 ++++++++++ ...nfigDataEnvironmentPostProcessorTests.java | 6 +- .../config/ConfigDataEnvironmentTests.java | 57 +++++---- .../config/ConfigDataImporterTests.java | 24 ++-- .../context/config/ConfigDataLoaderTests.java | 7 +- .../config/ConfigDataLoadersTests.java | 21 ++-- .../ConfigDataLocationResolverTests.java | 4 +- .../ConfigTreeConfigDataLoaderTests.java | 5 +- .../config/ResourceConfigDataLoaderTests.java | 7 +- .../config/TestConfigDataBootstrap.java | 115 ++++++++++++++++++ .../test/resources/META-INF/spring.factories | 6 + ...trap-registry-integration-tests.properties | 1 + 26 files changed, 491 insertions(+), 113 deletions(-) create mode 100644 spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaderContext.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorBootstrapRegistryIntegrationTests.java create mode 100644 spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/TestConfigDataBootstrap.java create mode 100644 spring-boot-project/spring-boot/src/test/resources/application-bootstrap-registry-integration-tests.properties diff --git a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ConfigDataApplicationContextInitializer.java b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ConfigDataApplicationContextInitializer.java index 65091484576..9b39ad11903 100644 --- a/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ConfigDataApplicationContextInitializer.java +++ b/spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/ConfigDataApplicationContextInitializer.java @@ -18,6 +18,7 @@ package org.springframework.boot.test.context; import org.springframework.boot.context.config.ConfigData; import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor; +import org.springframework.boot.env.DefaultBootstrapRegisty; import org.springframework.boot.env.DefaultPropertiesPropertySource; import org.springframework.boot.env.RandomValuePropertySource; import org.springframework.context.ApplicationContextInitializer; @@ -41,7 +42,9 @@ public class ConfigDataApplicationContextInitializer public void initialize(ConfigurableApplicationContext applicationContext) { ConfigurableEnvironment environment = applicationContext.getEnvironment(); RandomValuePropertySource.addToEnvironment(environment); - ConfigDataEnvironmentPostProcessor.applyTo(environment, applicationContext); + DefaultBootstrapRegisty bootstrapRegistry = new DefaultBootstrapRegisty(); + ConfigDataEnvironmentPostProcessor.applyTo(environment, applicationContext, bootstrapRegistry); + bootstrapRegistry.applicationContextPrepared(applicationContext); DefaultPropertiesPropertySource.moveToEnd(environment); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java index 73af332ba83..f826441a4cc 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironment.java @@ -25,6 +25,7 @@ import org.apache.commons.logging.Log; import org.springframework.boot.context.config.ConfigDataEnvironmentContributors.BinderOption; import org.springframework.boot.context.properties.bind.BindException; import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.boot.env.BootstrapRegistry; import org.springframework.boot.env.DefaultPropertiesPropertySource; import org.springframework.boot.logging.DeferredLogFactory; import org.springframework.core.env.ConfigurableEnvironment; @@ -77,6 +78,8 @@ class ConfigDataEnvironment { private final Log logger; + private final BootstrapRegistry bootstrapRegistry; + private final ConfigurableEnvironment environment; private final ConfigDataLocationResolvers resolvers; @@ -90,16 +93,18 @@ class ConfigDataEnvironment { /** * Create a new {@link ConfigDataEnvironment} instance. * @param logFactory the deferred log factory + * @param bootstrapRegistry the bootstrap registry * @param environment the Spring {@link Environment}. * @param resourceLoader {@link ResourceLoader} to load resource locations * @param additionalProfiles any additional profiles to activate */ - ConfigDataEnvironment(DeferredLogFactory logFactory, ConfigurableEnvironment environment, - ResourceLoader resourceLoader, Collection additionalProfiles) { + ConfigDataEnvironment(DeferredLogFactory logFactory, BootstrapRegistry bootstrapRegistry, + ConfigurableEnvironment environment, ResourceLoader resourceLoader, Collection additionalProfiles) { Binder binder = Binder.get(environment); UseLegacyConfigProcessingException.throwIfRequested(binder); this.logFactory = logFactory; this.logger = logFactory.getLog(getClass()); + this.bootstrapRegistry = bootstrapRegistry; this.environment = environment; this.resolvers = createConfigDataLocationResolvers(logFactory, binder, resourceLoader); this.additionalProfiles = additionalProfiles; @@ -132,7 +137,7 @@ class ConfigDataEnvironment { this.logger.trace("Creating wrapped config data contributor for default property source"); contributors.add(ConfigDataEnvironmentContributor.ofExisting(defaultPropertySource)); } - return new ConfigDataEnvironmentContributors(this.logFactory, contributors); + return new ConfigDataEnvironmentContributors(this.logFactory, this.bootstrapRegistry, contributors); } ConfigDataEnvironmentContributors getContributors() { diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributors.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributors.java index 5b26997b1d5..b659bf32728 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributors.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributors.java @@ -36,6 +36,7 @@ import org.springframework.boot.context.properties.bind.Binder; 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.env.BootstrapRegistry; import org.springframework.boot.logging.DeferredLogFactory; import org.springframework.core.log.LogMessage; import org.springframework.util.ObjectUtils; @@ -53,19 +54,25 @@ class ConfigDataEnvironmentContributors implements Iterable contributors) { this.logger = logFactory.getLog(getClass()); + this.bootstrapRegistry = bootstrapRegistry; this.root = ConfigDataEnvironmentContributor.of(contributors); } - private ConfigDataEnvironmentContributors(Log logger, ConfigDataEnvironmentContributor root) { + private ConfigDataEnvironmentContributors(Log logger, BootstrapRegistry bootstrapRegistry, + ConfigDataEnvironmentContributor root) { this.logger = logger; + this.bootstrapRegistry = bootstrapRegistry; this.root = root; } @@ -91,22 +98,27 @@ class ConfigDataEnvironmentContributors implements Iterable imports = unprocessed.getImports(); this.logger.trace(LogMessage.format("Processing imports %s", imports)); Map imported = importer.resolveAndLoad(activationContext, - locationResolverContext, imports); + locationResolverContext, loaderContext, imports); this.logger.trace(LogMessage.of(() -> imported.isEmpty() ? "Nothing imported" : "Imported " + imported.size() + " location " + ((imported.size() != 1) ? "s" : "") + imported.keySet())); ConfigDataEnvironmentContributor processed = unprocessed.withChildren(importPhase, asContributors(activationContext, imported)); - result = new ConfigDataEnvironmentContributors(this.logger, + result = new ConfigDataEnvironmentContributors(this.logger, this.bootstrapRegistry, result.getRoot().withReplacement(unprocessed, processed)); processedCount++; } } + protected final BootstrapRegistry getBootstrapRegistry() { + return this.bootstrapRegistry; + } + private ConfigDataEnvironmentContributor getFirstUnprocessed(ConfigDataEnvironmentContributors contributors, ConfigDataActivationContext activationContext, ImportPhase importPhase) { for (ConfigDataEnvironmentContributor contributor : contributors.getRoot()) { @@ -177,10 +189,27 @@ class ConfigDataEnvironmentContributors implements Iterable additionalProfiles) { - return new ConfigDataEnvironment(this.logFactory, environment, resourceLoader, additionalProfiles); + return new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry, environment, resourceLoader, + additionalProfiles); } private void postProcessUsingLegacyApplicationListener(ConfigurableEnvironment environment, @@ -103,7 +109,7 @@ public class ConfigDataEnvironmentPostProcessor implements EnvironmentPostProces * @param environment the environment to apply {@link ConfigData} to */ public static void applyTo(ConfigurableEnvironment environment) { - applyTo(environment, null, Collections.emptyList()); + applyTo(environment, null, null, Collections.emptyList()); } /** @@ -112,11 +118,13 @@ public class ConfigDataEnvironmentPostProcessor implements EnvironmentPostProces * directly and not necessarily as part of a {@link SpringApplication}. * @param environment the environment to apply {@link ConfigData} to * @param resourceLoader the resource loader to use + * @param bootstrapRegistry the bootstrap registry to use or {@code null} to use a + * throw-away registry * @param additionalProfiles any additional profiles that should be applied */ public static void applyTo(ConfigurableEnvironment environment, ResourceLoader resourceLoader, - String... additionalProfiles) { - applyTo(environment, resourceLoader, Arrays.asList(additionalProfiles)); + BootstrapRegistry bootstrapRegistry, String... additionalProfiles) { + applyTo(environment, resourceLoader, bootstrapRegistry, Arrays.asList(additionalProfiles)); } /** @@ -125,13 +133,17 @@ public class ConfigDataEnvironmentPostProcessor implements EnvironmentPostProces * directly and not necessarily as part of a {@link SpringApplication}. * @param environment the environment to apply {@link ConfigData} to * @param resourceLoader the resource loader to use + * @param bootstrapRegistry the bootstrap registry to use or {@code null} to use a + * throw-away registry * @param additionalProfiles any additional profiles that should be applied */ public static void applyTo(ConfigurableEnvironment environment, ResourceLoader resourceLoader, - Collection additionalProfiles) { - new ConfigDataEnvironmentPostProcessor(Supplier::get).postProcessEnvironment(environment, resourceLoader, - additionalProfiles); - + BootstrapRegistry bootstrapRegistry, Collection additionalProfiles) { + DeferredLogFactory logFactory = Supplier::get; + bootstrapRegistry = (bootstrapRegistry != null) ? bootstrapRegistry : new DefaultBootstrapRegisty(); + ConfigDataEnvironmentPostProcessor postProcessor = new ConfigDataEnvironmentPostProcessor(logFactory, + bootstrapRegistry); + postProcessor.postProcessEnvironment(environment, resourceLoader, additionalProfiles); } @SuppressWarnings("deprecation") diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataImporter.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataImporter.java index 73e46a21593..59dcb824878 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataImporter.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataImporter.java @@ -55,26 +55,29 @@ class ConfigDataImporter { * previously loaded. * @param activationContext the activation context * @param locationResolverContext the location resolver context + * @param loaderContext the loader context * @param locations the locations to resolve * @return a map of the loaded locations and data */ Map resolveAndLoad(ConfigDataActivationContext activationContext, - ConfigDataLocationResolverContext locationResolverContext, List locations) { + ConfigDataLocationResolverContext locationResolverContext, ConfigDataLoaderContext loaderContext, + List locations) { try { Profiles profiles = (activationContext != null) ? activationContext.getProfiles() : null; - return load(this.resolvers.resolveAll(locationResolverContext, locations, profiles)); + return load(loaderContext, this.resolvers.resolveAll(locationResolverContext, locations, profiles)); } catch (IOException ex) { throw new IllegalStateException("IO error on loading imports from " + locations, ex); } } - private Map load(List locations) throws IOException { + private Map load(ConfigDataLoaderContext loaderContext, + List locations) throws IOException { Map result = new LinkedHashMap<>(); for (int i = locations.size() - 1; i >= 0; i--) { ConfigDataLocation location = locations.get(i); if (this.loadedLocations.add(location)) { - result.put(location, this.loaders.load(location)); + result.put(location, this.loaders.load(loaderContext, location)); } } return Collections.unmodifiableMap(result); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoader.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoader.java index 7014d05aa87..75082703f7d 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoader.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoader.java @@ -40,19 +40,21 @@ public interface ConfigDataLoader { /** * Returns if the specified location 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 */ - default boolean isLoadable(L location) { + default boolean isLoadable(ConfigDataLoaderContext context, L location) { return true; } /** * Load {@link ConfigData} for the given location. + * @param context the loader context * @param location the location to load * @return the loaded config data or {@code null} if the location should be skipped * @throws IOException on IO error */ - ConfigData load(L location) throws IOException; + ConfigData load(ConfigDataLoaderContext context, L location) throws IOException; } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaderContext.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaderContext.java new file mode 100644 index 00000000000..e46211a6f32 --- /dev/null +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaderContext.java @@ -0,0 +1,37 @@ +/* + * 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.BootstrapRegistry; +import org.springframework.boot.env.EnvironmentPostProcessor; + +/** + * Context provided to {@link ConfigDataLoader} methods. + * + * @author Phillip Webb + * @since 2.4.0 + */ +public interface ConfigDataLoaderContext { + + /** + * Provides access to the {@link BootstrapRegistry} shared across all + * {@link EnvironmentPostProcessor EnvironmentPostProcessors}. + * @return the bootstrap registry + */ + BootstrapRegistry getBootstrapRegistry(); + +} diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaders.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaders.java index 3967083bcab..43c3af693b9 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaders.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLoaders.java @@ -80,24 +80,25 @@ class ConfigDataLoaders { /** * Load {@link ConfigData} using the first appropriate {@link ConfigDataLoader}. * @param the config data location type + * @param context the loader context * @param location the location to load * @return the loaded {@link ConfigData} * @throws IOException on IO error */ - ConfigData load(L location) throws IOException { - ConfigDataLoader loader = getLoader(location); + ConfigData load(ConfigDataLoaderContext context, L location) throws IOException { + ConfigDataLoader loader = getLoader(context, location); this.logger.trace(LogMessage.of(() -> "Loading " + location + " using loader " + loader.getClass().getName())); - return loader.load(location); + return loader.load(context, location); } @SuppressWarnings("unchecked") - private ConfigDataLoader getLoader(L location) { + private ConfigDataLoader getLoader(ConfigDataLoaderContext context, L location) { ConfigDataLoader result = null; for (int i = 0; i < this.loaders.size(); i++) { ConfigDataLoader candidate = this.loaders.get(i); if (this.locationTypes.get(i).isInstance(location)) { ConfigDataLoader loader = (ConfigDataLoader) candidate; - if (loader.isLoadable(location)) { + if (loader.isLoadable(context, location)) { if (result != null) { throw new IllegalStateException("Multiple loaders found for location " + location + " [" + candidate.getClass().getName() + "," + result.getClass().getName() + "]"); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolverContext.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolverContext.java index 763e1d6888f..a792a808f7d 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolverContext.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigDataLocationResolverContext.java @@ -17,6 +17,8 @@ package org.springframework.boot.context.config; import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.boot.env.BootstrapRegistry; +import org.springframework.boot.env.EnvironmentPostProcessor; /** * Context provided to {@link ConfigDataLocationResolver} methods. @@ -41,4 +43,11 @@ public interface ConfigDataLocationResolverContext { */ ConfigDataLocation getParent(); + /** + * Provides access to the {@link BootstrapRegistry} shared across all + * {@link EnvironmentPostProcessor EnvironmentPostProcessors}. + * @return the bootstrap registry + */ + BootstrapRegistry getBootstrapRegistry(); + } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigTreeConfigDataLoader.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigTreeConfigDataLoader.java index 86ae3306831..7a4bfc1adff 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigTreeConfigDataLoader.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ConfigTreeConfigDataLoader.java @@ -31,7 +31,7 @@ import org.springframework.boot.env.ConfigTreePropertySource; class ConfigTreeConfigDataLoader implements ConfigDataLoader { @Override - public ConfigData load(ConfigTreeConfigDataLocation location) throws IOException { + public ConfigData load(ConfigDataLoaderContext context, ConfigTreeConfigDataLocation location) throws IOException { Path path = location.getPath(); String name = "Config tree '" + path + "'"; ConfigTreePropertySource source = new ConfigTreePropertySource(name, path); diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ResourceConfigDataLoader.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ResourceConfigDataLoader.java index cc2414290bb..f293e6cd70a 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ResourceConfigDataLoader.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/config/ResourceConfigDataLoader.java @@ -29,7 +29,7 @@ import org.springframework.core.io.Resource; class ResourceConfigDataLoader implements ConfigDataLoader { @Override - public ConfigData load(ResourceConfigDataLocation location) throws IOException { + public ConfigData load(ConfigDataLoaderContext context, ResourceConfigDataLocation location) throws IOException { return new ConfigData(location.load()); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/DefaultBootstrapRegisty.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/DefaultBootstrapRegisty.java index a64c39cafb3..b1c214cdf57 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/DefaultBootstrapRegisty.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/DefaultBootstrapRegisty.java @@ -80,6 +80,13 @@ public class DefaultBootstrapRegisty implements BootstrapRegistry { .forEach((registration) -> registration.applicationContextPrepared(applicationContext)); } + /** + * Clear the registry to reclaim memory. + */ + public void clear() { + this.registrations.clear(); + } + /** * Default implementation of {@link Registration}. */ diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListener.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListener.java index b387cb67cc2..2e17928033f 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListener.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/EnvironmentPostProcessorApplicationListener.java @@ -104,11 +104,16 @@ public class EnvironmentPostProcessorApplicationListener implements SmartApplica } private void onApplicationPreparedEvent(ApplicationPreparedEvent event) { - this.deferredLogs.switchOverAll(); this.bootstrapRegistry.applicationContextPrepared(event.getApplicationContext()); + finish(); } private void onApplicationFailedEvent(ApplicationFailedEvent event) { + finish(); + } + + private void finish() { + this.bootstrapRegistry.clear(); this.deferredLogs.switchOverAll(); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorsTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorsTests.java index 24db7cd2d66..5242572bf15 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorsTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentContributorsTests.java @@ -35,6 +35,8 @@ import org.springframework.boot.context.config.ConfigDataEnvironmentContributor. import org.springframework.boot.context.config.ConfigDataEnvironmentContributors.BinderOption; import org.springframework.boot.context.properties.bind.BindException; import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.boot.env.BootstrapRegistry; +import org.springframework.boot.env.DefaultBootstrapRegisty; import org.springframework.boot.logging.DeferredLogFactory; import org.springframework.mock.env.MockEnvironment; import org.springframework.mock.env.MockPropertySource; @@ -58,6 +60,8 @@ class ConfigDataEnvironmentContributorsTests { private DeferredLogFactory logFactory = Supplier::get; + private BootstrapRegistry bootstrapRegistry = new DefaultBootstrapRegisty(); + private MockEnvironment environment; private Binder binder; @@ -69,6 +73,9 @@ class ConfigDataEnvironmentContributorsTests { @Captor private ArgumentCaptor locationResolverContext; + @Captor + private ArgumentCaptor loaderContext; + @BeforeEach void setup() { this.environment = new MockEnvironment(); @@ -83,7 +90,7 @@ class ConfigDataEnvironmentContributorsTests { void createCreatesWithInitialContributors() { ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("test"); ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, - Arrays.asList(contributor)); + this.bootstrapRegistry, Arrays.asList(contributor)); Iterator iterator = contributors.iterator(); assertThat(iterator.next()).isSameAs(contributor); assertThat(iterator.next().getKind()).isEqualTo(Kind.ROOT); @@ -94,7 +101,7 @@ class ConfigDataEnvironmentContributorsTests { ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor .ofExisting(new MockPropertySource()); ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, - Arrays.asList(contributor)); + this.bootstrapRegistry, Arrays.asList(contributor)); ConfigDataEnvironmentContributors withProcessedImports = contributors.withProcessedImports(this.importer, this.activationContext); assertThat(withProcessedImports).isSameAs(contributors); @@ -107,10 +114,11 @@ class ConfigDataEnvironmentContributorsTests { MockPropertySource propertySource = new MockPropertySource(); Map imported = new LinkedHashMap<>(); imported.put(new TestConfigDataLocation("a"), new ConfigData(Arrays.asList(propertySource))); - given(this.importer.resolveAndLoad(eq(this.activationContext), any(), eq(locations))).willReturn(imported); + given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(locations))) + .willReturn(imported); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("testimport"); ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, - Arrays.asList(contributor)); + this.bootstrapRegistry, Arrays.asList(contributor)); ConfigDataEnvironmentContributors withProcessedImports = contributors.withProcessedImports(this.importer, this.activationContext); Iterator iterator = withProcessedImports.iterator(); @@ -128,18 +136,18 @@ class ConfigDataEnvironmentContributorsTests { initialPropertySource.setProperty("spring.config.import", "secondimport"); Map initialImported = new LinkedHashMap<>(); initialImported.put(new TestConfigDataLocation("a"), new ConfigData(Arrays.asList(initialPropertySource))); - given(this.importer.resolveAndLoad(eq(this.activationContext), any(), eq(initialLocations))) + given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(initialLocations))) .willReturn(initialImported); List secondLocations = Arrays.asList("secondimport"); MockPropertySource secondPropertySource = new MockPropertySource(); Map secondImported = new LinkedHashMap<>(); secondImported.put(new TestConfigDataLocation("b"), new ConfigData(Arrays.asList(secondPropertySource))); - given(this.importer.resolveAndLoad(eq(this.activationContext), any(), eq(secondLocations))) + given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(secondLocations))) .willReturn(secondImported); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor .ofInitialImport("initialimport"); ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, - Arrays.asList(contributor)); + this.bootstrapRegistry, Arrays.asList(contributor)); ConfigDataEnvironmentContributors withProcessedImports = contributors.withProcessedImports(this.importer, this.activationContext); Iterator iterator = withProcessedImports.iterator(); @@ -161,12 +169,13 @@ class ConfigDataEnvironmentContributorsTests { MockPropertySource propertySource = new MockPropertySource(); Map imported = new LinkedHashMap<>(); imported.put(new TestConfigDataLocation("a'"), new ConfigData(Arrays.asList(propertySource))); - given(this.importer.resolveAndLoad(eq(this.activationContext), any(), eq(locations))).willReturn(imported); + given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(locations))) + .willReturn(imported); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("testimport"); ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, - Arrays.asList(existingContributor, contributor)); + this.bootstrapRegistry, Arrays.asList(existingContributor, contributor)); contributors.withProcessedImports(this.importer, this.activationContext); - verify(this.importer).resolveAndLoad(any(), this.locationResolverContext.capture(), any()); + verify(this.importer).resolveAndLoad(any(), this.locationResolverContext.capture(), any(), any()); ConfigDataLocationResolverContext context = this.locationResolverContext.getValue(); assertThat(context.getBinder().bind("test", String.class).get()).isEqualTo("springboot"); } @@ -179,31 +188,75 @@ class ConfigDataEnvironmentContributorsTests { initialPropertySource.setProperty("spring.config.import", "secondimport"); Map initialImported = new LinkedHashMap<>(); initialImported.put(new TestConfigDataLocation("a"), new ConfigData(Arrays.asList(initialPropertySource))); - given(this.importer.resolveAndLoad(eq(this.activationContext), any(), eq(initialLocations))) + given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(initialLocations))) .willReturn(initialImported); List secondLocations = Arrays.asList("secondimport"); MockPropertySource secondPropertySource = new MockPropertySource(); Map secondImported = new LinkedHashMap<>(); secondImported.put(new TestConfigDataLocation("b"), new ConfigData(Arrays.asList(secondPropertySource))); - given(this.importer.resolveAndLoad(eq(this.activationContext), any(), eq(secondLocations))) + given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(secondLocations))) .willReturn(secondImported); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor .ofInitialImport("initialimport"); ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, - Arrays.asList(contributor)); + this.bootstrapRegistry, Arrays.asList(contributor)); contributors.withProcessedImports(this.importer, this.activationContext); - verify(this.importer).resolveAndLoad(any(), this.locationResolverContext.capture(), eq(secondLocations)); + verify(this.importer).resolveAndLoad(any(), this.locationResolverContext.capture(), any(), eq(secondLocations)); ConfigDataLocationResolverContext context = this.locationResolverContext.getValue(); assertThat(context.getParent()).hasToString("a"); } + @Test + void withProcessedImportsProvidesLocationResolverContextWithAccessToBootstrapRegistry() { + MockPropertySource existingPropertySource = new MockPropertySource(); + existingPropertySource.setProperty("test", "springboot"); + ConfigDataEnvironmentContributor existingContributor = ConfigDataEnvironmentContributor + .ofExisting(existingPropertySource); + this.importer = mock(ConfigDataImporter.class); + List locations = Arrays.asList("testimport"); + MockPropertySource propertySource = new MockPropertySource(); + Map imported = new LinkedHashMap<>(); + imported.put(new TestConfigDataLocation("a'"), new ConfigData(Arrays.asList(propertySource))); + given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(locations))) + .willReturn(imported); + ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("testimport"); + ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, + this.bootstrapRegistry, Arrays.asList(existingContributor, contributor)); + contributors.withProcessedImports(this.importer, this.activationContext); + verify(this.importer).resolveAndLoad(any(), this.locationResolverContext.capture(), any(), any()); + ConfigDataLocationResolverContext context = this.locationResolverContext.getValue(); + assertThat(context.getBootstrapRegistry()).isSameAs(this.bootstrapRegistry); + } + + @Test + void withProcessedImportsProvidesLoaderContextWithAccessToBootstrapRegistry() { + MockPropertySource existingPropertySource = new MockPropertySource(); + existingPropertySource.setProperty("test", "springboot"); + ConfigDataEnvironmentContributor existingContributor = ConfigDataEnvironmentContributor + .ofExisting(existingPropertySource); + this.importer = mock(ConfigDataImporter.class); + List locations = Arrays.asList("testimport"); + MockPropertySource propertySource = new MockPropertySource(); + Map imported = new LinkedHashMap<>(); + imported.put(new TestConfigDataLocation("a'"), new ConfigData(Arrays.asList(propertySource))); + given(this.importer.resolveAndLoad(eq(this.activationContext), any(), any(), eq(locations))) + .willReturn(imported); + ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofInitialImport("testimport"); + ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, + this.bootstrapRegistry, Arrays.asList(existingContributor, contributor)); + contributors.withProcessedImports(this.importer, this.activationContext); + verify(this.importer).resolveAndLoad(any(), any(), this.loaderContext.capture(), any()); + ConfigDataLoaderContext context = this.loaderContext.getValue(); + assertThat(context.getBootstrapRegistry()).isSameAs(this.bootstrapRegistry); + } + @Test void getBinderProvidesBinder() { MockPropertySource propertySource = new MockPropertySource(); propertySource.setProperty("test", "springboot"); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofExisting(propertySource); ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, - Arrays.asList(contributor)); + this.bootstrapRegistry, Arrays.asList(contributor)); Binder binder = contributors.getBinder(this.activationContext); assertThat(binder.bind("test", String.class).get()).isEqualTo("springboot"); } @@ -220,7 +273,7 @@ class ConfigDataEnvironmentContributorsTests { ConfigDataEnvironmentContributor secondContributor = ConfigDataEnvironmentContributor.ofImported(null, configData, 1, this.activationContext); ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, - Arrays.asList(firstContributor, secondContributor)); + this.bootstrapRegistry, Arrays.asList(firstContributor, secondContributor)); Binder binder = contributors.getBinder(this.activationContext); assertThat(binder.bind("test", String.class).get()).isEqualTo("one"); } @@ -238,7 +291,7 @@ class ConfigDataEnvironmentContributorsTests { ConfigDataEnvironmentContributor secondContributor = ConfigDataEnvironmentContributor.ofImported(null, configData, 1, this.activationContext); ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, - Arrays.asList(firstContributor, secondContributor)); + this.bootstrapRegistry, Arrays.asList(firstContributor, secondContributor)); Binder binder = contributors.getBinder(this.activationContext); assertThat(binder.bind("test", String.class).get()).isEqualTo("two"); } @@ -250,7 +303,7 @@ class ConfigDataEnvironmentContributorsTests { propertySource.setProperty("other", "springboot"); ConfigDataEnvironmentContributor contributor = ConfigDataEnvironmentContributor.ofExisting(propertySource); ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, - Arrays.asList(contributor)); + this.bootstrapRegistry, Arrays.asList(contributor)); Binder binder = contributors.getBinder(this.activationContext); assertThat(binder.bind("test", String.class).get()).isEqualTo("springboot"); } @@ -269,7 +322,7 @@ class ConfigDataEnvironmentContributorsTests { ConfigDataEnvironmentContributor secondContributor = ConfigDataEnvironmentContributor.ofImported(null, configData, 1, this.activationContext); ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, - Arrays.asList(firstContributor, secondContributor)); + this.bootstrapRegistry, Arrays.asList(firstContributor, secondContributor)); Binder binder = contributors.getBinder(this.activationContext); assertThat(binder.bind("test", String.class).get()).isEqualTo("two"); } @@ -287,7 +340,7 @@ class ConfigDataEnvironmentContributorsTests { ConfigDataEnvironmentContributor secondContributor = ConfigDataEnvironmentContributor.ofImported(null, configData, 1, this.activationContext); ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, - Arrays.asList(firstContributor, secondContributor)); + this.bootstrapRegistry, Arrays.asList(firstContributor, secondContributor)); Binder binder = contributors.getBinder(this.activationContext, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE); assertThatExceptionOfType(BindException.class).isThrownBy(() -> binder.bind("test", String.class)) .satisfies((ex) -> assertThat(ex.getCause()).isInstanceOf(InactiveConfigDataAccessException.class)); @@ -306,7 +359,7 @@ class ConfigDataEnvironmentContributorsTests { ConfigDataEnvironmentContributor secondContributor = ConfigDataEnvironmentContributor.ofImported(null, configData, 1, this.activationContext); ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, - Arrays.asList(firstContributor, secondContributor)); + this.bootstrapRegistry, Arrays.asList(firstContributor, secondContributor)); Binder binder = contributors.getBinder(this.activationContext, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE); assertThatExceptionOfType(BindException.class).isThrownBy(() -> binder.bind("test", String.class)) .satisfies((ex) -> assertThat(ex.getCause()).isInstanceOf(InactiveConfigDataAccessException.class)); @@ -326,7 +379,7 @@ class ConfigDataEnvironmentContributorsTests { ConfigDataEnvironmentContributor secondContributor = ConfigDataEnvironmentContributor.ofImported(null, configData, 1, this.activationContext); ConfigDataEnvironmentContributors contributors = new ConfigDataEnvironmentContributors(this.logFactory, - Arrays.asList(firstContributor, secondContributor)); + this.bootstrapRegistry, Arrays.asList(firstContributor, secondContributor)); Binder binder = contributors.getBinder(this.activationContext, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE); assertThatExceptionOfType(BindException.class).isThrownBy(() -> binder.bind("test", String.class)) .satisfies((ex) -> assertThat(ex.getCause()).isInstanceOf(InactiveConfigDataAccessException.class)); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorBootstrapRegistryIntegrationTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorBootstrapRegistryIntegrationTests.java new file mode 100644 index 00000000000..9c0b375a963 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorBootstrapRegistryIntegrationTests.java @@ -0,0 +1,62 @@ +/* + * 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.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.context.config.TestConfigDataBootstrap.LoaderHelper; +import org.springframework.boot.env.BootstrapRegistry; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.annotation.Configuration; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link ConfigDataEnvironmentPostProcessor} when used with a + * {@link BootstrapRegistry}. + * + * @author Phillip Webb + */ +class ConfigDataEnvironmentPostProcessorBootstrapRegistryIntegrationTests { + + private SpringApplication application; + + @BeforeEach + void setup() { + this.application = new SpringApplication(Config.class); + this.application.setWebApplicationType(WebApplicationType.NONE); + } + + @Test + void bootstrapsApplicationContext() { + try (ConfigurableApplicationContext context = this.application + .run("--spring.config.import=classpath:application-bootstrap-registry-integration-tests.properties")) { + LoaderHelper bean = context.getBean(TestConfigDataBootstrap.LoaderHelper.class); + assertThat(bean).isNotNull(); + assertThat(bean.getLocation().getResolverHelper().getLocation()).isEqualTo("testbootstrap:test"); + } + } + + @Configuration + static class Config { + + } + +} diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorTests.java index 7ce9857b944..01d0278059e 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentPostProcessorTests.java @@ -28,6 +28,7 @@ import org.mockito.Spy; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.boot.SpringApplication; +import org.springframework.boot.env.DefaultBootstrapRegisty; import org.springframework.core.env.StandardEnvironment; import org.springframework.core.io.DefaultResourceLoader; import org.springframework.core.io.ResourceLoader; @@ -58,7 +59,8 @@ class ConfigDataEnvironmentPostProcessorTests { private ConfigDataEnvironment configDataEnvironment; @Spy - private ConfigDataEnvironmentPostProcessor postProcessor = new ConfigDataEnvironmentPostProcessor(Supplier::get); + private ConfigDataEnvironmentPostProcessor postProcessor = new ConfigDataEnvironmentPostProcessor(Supplier::get, + new DefaultBootstrapRegisty()); @Captor private ArgumentCaptor> additionalProfilesCaptor; @@ -117,7 +119,7 @@ class ConfigDataEnvironmentPostProcessorTests { @Test void applyToAppliesPostProcessing() { int before = this.environment.getPropertySources().size(); - ConfigDataEnvironmentPostProcessor.applyTo(this.environment, null, "dev"); + ConfigDataEnvironmentPostProcessor.applyTo(this.environment, null, null, "dev"); assertThat(this.environment.getPropertySources().size()).isGreaterThan(before); assertThat(this.environment.getActiveProfiles()).containsExactly("dev"); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java index a92fde5ed31..c70ab5431cf 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataEnvironmentTests.java @@ -29,6 +29,8 @@ import org.junit.jupiter.api.TestInfo; import org.springframework.boot.context.config.ConfigDataEnvironmentContributor.ImportPhase; import org.springframework.boot.context.config.ConfigDataEnvironmentContributor.Kind; import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.boot.env.BootstrapRegistry; +import org.springframework.boot.env.DefaultBootstrapRegisty; import org.springframework.boot.logging.DeferredLogFactory; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.PropertySource; @@ -50,6 +52,8 @@ class ConfigDataEnvironmentTests { private DeferredLogFactory logFactory = Supplier::get; + private BootstrapRegistry bootstrapRegistry = new DefaultBootstrapRegisty(); + private MockEnvironment environment = new MockEnvironment(); private ResourceLoader resourceLoader = new DefaultResourceLoader(); @@ -60,15 +64,15 @@ class ConfigDataEnvironmentTests { void createWhenUseLegacyPropertyInEnvironmentThrowsException() { this.environment.setProperty("spring.config.use-legacy-processing", "true"); assertThatExceptionOfType(UseLegacyConfigProcessingException.class) - .isThrownBy(() -> new ConfigDataEnvironment(this.logFactory, this.environment, this.resourceLoader, - this.additionalProfiles)); + .isThrownBy(() -> new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry, this.environment, + this.resourceLoader, this.additionalProfiles)); } @Test void createExposesEnvironmentBinderToConfigDataLocationResolvers() { this.environment.setProperty("spring", "boot"); TestConfigDataEnvironment configDataEnvironment = new TestConfigDataEnvironment(this.logFactory, - this.environment, this.resourceLoader, this.additionalProfiles); + this.bootstrapRegistry, this.environment, this.resourceLoader, this.additionalProfiles); assertThat(configDataEnvironment.getConfigDataLocationResolversBinder().bind("spring", String.class).get()) .isEqualTo("boot"); } @@ -81,8 +85,8 @@ class ConfigDataEnvironmentTests { this.environment.getPropertySources().addLast(propertySource1); this.environment.getPropertySources().addLast(propertySource2); this.environment.getPropertySources().addLast(propertySource3); - ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.environment, - this.resourceLoader, this.additionalProfiles); + ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry, + this.environment, this.resourceLoader, this.additionalProfiles); List children = configDataEnvironment.getContributors().getRoot() .getChildren(ImportPhase.BEFORE_PROFILE_ACTIVATION); Object[] wrapped = children.stream().filter((child) -> child.getKind() == Kind.EXISTING) @@ -100,8 +104,8 @@ class ConfigDataEnvironmentTests { this.environment.getPropertySources().addLast(defaultPropertySource); this.environment.getPropertySources().addLast(propertySource1); this.environment.getPropertySources().addLast(propertySource2); - ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.environment, - this.resourceLoader, this.additionalProfiles); + ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry, + this.environment, this.resourceLoader, this.additionalProfiles); List children = configDataEnvironment.getContributors().getRoot() .getChildren(ImportPhase.BEFORE_PROFILE_ACTIVATION); Object[] wrapped = children.stream().filter((child) -> child.getKind() == Kind.EXISTING) @@ -116,8 +120,8 @@ class ConfigDataEnvironmentTests { this.environment.setProperty("spring.config.location", "l1,l2"); this.environment.setProperty("spring.config.additional-location", "a1,a2"); this.environment.setProperty("spring.config.import", "i1,i2"); - ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.environment, - this.resourceLoader, this.additionalProfiles); + ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry, + this.environment, this.resourceLoader, this.additionalProfiles); List children = configDataEnvironment.getContributors().getRoot() .getChildren(ImportPhase.BEFORE_PROFILE_ACTIVATION); Object[] imports = children.stream().filter((child) -> child.getKind() == Kind.INITIAL_IMPORT) @@ -128,8 +132,8 @@ class ConfigDataEnvironmentTests { @Test void processAndApplyAddsImportedSourceToEnvironment(TestInfo info) { this.environment.setProperty("spring.config.location", getConfigLocation(info)); - ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.environment, - this.resourceLoader, this.additionalProfiles); + ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry, + this.environment, this.resourceLoader, this.additionalProfiles); configDataEnvironment.processAndApply(); assertThat(this.environment.getProperty("spring")).isEqualTo("boot"); } @@ -137,8 +141,8 @@ class ConfigDataEnvironmentTests { @Test void processAndApplyOnlyAddsActiveContributors(TestInfo info) { this.environment.setProperty("spring.config.location", getConfigLocation(info)); - ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.environment, - this.resourceLoader, this.additionalProfiles); + ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry, + this.environment, this.resourceLoader, this.additionalProfiles); configDataEnvironment.processAndApply(); assertThat(this.environment.getProperty("spring")).isEqualTo("boot"); assertThat(this.environment.getProperty("other")).isNull(); @@ -149,8 +153,8 @@ class ConfigDataEnvironmentTests { MockPropertySource defaultPropertySource = new MockPropertySource("defaultProperties"); this.environment.getPropertySources().addFirst(defaultPropertySource); this.environment.setProperty("spring.config.location", getConfigLocation(info)); - ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.environment, - this.resourceLoader, this.additionalProfiles); + ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry, + this.environment, this.resourceLoader, this.additionalProfiles); configDataEnvironment.processAndApply(); List> sources = this.environment.getPropertySources().stream().collect(Collectors.toList()); assertThat(sources.get(sources.size() - 1)).isSameAs(defaultPropertySource); @@ -159,8 +163,8 @@ class ConfigDataEnvironmentTests { @Test void processAndApplySetsDefaultProfiles(TestInfo info) { this.environment.setProperty("spring.config.location", getConfigLocation(info)); - ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.environment, - this.resourceLoader, this.additionalProfiles); + ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry, + this.environment, this.resourceLoader, this.additionalProfiles); configDataEnvironment.processAndApply(); assertThat(this.environment.getDefaultProfiles()).containsExactly("one", "two", "three"); } @@ -168,8 +172,8 @@ class ConfigDataEnvironmentTests { @Test void processAndApplySetsActiveProfiles(TestInfo info) { this.environment.setProperty("spring.config.location", getConfigLocation(info)); - ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.environment, - this.resourceLoader, this.additionalProfiles); + ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry, + this.environment, this.resourceLoader, this.additionalProfiles); configDataEnvironment.processAndApply(); assertThat(this.environment.getActiveProfiles()).containsExactly("one", "two", "three"); } @@ -177,8 +181,8 @@ class ConfigDataEnvironmentTests { @Test void processAndApplySetsActiveProfilesAndProfileGroups(TestInfo info) { this.environment.setProperty("spring.config.location", getConfigLocation(info)); - ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.environment, - this.resourceLoader, this.additionalProfiles); + ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry, + this.environment, this.resourceLoader, this.additionalProfiles); configDataEnvironment.processAndApply(); assertThat(this.environment.getActiveProfiles()).containsExactly("one", "four", "five", "two", "three"); } @@ -187,8 +191,8 @@ class ConfigDataEnvironmentTests { @Disabled("Disabled until spring.profiles suppport is dropped") void processAndApplyWhenHasInvalidPropertyThrowsException() { this.environment.setProperty("spring.profile", "a"); - ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.environment, - this.resourceLoader, this.additionalProfiles); + ConfigDataEnvironment configDataEnvironment = new ConfigDataEnvironment(this.logFactory, this.bootstrapRegistry, + this.environment, this.resourceLoader, this.additionalProfiles); assertThatExceptionOfType(InvalidConfigDataPropertyException.class) .isThrownBy(() -> configDataEnvironment.processAndApply()); } @@ -202,9 +206,10 @@ class ConfigDataEnvironmentTests { private Binder configDataLocationResolversBinder; - TestConfigDataEnvironment(DeferredLogFactory logFactory, ConfigurableEnvironment environment, - ResourceLoader resourceLoader, Collection additionalProfiles) { - super(logFactory, environment, resourceLoader, additionalProfiles); + TestConfigDataEnvironment(DeferredLogFactory logFactory, BootstrapRegistry bootstrapRegistry, + ConfigurableEnvironment environment, ResourceLoader resourceLoader, + Collection additionalProfiles) { + super(logFactory, bootstrapRegistry, environment, resourceLoader, additionalProfiles); } @Override diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataImporterTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataImporterTests.java index 9bf8b1d7bfa..a86fe987b81 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataImporterTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataImporterTests.java @@ -54,6 +54,9 @@ class ConfigDataImporterTests { @Mock private ConfigDataLocationResolverContext locationResolverContext; + @Mock + private ConfigDataLoaderContext loaderContext; + @Mock private ConfigDataActivationContext activationContext; @@ -75,11 +78,12 @@ class ConfigDataImporterTests { ConfigData configData2 = new ConfigData(Collections.singleton(new MockPropertySource())); given(this.resolvers.resolveAll(this.locationResolverContext, locations, this.profiles)) .willReturn(resolvedLocations); - given(this.loaders.load(resolvedLocation1)).willReturn(configData1); - given(this.loaders.load(resolvedLocation2)).willReturn(configData2); + given(this.loaders.load(this.loaderContext, resolvedLocation1)).willReturn(configData1); + given(this.loaders.load(this.loaderContext, resolvedLocation2)).willReturn(configData2); ConfigDataImporter importer = new ConfigDataImporter(this.resolvers, this.loaders); Collection loaded = importer - .resolveAndLoad(this.activationContext, this.locationResolverContext, locations).values(); + .resolveAndLoad(this.activationContext, this.locationResolverContext, this.loaderContext, locations) + .values(); assertThat(loaded).containsExactly(configData2, configData1); } @@ -99,14 +103,14 @@ class ConfigDataImporterTests { .willReturn(resolvedLocations1and2); given(this.resolvers.resolveAll(this.locationResolverContext, locations2and3, this.profiles)) .willReturn(resolvedLocations2and3); - given(this.loaders.load(resolvedLocation1)).willReturn(configData1); - given(this.loaders.load(resolvedLocation2)).willReturn(configData2); - given(this.loaders.load(resolvedLocation3)).willReturn(configData3); + 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); - Collection loaded1and2 = importer - .resolveAndLoad(this.activationContext, this.locationResolverContext, locations1and2).values(); - Collection loaded2and3 = importer - .resolveAndLoad(this.activationContext, this.locationResolverContext, locations2and3).values(); + Collection loaded1and2 = importer.resolveAndLoad(this.activationContext, + this.locationResolverContext, this.loaderContext, locations1and2).values(); + Collection loaded2and3 = importer.resolveAndLoad(this.activationContext, + this.locationResolverContext, this.loaderContext, locations2and3).values(); assertThat(loaded1and2).containsExactly(configData2, configData1); assertThat(loaded2and3).containsExactly(configData3); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoaderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoaderTests.java index b41793831e9..87aae67504e 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoaderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoaderTests.java @@ -21,6 +21,7 @@ import java.io.IOException; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; /** * Tests for {@link ConfigDataLoader}. @@ -32,15 +33,17 @@ class ConfigDataLoaderTests { private TestConfigDataLoader loader = new TestConfigDataLoader(); + private ConfigDataLoaderContext context = mock(ConfigDataLoaderContext.class); + @Test void isLoadableAlwaysReturnsTrue() { - assertThat(this.loader.isLoadable(new TestConfigDataLocation())).isTrue(); + assertThat(this.loader.isLoadable(this.context, new TestConfigDataLocation())).isTrue(); } static class TestConfigDataLoader implements ConfigDataLoader { @Override - public ConfigData load(TestConfigDataLocation location) throws IOException { + public ConfigData load(ConfigDataLoaderContext context, TestConfigDataLocation location) throws IOException { return null; } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoadersTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoadersTests.java index 4de530d9856..d8311df8a38 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoadersTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLoadersTests.java @@ -30,6 +30,7 @@ import org.springframework.mock.env.MockPropertySource; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.mockito.Mockito.mock; /** * Tests for {@link ConfigDataLoaders}. @@ -41,6 +42,8 @@ class ConfigDataLoadersTests { private DeferredLogFactory logFactory = Supplier::get; + private ConfigDataLoaderContext context = mock(ConfigDataLoaderContext.class); + @Test void createWhenLoaderHasLogParameterInjectsLog() { new ConfigDataLoaders(this.logFactory, Arrays.asList(LoggingConfigDataLoader.class.getName())); @@ -51,7 +54,7 @@ class ConfigDataLoadersTests { TestConfigDataLocation location = new TestConfigDataLocation("test"); ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, Arrays.asList(TestConfigDataLoader.class.getName())); - ConfigData loaded = loaders.load(location); + ConfigData loaded = loaders.load(this.context, location); assertThat(getLoader(loaded)).isInstanceOf(TestConfigDataLoader.class); } @@ -60,7 +63,7 @@ class ConfigDataLoadersTests { TestConfigDataLocation location = new TestConfigDataLocation("test"); ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, Arrays.asList(LoggingConfigDataLoader.class.getName(), TestConfigDataLoader.class.getName())); - assertThatIllegalStateException().isThrownBy(() -> loaders.load(location)) + assertThatIllegalStateException().isThrownBy(() -> loaders.load(this.context, location)) .withMessageContaining("Multiple loaders found for location test"); } @@ -69,7 +72,7 @@ class ConfigDataLoadersTests { TestConfigDataLocation location = new TestConfigDataLocation("test"); ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, Arrays.asList(NonLoadableConfigDataLoader.class.getName())); - assertThatIllegalStateException().isThrownBy(() -> loaders.load(location)) + assertThatIllegalStateException().isThrownBy(() -> loaders.load(this.context, location)) .withMessage("No loader found for location 'test'"); } @@ -78,7 +81,7 @@ class ConfigDataLoadersTests { TestConfigDataLocation location = new TestConfigDataLocation("test"); ConfigDataLoaders loaders = new ConfigDataLoaders(this.logFactory, Arrays.asList(OtherConfigDataLoader.class.getName(), SpecificConfigDataLoader.class.getName())); - ConfigData loaded = loaders.load(location); + ConfigData loaded = loaders.load(this.context, location); assertThat(getLoader(loaded)).isInstanceOf(SpecificConfigDataLoader.class); } @@ -121,7 +124,7 @@ class ConfigDataLoadersTests { } @Override - public ConfigData load(ConfigDataLocation location) throws IOException { + public ConfigData load(ConfigDataLoaderContext context, ConfigDataLocation location) throws IOException { throw new AssertionError("Unexpected call"); } @@ -130,7 +133,7 @@ class ConfigDataLoadersTests { static class TestConfigDataLoader implements ConfigDataLoader { @Override - public ConfigData load(ConfigDataLocation location) throws IOException { + public ConfigData load(ConfigDataLoaderContext context, ConfigDataLocation location) throws IOException { return createConfigData(this, location); } @@ -139,7 +142,7 @@ class ConfigDataLoadersTests { static class NonLoadableConfigDataLoader extends TestConfigDataLoader { @Override - public boolean isLoadable(ConfigDataLocation location) { + public boolean isLoadable(ConfigDataLoaderContext context, ConfigDataLocation location) { return false; } @@ -148,7 +151,7 @@ class ConfigDataLoadersTests { static class SpecificConfigDataLoader implements ConfigDataLoader { @Override - public ConfigData load(TestConfigDataLocation location) throws IOException { + public ConfigData load(ConfigDataLoaderContext context, TestConfigDataLocation location) throws IOException { return createConfigData(this, location); } @@ -157,7 +160,7 @@ class ConfigDataLoadersTests { static class OtherConfigDataLoader implements ConfigDataLoader { @Override - public ConfigData load(OtherConfigDataLocation location) throws IOException { + public ConfigData load(ConfigDataLoaderContext context, OtherConfigDataLocation location) throws IOException { return createConfigData(this, location); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLocationResolverTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLocationResolverTests.java index 535cb46f525..d675960d73c 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLocationResolverTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigDataLocationResolverTests.java @@ -31,9 +31,9 @@ import static org.mockito.Mockito.mock; */ class ConfigDataLocationResolverTests { - ConfigDataLocationResolver resolver = new TestConfigDataLocationResolver(); + private ConfigDataLocationResolver resolver = new TestConfigDataLocationResolver(); - ConfigDataLocationResolverContext context = mock(ConfigDataLocationResolverContext.class); + private ConfigDataLocationResolverContext context = mock(ConfigDataLocationResolverContext.class); @Test void resolveProfileSpecificReturnsEmptyList() { diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigTreeConfigDataLoaderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigTreeConfigDataLoaderTests.java index 45fb1286b1d..1999795a278 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigTreeConfigDataLoaderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ConfigTreeConfigDataLoaderTests.java @@ -28,6 +28,7 @@ import org.springframework.core.env.PropertySource; import org.springframework.util.FileCopyUtils; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; /** * Tests for {@link ConfigTreeConfigDataLoader}. @@ -39,6 +40,8 @@ public class ConfigTreeConfigDataLoaderTests { private ConfigTreeConfigDataLoader loader = new ConfigTreeConfigDataLoader(); + private ConfigDataLoaderContext loaderContext = mock(ConfigDataLoaderContext.class); + @TempDir Path directory; @@ -48,7 +51,7 @@ public class ConfigTreeConfigDataLoaderTests { file.getParentFile().mkdirs(); FileCopyUtils.copy("world".getBytes(StandardCharsets.UTF_8), file); ConfigTreeConfigDataLocation location = new ConfigTreeConfigDataLocation(this.directory.toString()); - ConfigData configData = this.loader.load(location); + ConfigData configData = this.loader.load(this.loaderContext, location); assertThat(configData.getPropertySources().size()).isEqualTo(1); PropertySource source = configData.getPropertySources().get(0); assertThat(source.getName()).isEqualTo("Config tree '" + this.directory.toString() + "'"); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ResourceConfigDataLoaderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ResourceConfigDataLoaderTests.java index 01d4d2cf870..6d254e446b3 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ResourceConfigDataLoaderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/ResourceConfigDataLoaderTests.java @@ -26,6 +26,7 @@ import org.springframework.core.env.PropertySource; import org.springframework.core.io.ClassPathResource; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; /** * Tests for {@link ResourceConfigDataLoader}. @@ -37,11 +38,13 @@ public class ResourceConfigDataLoaderTests { private ResourceConfigDataLoader loader = new ResourceConfigDataLoader(); + private ConfigDataLoaderContext loaderContext = mock(ConfigDataLoaderContext.class); + @Test void loadWhenLocationResultsInMultiplePropertySourcesAddsAllToConfigData() throws IOException { ResourceConfigDataLocation location = new ResourceConfigDataLocation("application.yml", new ClassPathResource("configdata/yaml/application.yml"), new YamlPropertySourceLoader()); - ConfigData configData = this.loader.load(location); + 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); @@ -56,7 +59,7 @@ public class ResourceConfigDataLoaderTests { ResourceConfigDataLocation location = new ResourceConfigDataLocation("testproperties.properties", new ClassPathResource("config/0-empty/testproperties.properties"), new PropertiesPropertySourceLoader()); - ConfigData configData = this.loader.load(location); + ConfigData configData = this.loader.load(this.loaderContext, location); assertThat(configData.getPropertySources().size()).isEqualTo(0); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/TestConfigDataBootstrap.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/TestConfigDataBootstrap.java new file mode 100644 index 00000000000..726ffb5b055 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/context/config/TestConfigDataBootstrap.java @@ -0,0 +1,115 @@ +/* + * 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.Collections; +import java.util.List; + +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.MapPropertySource; + +/** + * Test classes used with + * {@link ConfigDataEnvironmentPostProcessorBootstrapRegistryIntegrationTests} to show how + * a bootstrap registry can be used. This example will create helper instances during + * result and load. It also shows how the helper can ultimately be registered as a bean. + * + * @author Phillip Webb + */ +class TestConfigDataBootstrap { + + static class LocationResolver implements ConfigDataLocationResolver { + + @Override + public boolean isResolvable(ConfigDataLocationResolverContext context, String location) { + return location.startsWith("testbootstrap:"); + } + + @Override + public List resolve(ConfigDataLocationResolverContext context, String location) { + ResolverHelper helper = context.getBootstrapRegistry().get(ResolverHelper.class, + () -> new ResolverHelper(location)); + return Collections.singletonList(new Location(helper)); + } + + } + + static class Loader implements ConfigDataLoader { + + @Override + public ConfigData load(ConfigDataLoaderContext context, Location location) throws IOException { + context.getBootstrapRegistry().get(LoaderHelper.class, () -> new LoaderHelper(location), + LoaderHelper::addToContext); + return new ConfigData( + Collections.singleton(new MapPropertySource("loaded", Collections.singletonMap("test", "test")))); + } + + } + + static class Location extends ConfigDataLocation { + + private final ResolverHelper resolverHelper; + + Location(ResolverHelper resolverHelper) { + this.resolverHelper = resolverHelper; + } + + @Override + public String toString() { + return "test"; + } + + ResolverHelper getResolverHelper() { + return this.resolverHelper; + } + + } + + static class ResolverHelper { + + private final String location; + + ResolverHelper(String location) { + this.location = location; + } + + String getLocation() { + return this.location; + } + + } + + static class LoaderHelper { + + private final Location location; + + LoaderHelper(Location location) { + this.location = location; + } + + Location getLocation() { + return this.location; + } + + static void addToContext(ConfigurableApplicationContext context, LoaderHelper loaderHelper) { + context.getBeanFactory().registerSingleton("loaderHelper", loaderHelper); + } + + } + +} diff --git a/spring-boot-project/spring-boot/src/test/resources/META-INF/spring.factories b/spring-boot-project/spring-boot/src/test/resources/META-INF/spring.factories index 7be3145156b..35e21e5ae5a 100644 --- a/spring-boot-project/spring-boot/src/test/resources/META-INF/spring.factories +++ b/spring-boot-project/spring-boot/src/test/resources/META-INF/spring.factories @@ -1,3 +1,9 @@ org.springframework.boot.env.PropertySourceLoader=\ org.springframework.boot.context.config.TestPropertySourceLoader1,\ org.springframework.boot.context.config.TestPropertySourceLoader2 + +org.springframework.boot.context.config.ConfigDataLocationResolver=\ +org.springframework.boot.context.config.TestConfigDataBootstrap.LocationResolver + +org.springframework.boot.context.config.ConfigDataLoader=\ +org.springframework.boot.context.config.TestConfigDataBootstrap.Loader \ No newline at end of file diff --git a/spring-boot-project/spring-boot/src/test/resources/application-bootstrap-registry-integration-tests.properties b/spring-boot-project/spring-boot/src/test/resources/application-bootstrap-registry-integration-tests.properties new file mode 100644 index 00000000000..5a1fdceb704 --- /dev/null +++ b/spring-boot-project/spring-boot/src/test/resources/application-bootstrap-registry-integration-tests.properties @@ -0,0 +1 @@ +spring.config.import=testbootstrap:test \ No newline at end of file