From ce788657ce89095c20e4139fc300648add5e14b7 Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Mon, 21 Mar 2022 13:02:28 +0000 Subject: [PATCH] Improve diagnostics for map binding failures Closes gh-30281 --- .../context/properties/bind/MapBinder.java | 3 +- .../analyzer/BindFailureAnalyzerTests.java | 33 ++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java index ea4382cb702f..e484cf4748bc 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/MapBinder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2019 the original author or authors. + * Copyright 2012-2022 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. @@ -61,6 +61,7 @@ protected Object bindAggregate(ConfigurationPropertyName name, Bindable targe if (!ConfigurationPropertyName.EMPTY.equals(name)) { ConfigurationProperty property = source.getConfigurationProperty(name); if (property != null && !hasDescendants) { + getContext().setConfigurationProperty(property); return getContext().getConverter().convert(property.getValue(), target); } source = source.filter(name::isAncestorOf); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/BindFailureAnalyzerTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/BindFailureAnalyzerTests.java index c0b5cdb418fb..d02dd8513b8a 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/BindFailureAnalyzerTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/BindFailureAnalyzerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 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. @@ -29,6 +29,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.diagnostics.FailureAnalysis; +import org.springframework.boot.logging.LogLevel; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; @@ -89,6 +90,16 @@ void bindExceptionDueToClassNotFoundConvertionFailure() { "failed to convert java.lang.String to java.lang.Class (caused by java.lang.ClassNotFoundException: com.example.Missing")); } + @Test + void bindExceptionDueToMapConversionFailure() { + FailureAnalysis analysis = performAnalysis(LoggingLevelFailureConfiguration.class, "logging.level=debug"); + assertThat(analysis.getDescription()).contains(failure("logging.level", "debug", + "\"logging.level\" from property source \"test\"", + "org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting " + + "from type [java.lang.String] to type [java.util.Map]")); + } + private static String failure(String property, String value, String origin, String reason) { return String.format("Property: %s%n Value: %s%n Origin: %s%n Reason: %s", property, value, origin, reason); @@ -151,6 +162,11 @@ static class NestedFailureConfiguration { } + @EnableConfigurationProperties(LoggingProperties.class) + static class LoggingLevelFailureConfiguration { + + } + @ConfigurationProperties("test.foo") @Validated static class FieldValidationFailureProperties { @@ -238,6 +254,21 @@ void setValue(String value) { } + @ConfigurationProperties("logging") + static class LoggingProperties { + + private Map level = new HashMap<>(); + + Map getLevel() { + return this.level; + } + + void setLevel(Map level) { + this.level = level; + } + + } + enum Fruit { APPLE, BANANA, ORANGE