Polish 'Allow properties migrator to fail application startup'

See gh-51855
This commit is contained in:
Phillip Webb
2026-09-25 20:24:40 -07:00
parent a86aa43303
commit 602a7e73e9
5 changed files with 29 additions and 32 deletions
@@ -106,7 +106,7 @@ public abstract class DocumentConfigurationProperties extends DefaultTask {
config.accept("spring.ssl");
config.accept("spring.task");
config.accept("spring.threads");
config.accept("spring.tools.properties-migrator");
config.accept("spring.properties");
config.accept("spring.validation");
config.accept("spring.mandatory-file-encoding");
config.accept("info");
@@ -18,7 +18,7 @@ package org.springframework.boot.context.properties.migrator;
import java.io.IOException;
import java.io.InputStream;
import java.util.Locale;
import java.util.function.Predicate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -35,6 +35,7 @@ import org.springframework.context.ApplicationListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.util.Assert;
/**
* An {@link ApplicationListener} that inspects the {@link ConfigurableEnvironment
@@ -49,7 +50,7 @@ class PropertiesMigrationListener implements ApplicationListener<SpringApplicati
private static final Log logger = LogFactory.getLog(PropertiesMigrationListener.class);
private static final String FAIL_PROPERTY = "spring.tools.properties-migrator.fail";
private static final String FAIL_PROPERTY = "spring.properties.migrator.fail";
private @Nullable PropertiesMigrationReport report;
@@ -67,10 +68,10 @@ class PropertiesMigrationListener implements ApplicationListener<SpringApplicati
private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
ConfigurableEnvironment environment = event.getApplicationContext().getEnvironment();
Fail fail = Binder.get(environment).bind(FAIL_PROPERTY, Fail.class).orElse(Fail.NEVER);
PropertiesMigrationReport report = new PropertiesMigrationReporter(loadRepository(), environment).getReport();
this.report = report;
Fail fail = Binder.get(environment).bind(FAIL_PROPERTY, Fail.class).orElse(Fail.NEVER);
failIfNecessary(report, fail);
fail.check(report);
}
private ConfigurationMetadataRepository loadRepository() {
@@ -109,40 +110,36 @@ class PropertiesMigrationListener implements ApplicationListener<SpringApplicati
this.reported = true;
}
private void failIfNecessary(PropertiesMigrationReport report, Fail fail) {
if (shouldFail(report, fail)) {
throw new IllegalStateException("Found configuration keys that need to be migrated (%s=%s)"
.formatted(FAIL_PROPERTY, fail.name().toLowerCase(Locale.ROOT).replace('_', '-')));
}
}
private static boolean shouldFail(PropertiesMigrationReport report, Fail fail) {
return switch (fail) {
case NEVER -> false;
case ON_ERROR -> report.getErrorReport() != null;
case ON_WARNING -> report.getErrorReport() != null || report.getWarningReport() != null;
};
}
/**
* Conditions under which property migration should cause application startup to fail.
*/
enum Fail {
public enum Fail {
/**
* Do not fail application startup due to migration warnings or errors.
*/
NEVER,
NEVER((report) -> false),
/**
* Fail application startup if the migration report contains errors.
*/
ON_ERROR,
ON_ERROR((report) -> report.getErrorReport() != null),
/**
* Fail application startup if the migration report contains warnings or errors.
*/
ON_WARNING
ON_WARNING((report) -> report.getErrorReport() != null || report.getWarningReport() != null);
private final Predicate<PropertiesMigrationReport> predicate;
Fail(Predicate<PropertiesMigrationReport> predicate) {
this.predicate = predicate;
}
void check(PropertiesMigrationReport report) {
boolean failed = this.predicate.test(report);
Assert.state(!failed, "Found configuration keys that need to be migrated");
}
}
@@ -1,7 +1,7 @@
{
"properties": [
{
"name": "spring.tools.properties-migrator.fail",
"name": "spring.properties.migrator.fail",
"type": "org.springframework.boot.context.properties.migrator.PropertiesMigrationListener$Fail",
"description": "When the application should fail to start because of configuration keys that need to be migrated.",
"sourceType": "org.springframework.boot.context.properties.migrator.PropertiesMigrationListener",
@@ -61,37 +61,37 @@ class PropertiesMigrationListenerTests {
@Test
void failNeverWithRenamedKeyStarts() {
assertThatNoException().isThrownBy(() -> this.context = createSampleApplication()
.run("--spring.tools.properties-migrator.fail=never", "--logging.file=test.log"));
.run("--spring.properties.migrator.fail=never", "--logging.file=test.log"));
}
@Test
void failNeverWithUnsupportedKeyStarts() {
assertThatNoException().isThrownBy(() -> this.context = createSampleApplication()
.run("--spring.tools.properties-migrator.fail=never", "--spring.banner.image.width=10"));
.run("--spring.properties.migrator.fail=never", "--spring.banner.image.width=10"));
}
@Test
void failOnErrorWithRenamedKeyStarts() {
assertThatNoException().isThrownBy(() -> this.context = createSampleApplication()
.run("--spring.tools.properties-migrator.fail=on-error", "--logging.file=test.log"));
.run("--spring.properties.migrator.fail=on-error", "--logging.file=test.log"));
}
@Test
void failOnErrorWithUnsupportedKeyFails() {
assertThatIllegalStateException().isThrownBy(() -> createSampleApplication()
.run("--spring.tools.properties-migrator.fail=on-error", "--spring.banner.image.width=10"));
.run("--spring.properties.migrator.fail=on-error", "--spring.banner.image.width=10"));
}
@Test
void failOnWarningWithRenamedKeyFails() {
assertThatIllegalStateException().isThrownBy(() -> createSampleApplication()
.run("--spring.tools.properties-migrator.fail=on-warning", "--logging.file=test.log"));
.run("--spring.properties.migrator.fail=on-warning", "--logging.file=test.log"));
}
@Test
void failOnWarningWithUnsupportedKeyFails() {
assertThatIllegalStateException().isThrownBy(() -> createSampleApplication()
.run("--spring.tools.properties-migrator.fail=on-warning", "--spring.banner.image.width=10"));
.run("--spring.properties.migrator.fail=on-warning", "--spring.banner.image.width=10"));
}
private SpringApplication createSampleApplication() {
@@ -49,7 +49,7 @@ To enable that feature, add the following dependency to your project:
</dependency>
----
The configprop:spring.tools.properties-migrator.fail[] property controls whether migration warnings or errors cause the application to fail to start:
The configprop:spring.properties.migrator.fail[] property controls whether migration warnings or errors cause the application to fail to start:
* `never`: do not fail application startup because of migration warnings or errors (default).
* `on-error`: fail when properties that are no longer supported are found.