Skip to content

Commit 04310a7

Browse files
committed
Merge branch '3.3.x'
Closes gh-43382
2 parents 60b028e + 7c7bb53 commit 04310a7

File tree

2 files changed

+87
-17
lines changed

2 files changed

+87
-17
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/diagnostics/analyzer/InvalidConfigurationPropertyValueFailureAnalyzer.java

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,18 @@
1616

1717
package org.springframework.boot.diagnostics.analyzer;
1818

19+
import java.util.HashSet;
1920
import java.util.List;
21+
import java.util.Set;
2022
import java.util.stream.Stream;
2123

22-
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
2324
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
2425
import org.springframework.boot.diagnostics.AbstractFailureAnalyzer;
2526
import org.springframework.boot.diagnostics.FailureAnalysis;
2627
import org.springframework.boot.diagnostics.FailureAnalyzer;
2728
import org.springframework.boot.origin.Origin;
2829
import org.springframework.boot.origin.OriginLookup;
30+
import org.springframework.boot.origin.PropertySourceOrigin;
2931
import org.springframework.core.env.ConfigurableEnvironment;
3032
import org.springframework.core.env.Environment;
3133
import org.springframework.core.env.PropertySource;
@@ -61,18 +63,23 @@ protected FailureAnalysis analyze(Throwable rootFailure, InvalidConfigurationPro
6163
}
6264

6365
private List<Descriptor> getDescriptors(String propertyName) {
66+
Set<Origin> seen = new HashSet<>();
6467
return getPropertySources().filter((source) -> source.containsProperty(propertyName))
6568
.map((source) -> Descriptor.get(source, propertyName))
69+
.filter((descriptor) -> seen.add(getOrigin(descriptor)))
6670
.toList();
6771
}
6872

69-
private Stream<PropertySource<?>> getPropertySources() {
70-
if (this.environment == null) {
71-
return Stream.empty();
73+
private Origin getOrigin(Descriptor descriptor) {
74+
Origin origin = descriptor.origin;
75+
if (origin instanceof PropertySourceOrigin propertySourceOrigin) {
76+
origin = propertySourceOrigin.getOrigin();
7277
}
73-
return this.environment.getPropertySources()
74-
.stream()
75-
.filter((source) -> !ConfigurationPropertySources.isAttachedConfigurationPropertySource(source));
78+
return origin;
79+
}
80+
81+
private Stream<PropertySource<?>> getPropertySources() {
82+
return (this.environment != null) ? this.environment.getPropertySources().stream() : Stream.empty();
7683
}
7784

7885
private void appendDetails(StringBuilder message, InvalidConfigurationPropertyValueException cause,

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/diagnostics/analyzer/InvalidConfigurationPropertyValueFailureAnalyzerTests.java

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,16 @@
2020

2121
import org.junit.jupiter.api.Test;
2222

23+
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
2324
import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException;
2425
import org.springframework.boot.diagnostics.FailureAnalysis;
2526
import org.springframework.boot.origin.Origin;
2627
import org.springframework.boot.origin.OriginLookup;
2728
import org.springframework.core.env.EnumerablePropertySource;
2829
import org.springframework.core.env.MapPropertySource;
30+
import org.springframework.core.env.SystemEnvironmentPropertySource;
2931
import org.springframework.mock.env.MockEnvironment;
32+
import org.springframework.util.ObjectUtils;
3033

3134
import static org.assertj.core.api.Assertions.assertThat;
3235

@@ -63,6 +66,26 @@ void analysisWithKnownProperty() {
6366
.doesNotContain("Additionally, this property is also set");
6467
}
6568

69+
@Test
70+
void analysisWithKnownPropertyFromSystemEnvironment() {
71+
MapPropertySource source = new SystemEnvironmentPropertySource("systemEnvironment",
72+
Collections.singletonMap("COM_EXAMPLE_TESTPROPERTY", "invalid"));
73+
this.environment.getPropertySources().addFirst(source);
74+
ConfigurationPropertySources.attach(this.environment);
75+
assertThat(this.environment.getProperty("com.example.test-property")).isEqualTo("invalid");
76+
InvalidConfigurationPropertyValueException failure = new InvalidConfigurationPropertyValueException(
77+
"com.example.test-property", "invalid", "This is not valid.");
78+
FailureAnalysis analysis = performAnalysis(failure);
79+
assertThat(analysis.getDescription()).contains("com.example.test-property")
80+
.contains("invalid")
81+
.contains("property source \"systemEnvironment\"");
82+
assertThat(analysis.getCause()).isSameAs(failure);
83+
assertThat(analysis.getAction()).contains("Review the value of the property with the provided reason.");
84+
assertThat(analysis.getDescription()).contains("Validation failed for the following reason")
85+
.contains("This is not valid.")
86+
.doesNotContain("Additionally, this property is also set");
87+
}
88+
6689
@Test
6790
void analysisWithKnownPropertyAndNoReason() {
6891
MapPropertySource source = new MapPropertySource("test", Collections.singletonMap("test.property", "invalid"));
@@ -84,6 +107,8 @@ void analysisWithKnownPropertyAndOtherCandidates() {
84107
this.environment.getPropertySources().addFirst(OriginCapablePropertySource.get(source));
85108
this.environment.getPropertySources().addLast(additional);
86109
this.environment.getPropertySources().addLast(OriginCapablePropertySource.get(another));
110+
this.environment.getPropertySources().addLast(OriginCapablePropertySource.get("another-again", another));
111+
ConfigurationPropertySources.attach(this.environment);
87112
InvalidConfigurationPropertyValueException failure = new InvalidConfigurationPropertyValueException(
88113
"test.property", "invalid", "This is not valid.");
89114
FailureAnalysis analysis = performAnalysis(failure);
@@ -92,7 +117,8 @@ void analysisWithKnownPropertyAndOtherCandidates() {
92117
assertThat(analysis.getDescription())
93118
.contains("Additionally, this property is also set in the following property sources:")
94119
.contains("In 'additional' with the value 'valid'")
95-
.contains("In 'another' with the value 'test' (originating from 'TestOrigin test.property')");
120+
.contains("In 'another' with the value 'test' (originating from 'TestOrigin test.property')")
121+
.doesNotContain("another-again");
96122
}
97123

98124
@Test
@@ -122,7 +148,11 @@ static class OriginCapablePropertySource<T> extends EnumerablePropertySource<T>
122148
private final EnumerablePropertySource<T> propertySource;
123149

124150
OriginCapablePropertySource(EnumerablePropertySource<T> propertySource) {
125-
super(propertySource.getName(), propertySource.getSource());
151+
this(propertySource.getName(), propertySource);
152+
}
153+
154+
OriginCapablePropertySource(String name, EnumerablePropertySource<T> propertySource) {
155+
super(name, propertySource.getSource());
126156
this.propertySource = propertySource;
127157
}
128158

@@ -138,20 +168,53 @@ public String[] getPropertyNames() {
138168

139169
@Override
140170
public Origin getOrigin(String name) {
141-
return new Origin() {
142-
143-
@Override
144-
public String toString() {
145-
return "TestOrigin " + name;
146-
}
147-
148-
};
171+
return new TestOrigin(name, this.propertySource.getName());
149172
}
150173

151174
static <T> OriginCapablePropertySource<T> get(EnumerablePropertySource<T> propertySource) {
152175
return new OriginCapablePropertySource<>(propertySource);
153176
}
154177

178+
static <T> OriginCapablePropertySource<T> get(String name, EnumerablePropertySource<T> propertySource) {
179+
return new OriginCapablePropertySource<>(name, propertySource);
180+
}
181+
182+
static final class TestOrigin implements Origin {
183+
184+
private final String name;
185+
186+
private final String sourceName;
187+
188+
private TestOrigin(String name, String sourceName) {
189+
this.name = name;
190+
this.sourceName = sourceName;
191+
}
192+
193+
@Override
194+
public boolean equals(Object obj) {
195+
if (this == obj) {
196+
return true;
197+
}
198+
if (obj == null || getClass() != obj.getClass()) {
199+
return false;
200+
}
201+
TestOrigin other = (TestOrigin) obj;
202+
return ObjectUtils.nullSafeEquals(this.name, other.name)
203+
&& ObjectUtils.nullSafeEquals(this.sourceName, other.sourceName);
204+
}
205+
206+
@Override
207+
public int hashCode() {
208+
return ObjectUtils.nullSafeHashCode(this.name);
209+
}
210+
211+
@Override
212+
public String toString() {
213+
return "TestOrigin " + this.name;
214+
}
215+
216+
}
217+
155218
}
156219

157220
}

0 commit comments

Comments
 (0)