Skip to content

Commit af846da

Browse files
committed
Merge branch '2.7.x' into 3.0.x
Closes gh-35933
2 parents 48e13af + efa0722 commit af846da

File tree

2 files changed

+60
-9
lines changed

2 files changed

+60
-9
lines changed

Diff for: spring-boot-project/spring-boot-tools/spring-boot-properties-migrator/src/main/java/org/springframework/boot/context/properties/migrator/PropertiesMigrationReporter.java

+50-7
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
package org.springframework.boot.context.properties.migrator;
1818

1919
import java.util.Collections;
20+
import java.util.HashSet;
2021
import java.util.LinkedHashMap;
2122
import java.util.List;
2223
import java.util.Map;
24+
import java.util.Set;
2325
import java.util.function.Predicate;
2426
import java.util.stream.Collectors;
2527

@@ -86,14 +88,26 @@ private PropertySource<?> mapPropertiesWithReplacement(PropertiesMigrationReport
8688
if (renamed.isEmpty()) {
8789
return null;
8890
}
89-
String target = "migrate-" + name;
90-
Map<String, OriginTrackedValue> content = new LinkedHashMap<>();
91-
for (PropertyMigration candidate : renamed) {
92-
OriginTrackedValue value = OriginTrackedValue.of(candidate.getProperty().getValue(),
93-
candidate.getProperty().getOrigin());
94-
content.put(candidate.getNewPropertyName(), value);
91+
NameTrackingPropertySource nameTrackingPropertySource = new NameTrackingPropertySource();
92+
this.environment.getPropertySources().addFirst(nameTrackingPropertySource);
93+
try {
94+
String target = "migrate-" + name;
95+
Map<String, OriginTrackedValue> content = new LinkedHashMap<>();
96+
for (PropertyMigration candidate : renamed) {
97+
String newPropertyName = candidate.getNewPropertyName();
98+
Object value = candidate.getProperty().getValue();
99+
if (nameTrackingPropertySource.isPlaceholderThatAccessesName(value, newPropertyName)) {
100+
continue;
101+
}
102+
OriginTrackedValue originTrackedValue = OriginTrackedValue.of(value,
103+
candidate.getProperty().getOrigin());
104+
content.put(newPropertyName, originTrackedValue);
105+
}
106+
return new OriginTrackedMapPropertySource(target, content);
107+
}
108+
finally {
109+
this.environment.getPropertySources().remove(nameTrackingPropertySource.getName());
95110
}
96-
return new OriginTrackedMapPropertySource(target, content);
97111
}
98112

99113
private boolean isMapType(ConfigurationMetadataProperty property) {
@@ -172,4 +186,33 @@ private String determinePropertySourceName(ConfigurationPropertySource source) {
172186
return source.getUnderlyingSource().toString();
173187
}
174188

189+
/**
190+
* {@link PropertySource} used to track accessed properties to protect against
191+
* circular references.
192+
*/
193+
private class NameTrackingPropertySource extends PropertySource<Object> {
194+
195+
private final Set<String> accessedNames = new HashSet<>();
196+
197+
NameTrackingPropertySource() {
198+
super(NameTrackingPropertySource.class.getName());
199+
}
200+
201+
boolean isPlaceholderThatAccessesName(Object value, String name) {
202+
if (value instanceof String) {
203+
this.accessedNames.clear();
204+
PropertiesMigrationReporter.this.environment.resolvePlaceholders((String) value);
205+
return this.accessedNames.contains(name);
206+
}
207+
return false;
208+
}
209+
210+
@Override
211+
public Object getProperty(String name) {
212+
this.accessedNames.add(name);
213+
return null;
214+
}
215+
216+
}
217+
175218
}

Diff for: spring-boot-project/spring-boot-tools/spring-boot-properties-migrator/src/test/java/org/springframework/boot/context/properties/migrator/PropertiesMigrationReporterTests.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ void invalidNameHandledGracefully() {
162162
}
163163

164164
@Test
165-
void mapPropertiesDeprecatedNoReplacement() throws IOException {
165+
void mapPropertiesDeprecatedNoReplacement() {
166166
this.environment.getPropertySources()
167167
.addFirst(
168168
new MapPropertySource("first", Collections.singletonMap("custom.map-no-replacement.key", "value")));
@@ -173,7 +173,7 @@ void mapPropertiesDeprecatedNoReplacement() throws IOException {
173173
}
174174

175175
@Test
176-
void mapPropertiesDeprecatedWithReplacement() throws IOException {
176+
void mapPropertiesDeprecatedWithReplacement() {
177177
this.environment.getPropertySources()
178178
.addFirst(new MapPropertySource("first",
179179
Collections.singletonMap("custom.map-with-replacement.key", "value")));
@@ -205,6 +205,14 @@ void mapPropertiesDeprecatedWithReplacementRelaxedBindingCamelCase() {
205205
.contains("Replacement: custom.the-map-replacement.key");
206206
}
207207

208+
@Test // gh-35919
209+
void directCircularReference() {
210+
this.environment.getPropertySources()
211+
.addFirst(new MapPropertySource("backcompat", Collections.singletonMap("wrong.two", "${test.two}")));
212+
createAnalyzer(loadRepository("metadata/sample-metadata.json")).getReport();
213+
assertThat(this.environment.getProperty("test.two")).isNull();
214+
}
215+
208216
private List<String> mapToNames(PropertySources sources) {
209217
List<String> names = new ArrayList<>();
210218
for (PropertySource<?> source : sources) {

0 commit comments

Comments
 (0)