Skip to content

Commit

Permalink
Replace binder as contributors are processed
Browse files Browse the repository at this point in the history
Update `ConfigDataEnvironment` so that the bootstrap `Binder` is
replaced as contributors are processed. The final `Binder` is now
also added without the `FAIL_ON_BIND_TO_INACTIVE_SOURCE` option so
that properties can be bound at the last stage even if values exist
in an inactive profile-specific document.

Closes gh-24669
  • Loading branch information
philwebb committed Jan 6, 2021
1 parent ac4243d commit 5b126b0
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 14 deletions.
Expand Up @@ -22,7 +22,6 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;

import org.apache.commons.logging.Log;

Expand Down Expand Up @@ -107,6 +106,10 @@ class ConfigDataEnvironment {

private static final Bindable<List<String>> STRING_LIST = Bindable.listOf(String.class);

private static final BinderOption[] ALLOW_INACTIVE_BINDING = {};

private static final BinderOption[] DENY_INACTIVE_BINDING = { BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE };

private final DeferredLogFactory logFactory;

private final Log logger;
Expand Down Expand Up @@ -222,25 +225,22 @@ private ConfigDataEnvironmentContributor createInitialImportContributor(ConfigDa
void processAndApply() {
ConfigDataImporter importer = new ConfigDataImporter(this.logFactory, this.notFoundAction, this.resolvers,
this.loaders);
registerBootstrapBinder(() -> this.contributors.getBinder(null, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE));
registerBootstrapBinder(this.contributors, null, DENY_INACTIVE_BINDING);
ConfigDataEnvironmentContributors contributors = processInitial(this.contributors, importer);
Binder initialBinder = contributors.getBinder(null, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE);
registerBootstrapBinder(() -> initialBinder);
ConfigDataActivationContext activationContext = createActivationContext(initialBinder);
ConfigDataActivationContext activationContext = createActivationContext(
contributors.getBinder(null, BinderOption.FAIL_ON_BIND_TO_INACTIVE_SOURCE));
contributors = processWithoutProfiles(contributors, importer, activationContext);
activationContext = withProfiles(contributors, activationContext);
contributors = processWithProfiles(contributors, importer, activationContext);
applyToEnvironment(contributors, activationContext);
}

private void registerBootstrapBinder(Supplier<Binder> supplier) {
this.bootstrapContext.register(Binder.class, InstanceSupplier.from(supplier).withScope(Scope.PROTOTYPE));
}

private ConfigDataEnvironmentContributors processInitial(ConfigDataEnvironmentContributors contributors,
ConfigDataImporter importer) {
this.logger.trace("Processing initial config data environment contributors without activation context");
return contributors.withProcessedImports(importer, null);
contributors = contributors.withProcessedImports(importer, null);
registerBootstrapBinder(contributors, null, DENY_INACTIVE_BINDING);
return contributors;
}

private ConfigDataActivationContext createActivationContext(Binder initialBinder) {
Expand All @@ -259,7 +259,9 @@ private ConfigDataActivationContext createActivationContext(Binder initialBinder
private ConfigDataEnvironmentContributors processWithoutProfiles(ConfigDataEnvironmentContributors contributors,
ConfigDataImporter importer, ConfigDataActivationContext activationContext) {
this.logger.trace("Processing config data environment contributors with initial activation context");
return contributors.withProcessedImports(importer, activationContext);
contributors = contributors.withProcessedImports(importer, activationContext);
registerBootstrapBinder(contributors, activationContext, DENY_INACTIVE_BINDING);
return contributors;
}

private ConfigDataActivationContext withProfiles(ConfigDataEnvironmentContributors contributors,
Expand Down Expand Up @@ -304,7 +306,15 @@ private Collection<? extends String> getIncludedProfiles(ConfigDataEnvironmentCo
private ConfigDataEnvironmentContributors processWithProfiles(ConfigDataEnvironmentContributors contributors,
ConfigDataImporter importer, ConfigDataActivationContext activationContext) {
this.logger.trace("Processing config data environment contributors with profile activation context");
return contributors.withProcessedImports(importer, activationContext);
contributors = contributors.withProcessedImports(importer, activationContext);
registerBootstrapBinder(contributors, activationContext, ALLOW_INACTIVE_BINDING);
return contributors;
}

private void registerBootstrapBinder(ConfigDataEnvironmentContributors contributors,
ConfigDataActivationContext activationContext, BinderOption... binderOptions) {
this.bootstrapContext.register(Binder.class, InstanceSupplier
.from(() -> contributors.getBinder(activationContext, binderOptions)).withScope(Scope.PROTOTYPE));
}

private void applyToEnvironment(ConfigDataEnvironmentContributors contributors,
Expand Down
@@ -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.
Expand Down Expand Up @@ -51,6 +51,7 @@ void bootstrapsApplicationContext() {
LoaderHelper bean = context.getBean(TestConfigDataBootstrap.LoaderHelper.class);
assertThat(bean).isNotNull();
assertThat(bean.getBound()).isEqualTo("igotbound");
assertThat(bean.getProfileBound()).isEqualTo("igotprofilebound");
assertThat(bean.getLocation().getResolverHelper().getLocation())
.isEqualTo(ConfigDataLocation.of("testbootstrap:test"));
}
Expand Down
@@ -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.
Expand Down Expand Up @@ -121,6 +121,10 @@ String getBound() {
return this.binder.get().bind("myprop", String.class).orElse(null);
}

String getProfileBound() {
return this.binder.get().bind("myprofileprop", String.class).orElse(null);
}

@Override
public void onApplicationEvent(BootstrapContextClosedEvent event) {
event.getApplicationContext().getBeanFactory().registerSingleton("loaderHelper", this);
Expand Down
@@ -1,2 +1,6 @@
spring.config.import=testbootstrap:test
spring.profiles.active=test
myprop=igotbound
#---
spring.config.activate.on-profile=test
myprofileprop=igotprofilebound

0 comments on commit 5b126b0

Please sign in to comment.