Skip to content

Commit 1a699ae

Browse files
committed
Merge branch '3.2.x' into 3.3.x
Closes gh-41252
2 parents 2dd6c1c + 9629363 commit 1a699ae

File tree

7 files changed

+160
-71
lines changed

7 files changed

+160
-71
lines changed

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

Lines changed: 91 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@
1616

1717
package org.springframework.boot.context.properties.migrator;
1818

19+
import java.util.ArrayList;
1920
import java.util.Collections;
2021
import java.util.HashSet;
2122
import java.util.LinkedHashMap;
2223
import java.util.List;
2324
import java.util.Map;
25+
import java.util.Objects;
2426
import java.util.Set;
2527
import java.util.function.Predicate;
2628

@@ -33,6 +35,7 @@
3335
import org.springframework.boot.context.properties.source.IterableConfigurationPropertySource;
3436
import org.springframework.boot.env.OriginTrackedMapPropertySource;
3537
import org.springframework.boot.origin.OriginTrackedValue;
38+
import org.springframework.boot.origin.PropertySourceOrigin;
3639
import org.springframework.core.env.ConfigurableEnvironment;
3740
import org.springframework.core.env.PropertySource;
3841
import org.springframework.util.LinkedMultiValueMap;
@@ -64,7 +67,7 @@ class PropertiesMigrationReporter {
6467
*/
6568
PropertiesMigrationReport getReport() {
6669
PropertiesMigrationReport report = new PropertiesMigrationReport();
67-
Map<String, List<PropertyMigration>> properties = getMatchingProperties(
70+
Map<String, List<PropertyMigration>> properties = getPropertySourceMigrations(
6871
ConfigurationMetadataProperty::isDeprecated);
6972
if (properties.isEmpty()) {
7073
return report;
@@ -78,66 +81,72 @@ PropertiesMigrationReport getReport() {
7881
return report;
7982
}
8083

81-
private PropertySource<?> mapPropertiesWithReplacement(PropertiesMigrationReport report, String name,
82-
List<PropertyMigration> properties) {
83-
report.add(name, properties);
84-
List<PropertyMigration> renamed = properties.stream().filter(PropertyMigration::isCompatibleType).toList();
85-
if (renamed.isEmpty()) {
86-
return null;
87-
}
88-
NameTrackingPropertySource nameTrackingPropertySource = new NameTrackingPropertySource();
89-
this.environment.getPropertySources().addFirst(nameTrackingPropertySource);
90-
try {
91-
String target = "migrate-" + name;
92-
Map<String, OriginTrackedValue> content = new LinkedHashMap<>();
93-
for (PropertyMigration candidate : renamed) {
94-
String newPropertyName = candidate.getNewPropertyName();
95-
Object value = candidate.getProperty().getValue();
96-
if (nameTrackingPropertySource.isPlaceholderThatAccessesName(value, newPropertyName)) {
97-
continue;
98-
}
99-
OriginTrackedValue originTrackedValue = OriginTrackedValue.of(value,
100-
candidate.getProperty().getOrigin());
101-
content.put(newPropertyName, originTrackedValue);
84+
private Map<String, List<PropertyMigration>> getPropertySourceMigrations(
85+
Predicate<ConfigurationMetadataProperty> filter) {
86+
return getPropertySourceMigrations(this.allProperties.values().stream().filter(filter).toList());
87+
}
88+
89+
private Map<String, List<PropertyMigration>> getPropertySourceMigrations(
90+
List<ConfigurationMetadataProperty> metadataProperties) {
91+
MultiValueMap<String, PropertyMigration> result = new LinkedMultiValueMap<>();
92+
getPropertySourcesAsMap().forEach((propertySourceName, propertySource) -> {
93+
for (ConfigurationMetadataProperty metadataProperty : metadataProperties) {
94+
result.addAll(propertySourceName, getMigrations(propertySource, metadataProperty));
10295
}
103-
return new OriginTrackedMapPropertySource(target, content);
96+
});
97+
return result;
98+
}
99+
100+
private Map<String, ConfigurationPropertySource> getPropertySourcesAsMap() {
101+
Map<String, ConfigurationPropertySource> map = new LinkedHashMap<>();
102+
for (ConfigurationPropertySource source : ConfigurationPropertySources.get(this.environment)) {
103+
map.put(determinePropertySourceName(source), source);
104104
}
105-
finally {
106-
this.environment.getPropertySources().remove(nameTrackingPropertySource.getName());
105+
return map;
106+
}
107+
108+
private String determinePropertySourceName(ConfigurationPropertySource source) {
109+
if (source.getUnderlyingSource() instanceof PropertySource<?> underlyingSource) {
110+
return underlyingSource.getName();
107111
}
112+
return source.getUnderlyingSource().toString();
108113
}
109114

110-
private boolean isMapType(ConfigurationMetadataProperty property) {
111-
String type = property.getType();
112-
return type != null && type.startsWith(Map.class.getName());
115+
private List<PropertyMigration> getMigrations(ConfigurationPropertySource propertySource,
116+
ConfigurationMetadataProperty metadataProperty) {
117+
ConfigurationPropertyName propertyName = asConfigurationPropertyName(metadataProperty);
118+
List<PropertyMigration> migrations = new ArrayList<>();
119+
addMigration(propertySource, metadataProperty, propertyName, false, migrations);
120+
if (isMapType(metadataProperty) && propertySource instanceof IterableConfigurationPropertySource iterable) {
121+
iterable.stream()
122+
.filter(propertyName::isAncestorOf)
123+
.forEach((ancestorPropertyName) -> addMigration(propertySource, metadataProperty, ancestorPropertyName,
124+
true, migrations));
125+
}
126+
return migrations;
113127
}
114128

115-
private Map<String, List<PropertyMigration>> getMatchingProperties(
116-
Predicate<ConfigurationMetadataProperty> filter) {
117-
MultiValueMap<String, PropertyMigration> result = new LinkedMultiValueMap<>();
118-
List<ConfigurationMetadataProperty> candidates = this.allProperties.values().stream().filter(filter).toList();
119-
getPropertySourcesAsMap().forEach((propertySourceName, propertySource) -> candidates.forEach((metadata) -> {
120-
ConfigurationPropertyName metadataName = ConfigurationPropertyName.isValid(metadata.getId())
121-
? ConfigurationPropertyName.of(metadata.getId())
122-
: ConfigurationPropertyName.adapt(metadata.getId(), '.');
123-
// Direct match
124-
ConfigurationProperty match = propertySource.getConfigurationProperty(metadataName);
125-
if (match != null) {
126-
result.add(propertySourceName,
127-
new PropertyMigration(match, metadata, determineReplacementMetadata(metadata), false));
128-
}
129-
// Prefix match for maps
130-
if (isMapType(metadata) && propertySource instanceof IterableConfigurationPropertySource iterableSource) {
131-
iterableSource.stream()
132-
.filter(metadataName::isAncestorOf)
133-
.map(propertySource::getConfigurationProperty)
134-
.forEach((property) -> {
135-
ConfigurationMetadataProperty replacement = determineReplacementMetadata(metadata);
136-
result.add(propertySourceName, new PropertyMigration(property, metadata, replacement, true));
137-
});
129+
private ConfigurationPropertyName asConfigurationPropertyName(ConfigurationMetadataProperty metadataProperty) {
130+
return ConfigurationPropertyName.isValid(metadataProperty.getId())
131+
? ConfigurationPropertyName.of(metadataProperty.getId())
132+
: ConfigurationPropertyName.adapt(metadataProperty.getId(), '.');
133+
}
134+
135+
private void addMigration(ConfigurationPropertySource propertySource,
136+
ConfigurationMetadataProperty metadataProperty, ConfigurationPropertyName propertyName,
137+
boolean mapMigration, List<PropertyMigration> migrations) {
138+
ConfigurationProperty property = propertySource.getConfigurationProperty(propertyName);
139+
if (property != null) {
140+
ConfigurationMetadataProperty replacement = determineReplacementMetadata(metadataProperty);
141+
if (replacement == null || !hasSameName(property, replacement)) {
142+
migrations.add(new PropertyMigration(property, metadataProperty, replacement, mapMigration));
138143
}
139-
}));
140-
return result;
144+
}
145+
}
146+
147+
private boolean hasSameName(ConfigurationProperty property, ConfigurationMetadataProperty replacement) {
148+
return (property.getOrigin() instanceof PropertySourceOrigin propertySourceOrigin)
149+
&& Objects.equals(propertySourceOrigin.getPropertyName(), replacement.getName());
141150
}
142151

143152
private ConfigurationMetadataProperty determineReplacementMetadata(ConfigurationMetadataProperty metadata) {
@@ -164,19 +173,38 @@ private ConfigurationMetadataProperty detectMapValueReplacement(String fullId) {
164173
return null;
165174
}
166175

167-
private Map<String, ConfigurationPropertySource> getPropertySourcesAsMap() {
168-
Map<String, ConfigurationPropertySource> map = new LinkedHashMap<>();
169-
for (ConfigurationPropertySource source : ConfigurationPropertySources.get(this.environment)) {
170-
map.put(determinePropertySourceName(source), source);
171-
}
172-
return map;
176+
private boolean isMapType(ConfigurationMetadataProperty property) {
177+
String type = property.getType();
178+
return type != null && type.startsWith(Map.class.getName());
173179
}
174180

175-
private String determinePropertySourceName(ConfigurationPropertySource source) {
176-
if (source.getUnderlyingSource() instanceof PropertySource) {
177-
return ((PropertySource<?>) source.getUnderlyingSource()).getName();
181+
private PropertySource<?> mapPropertiesWithReplacement(PropertiesMigrationReport report, String name,
182+
List<PropertyMigration> properties) {
183+
report.add(name, properties);
184+
List<PropertyMigration> renamed = properties.stream().filter(PropertyMigration::isCompatibleType).toList();
185+
if (renamed.isEmpty()) {
186+
return null;
187+
}
188+
NameTrackingPropertySource nameTrackingPropertySource = new NameTrackingPropertySource();
189+
this.environment.getPropertySources().addFirst(nameTrackingPropertySource);
190+
try {
191+
String target = "migrate-" + name;
192+
Map<String, OriginTrackedValue> content = new LinkedHashMap<>();
193+
for (PropertyMigration candidate : renamed) {
194+
String newPropertyName = candidate.getNewPropertyName();
195+
Object value = candidate.getProperty().getValue();
196+
if (nameTrackingPropertySource.isPlaceholderThatAccessesName(value, newPropertyName)) {
197+
continue;
198+
}
199+
OriginTrackedValue originTrackedValue = OriginTrackedValue.of(value,
200+
candidate.getProperty().getOrigin());
201+
content.put(newPropertyName, originTrackedValue);
202+
}
203+
return new OriginTrackedMapPropertySource(target, content);
204+
}
205+
finally {
206+
this.environment.getPropertySources().remove(nameTrackingPropertySource.getName());
178207
}
179-
return source.getUnderlyingSource().toString();
180208
}
181209

182210
/**

spring-boot-project/spring-boot-tools/spring-boot-properties-migrator/src/main/java/org/springframework/boot/context/properties/migrator/PropertyMigration.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.springframework.boot.context.properties.source.ConfigurationProperty;
2626
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
2727
import org.springframework.boot.origin.Origin;
28+
import org.springframework.boot.origin.PropertySourceOrigin;
2829
import org.springframework.boot.origin.TextResourceOrigin;
2930
import org.springframework.util.Assert;
3031
import org.springframework.util.StringUtils;
@@ -67,6 +68,9 @@ class PropertyMigration {
6768

6869
private static Integer determineLineNumber(ConfigurationProperty property) {
6970
Origin origin = property.getOrigin();
71+
if (origin instanceof PropertySourceOrigin propertySourceOrigin) {
72+
origin = propertySourceOrigin.getOrigin();
73+
}
7074
if (origin instanceof TextResourceOrigin textOrigin) {
7175
if (textOrigin.getLocation() != null) {
7276
return textOrigin.getLocation().getLine() + 1;

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -31,6 +31,7 @@
3131
import org.springframework.boot.env.PropertiesPropertySourceLoader;
3232
import org.springframework.boot.origin.Origin;
3333
import org.springframework.boot.origin.OriginLookup;
34+
import org.springframework.boot.origin.PropertySourceOrigin;
3435
import org.springframework.core.env.ConfigurableEnvironment;
3536
import org.springframework.core.env.MapPropertySource;
3637
import org.springframework.core.env.MutablePropertySources;
@@ -87,6 +88,13 @@ void warningReport() throws IOException {
8788
assertThat(report).doesNotContain("wrong.one");
8889
}
8990

91+
@Test
92+
void warningReportReplacedWithSameRelaxedName() throws IOException {
93+
this.environment.getPropertySources().addFirst(loadPropertySource("test", "config/config-relaxed.properties"));
94+
String report = createWarningReport(loadRepository("metadata/sample-metadata.json"));
95+
assertThat(report).isNull();
96+
}
97+
9098
@Test
9199
void errorReport() throws IOException {
92100
this.environment.getPropertySources()
@@ -232,7 +240,11 @@ private void assertMappedProperty(PropertySource<?> propertySource, String name,
232240
assertThat(propertySource.getProperty(name)).isEqualTo(value);
233241
if (origin != null) {
234242
assertThat(propertySource).isInstanceOf(OriginLookup.class);
235-
assertThat(((OriginLookup<Object>) propertySource).getOrigin(name)).isEqualTo(origin);
243+
Origin actualOrigin = ((OriginLookup<Object>) propertySource).getOrigin(name);
244+
if (actualOrigin instanceof PropertySourceOrigin propertySourceOrigin) {
245+
actualOrigin = propertySourceOrigin.getOrigin();
246+
}
247+
assertThat(actualOrigin).isEqualTo(origin);
236248
}
237249
}
238250

Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
relaxed.this-that-the-other=test

spring-boot-project/spring-boot-tools/spring-boot-properties-migrator/src/test/resources/metadata/sample-metadata.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,17 @@
5454
{
5555
"name": "custom.the-map-replacement",
5656
"type": "java.util.Map<java.lang.String,java.lang.String>"
57+
},
58+
{
59+
"name": "relaxed.thisthat-theother",
60+
"type": "java.lang.String",
61+
"deprecation": {
62+
"replacement": "relaxed.this-that-the-other"
63+
}
64+
},
65+
{
66+
"name": "relaxed.this-that-the-other",
67+
"type": "java.lang.String"
5768
}
5869
]
5970
}

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/origin/PropertySourceOrigin.java

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -25,22 +25,36 @@
2525
* @author Phillip Webb
2626
* @since 2.0.0
2727
*/
28-
public class PropertySourceOrigin implements Origin {
28+
public class PropertySourceOrigin implements Origin, OriginProvider {
2929

3030
private final PropertySource<?> propertySource;
3131

3232
private final String propertyName;
3333

34+
private final Origin origin;
35+
3436
/**
3537
* Create a new {@link PropertySourceOrigin} instance.
3638
* @param propertySource the property source
3739
* @param propertyName the name from the property source
3840
*/
3941
public PropertySourceOrigin(PropertySource<?> propertySource, String propertyName) {
42+
this(propertySource, propertyName, null);
43+
}
44+
45+
/**
46+
* Create a new {@link PropertySourceOrigin} instance.
47+
* @param propertySource the property source
48+
* @param propertyName the name from the property source
49+
* @param origin the actual origin for the source if known
50+
* @since 3.2.8
51+
*/
52+
public PropertySourceOrigin(PropertySource<?> propertySource, String propertyName, Origin origin) {
4053
Assert.notNull(propertySource, "PropertySource must not be null");
4154
Assert.hasLength(propertyName, "PropertyName must not be empty");
4255
this.propertySource = propertySource;
4356
this.propertyName = propertyName;
57+
this.origin = origin;
4458
}
4559

4660
/**
@@ -60,9 +74,25 @@ public String getPropertyName() {
6074
return this.propertyName;
6175
}
6276

77+
/**
78+
* Return the actual origin for the source if known.
79+
* @return the actual source origin
80+
* @since 3.2.8
81+
*/
82+
@Override
83+
public Origin getOrigin() {
84+
return this.origin;
85+
}
86+
87+
@Override
88+
public Origin getParent() {
89+
return (this.origin != null) ? this.origin.getParent() : null;
90+
}
91+
6392
@Override
6493
public String toString() {
65-
return "\"" + this.propertyName + "\" from property source \"" + this.propertySource.getName() + "\"";
94+
return (this.origin != null) ? this.origin.toString()
95+
: "\"" + this.propertyName + "\" from property source \"" + this.propertySource.getName() + "\"";
6696
}
6797

6898
/**
@@ -75,7 +105,8 @@ public String toString() {
75105
*/
76106
public static Origin get(PropertySource<?> propertySource, String name) {
77107
Origin origin = OriginLookup.getOrigin(propertySource, name);
78-
return (origin != null) ? origin : new PropertySourceOrigin(propertySource, name);
108+
return (origin instanceof PropertySourceOrigin) ? origin
109+
: new PropertySourceOrigin(propertySource, name, origin);
79110
}
80111

81112
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/origin/PropertySourceOriginTests.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2023 the original author or authors.
2+
* Copyright 2012-2024 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -84,7 +84,9 @@ void getWhenPropertySourceSupportsOriginLookupShouldReturnOrigin() {
8484
withSettings().extraInterfaces(OriginLookup.class));
8585
OriginLookup<String> originCapablePropertySource = (OriginLookup<String>) propertySource;
8686
given(originCapablePropertySource.getOrigin("foo")).willReturn(origin);
87-
assertThat(PropertySourceOrigin.get(propertySource, "foo")).isSameAs(origin);
87+
Origin actual = PropertySourceOrigin.get(propertySource, "foo");
88+
assertThat(actual).hasToString(origin.toString());
89+
assertThat(((PropertySourceOrigin) actual).getOrigin()).isSameAs(origin);
8890
}
8991

9092
@Test

0 commit comments

Comments
 (0)