|
|
|
|
@ -16,11 +16,13 @@
@@ -16,11 +16,13 @@
|
|
|
|
|
|
|
|
|
|
package org.springframework.boot.context.properties.migrator; |
|
|
|
|
|
|
|
|
|
import java.util.ArrayList; |
|
|
|
|
import java.util.Collections; |
|
|
|
|
import java.util.HashSet; |
|
|
|
|
import java.util.LinkedHashMap; |
|
|
|
|
import java.util.List; |
|
|
|
|
import java.util.Map; |
|
|
|
|
import java.util.Objects; |
|
|
|
|
import java.util.Set; |
|
|
|
|
import java.util.function.Predicate; |
|
|
|
|
|
|
|
|
|
@ -33,6 +35,7 @@ import org.springframework.boot.context.properties.source.ConfigurationPropertyS
@@ -33,6 +35,7 @@ import org.springframework.boot.context.properties.source.ConfigurationPropertyS
|
|
|
|
|
import org.springframework.boot.context.properties.source.IterableConfigurationPropertySource; |
|
|
|
|
import org.springframework.boot.env.OriginTrackedMapPropertySource; |
|
|
|
|
import org.springframework.boot.origin.OriginTrackedValue; |
|
|
|
|
import org.springframework.boot.origin.PropertySourceOrigin; |
|
|
|
|
import org.springframework.core.env.ConfigurableEnvironment; |
|
|
|
|
import org.springframework.core.env.PropertySource; |
|
|
|
|
import org.springframework.util.LinkedMultiValueMap; |
|
|
|
|
@ -64,7 +67,7 @@ class PropertiesMigrationReporter {
@@ -64,7 +67,7 @@ class PropertiesMigrationReporter {
|
|
|
|
|
*/ |
|
|
|
|
PropertiesMigrationReport getReport() { |
|
|
|
|
PropertiesMigrationReport report = new PropertiesMigrationReport(); |
|
|
|
|
Map<String, List<PropertyMigration>> properties = getMatchingProperties( |
|
|
|
|
Map<String, List<PropertyMigration>> properties = getPropertySourceMigrations( |
|
|
|
|
ConfigurationMetadataProperty::isDeprecated); |
|
|
|
|
if (properties.isEmpty()) { |
|
|
|
|
return report; |
|
|
|
|
@ -78,66 +81,72 @@ class PropertiesMigrationReporter {
@@ -78,66 +81,72 @@ class PropertiesMigrationReporter {
|
|
|
|
|
return report; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private PropertySource<?> mapPropertiesWithReplacement(PropertiesMigrationReport report, String name, |
|
|
|
|
List<PropertyMigration> properties) { |
|
|
|
|
report.add(name, properties); |
|
|
|
|
List<PropertyMigration> renamed = properties.stream().filter(PropertyMigration::isCompatibleType).toList(); |
|
|
|
|
if (renamed.isEmpty()) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
NameTrackingPropertySource nameTrackingPropertySource = new NameTrackingPropertySource(); |
|
|
|
|
this.environment.getPropertySources().addFirst(nameTrackingPropertySource); |
|
|
|
|
try { |
|
|
|
|
String target = "migrate-" + name; |
|
|
|
|
Map<String, OriginTrackedValue> content = new LinkedHashMap<>(); |
|
|
|
|
for (PropertyMigration candidate : renamed) { |
|
|
|
|
String newPropertyName = candidate.getNewPropertyName(); |
|
|
|
|
Object value = candidate.getProperty().getValue(); |
|
|
|
|
if (nameTrackingPropertySource.isPlaceholderThatAccessesName(value, newPropertyName)) { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
OriginTrackedValue originTrackedValue = OriginTrackedValue.of(value, |
|
|
|
|
candidate.getProperty().getOrigin()); |
|
|
|
|
content.put(newPropertyName, originTrackedValue); |
|
|
|
|
private Map<String, List<PropertyMigration>> getPropertySourceMigrations( |
|
|
|
|
Predicate<ConfigurationMetadataProperty> filter) { |
|
|
|
|
return getPropertySourceMigrations(this.allProperties.values().stream().filter(filter).toList()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private Map<String, List<PropertyMigration>> getPropertySourceMigrations( |
|
|
|
|
List<ConfigurationMetadataProperty> metadataProperties) { |
|
|
|
|
MultiValueMap<String, PropertyMigration> result = new LinkedMultiValueMap<>(); |
|
|
|
|
getPropertySourcesAsMap().forEach((propertySourceName, propertySource) -> { |
|
|
|
|
for (ConfigurationMetadataProperty metadataProperty : metadataProperties) { |
|
|
|
|
result.addAll(propertySourceName, getMigrations(propertySource, metadataProperty)); |
|
|
|
|
} |
|
|
|
|
return new OriginTrackedMapPropertySource(target, content); |
|
|
|
|
}); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private Map<String, ConfigurationPropertySource> getPropertySourcesAsMap() { |
|
|
|
|
Map<String, ConfigurationPropertySource> map = new LinkedHashMap<>(); |
|
|
|
|
for (ConfigurationPropertySource source : ConfigurationPropertySources.get(this.environment)) { |
|
|
|
|
map.put(determinePropertySourceName(source), source); |
|
|
|
|
} |
|
|
|
|
finally { |
|
|
|
|
this.environment.getPropertySources().remove(nameTrackingPropertySource.getName()); |
|
|
|
|
return map; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private String determinePropertySourceName(ConfigurationPropertySource source) { |
|
|
|
|
if (source.getUnderlyingSource() instanceof PropertySource<?> underlyingSource) { |
|
|
|
|
return underlyingSource.getName(); |
|
|
|
|
} |
|
|
|
|
return source.getUnderlyingSource().toString(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean isMapType(ConfigurationMetadataProperty property) { |
|
|
|
|
String type = property.getType(); |
|
|
|
|
return type != null && type.startsWith(Map.class.getName()); |
|
|
|
|
private List<PropertyMigration> getMigrations(ConfigurationPropertySource propertySource, |
|
|
|
|
ConfigurationMetadataProperty metadataProperty) { |
|
|
|
|
ConfigurationPropertyName propertyName = asConfigurationPropertyName(metadataProperty); |
|
|
|
|
List<PropertyMigration> migrations = new ArrayList<>(); |
|
|
|
|
addMigration(propertySource, metadataProperty, propertyName, false, migrations); |
|
|
|
|
if (isMapType(metadataProperty) && propertySource instanceof IterableConfigurationPropertySource iterable) { |
|
|
|
|
iterable.stream() |
|
|
|
|
.filter(propertyName::isAncestorOf) |
|
|
|
|
.forEach((ancestorPropertyName) -> addMigration(propertySource, metadataProperty, ancestorPropertyName, |
|
|
|
|
true, migrations)); |
|
|
|
|
} |
|
|
|
|
return migrations; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private Map<String, List<PropertyMigration>> getMatchingProperties( |
|
|
|
|
Predicate<ConfigurationMetadataProperty> filter) { |
|
|
|
|
MultiValueMap<String, PropertyMigration> result = new LinkedMultiValueMap<>(); |
|
|
|
|
List<ConfigurationMetadataProperty> candidates = this.allProperties.values().stream().filter(filter).toList(); |
|
|
|
|
getPropertySourcesAsMap().forEach((propertySourceName, propertySource) -> candidates.forEach((metadata) -> { |
|
|
|
|
ConfigurationPropertyName metadataName = ConfigurationPropertyName.isValid(metadata.getId()) |
|
|
|
|
? ConfigurationPropertyName.of(metadata.getId()) |
|
|
|
|
: ConfigurationPropertyName.adapt(metadata.getId(), '.'); |
|
|
|
|
// Direct match
|
|
|
|
|
ConfigurationProperty match = propertySource.getConfigurationProperty(metadataName); |
|
|
|
|
if (match != null) { |
|
|
|
|
result.add(propertySourceName, |
|
|
|
|
new PropertyMigration(match, metadata, determineReplacementMetadata(metadata), false)); |
|
|
|
|
} |
|
|
|
|
// Prefix match for maps
|
|
|
|
|
if (isMapType(metadata) && propertySource instanceof IterableConfigurationPropertySource iterableSource) { |
|
|
|
|
iterableSource.stream() |
|
|
|
|
.filter(metadataName::isAncestorOf) |
|
|
|
|
.map(propertySource::getConfigurationProperty) |
|
|
|
|
.forEach((property) -> { |
|
|
|
|
ConfigurationMetadataProperty replacement = determineReplacementMetadata(metadata); |
|
|
|
|
result.add(propertySourceName, new PropertyMigration(property, metadata, replacement, true)); |
|
|
|
|
}); |
|
|
|
|
private ConfigurationPropertyName asConfigurationPropertyName(ConfigurationMetadataProperty metadataProperty) { |
|
|
|
|
return ConfigurationPropertyName.isValid(metadataProperty.getId()) |
|
|
|
|
? ConfigurationPropertyName.of(metadataProperty.getId()) |
|
|
|
|
: ConfigurationPropertyName.adapt(metadataProperty.getId(), '.'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private void addMigration(ConfigurationPropertySource propertySource, |
|
|
|
|
ConfigurationMetadataProperty metadataProperty, ConfigurationPropertyName propertyName, |
|
|
|
|
boolean mapMigration, List<PropertyMigration> migrations) { |
|
|
|
|
ConfigurationProperty property = propertySource.getConfigurationProperty(propertyName); |
|
|
|
|
if (property != null) { |
|
|
|
|
ConfigurationMetadataProperty replacement = determineReplacementMetadata(metadataProperty); |
|
|
|
|
if (replacement == null || !hasSameName(property, replacement)) { |
|
|
|
|
migrations.add(new PropertyMigration(property, metadataProperty, replacement, mapMigration)); |
|
|
|
|
} |
|
|
|
|
})); |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean hasSameName(ConfigurationProperty property, ConfigurationMetadataProperty replacement) { |
|
|
|
|
return (property.getOrigin() instanceof PropertySourceOrigin propertySourceOrigin) |
|
|
|
|
&& Objects.equals(propertySourceOrigin.getPropertyName(), replacement.getName()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private ConfigurationMetadataProperty determineReplacementMetadata(ConfigurationMetadataProperty metadata) { |
|
|
|
|
@ -164,19 +173,38 @@ class PropertiesMigrationReporter {
@@ -164,19 +173,38 @@ class PropertiesMigrationReporter {
|
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private Map<String, ConfigurationPropertySource> getPropertySourcesAsMap() { |
|
|
|
|
Map<String, ConfigurationPropertySource> map = new LinkedHashMap<>(); |
|
|
|
|
for (ConfigurationPropertySource source : ConfigurationPropertySources.get(this.environment)) { |
|
|
|
|
map.put(determinePropertySourceName(source), source); |
|
|
|
|
} |
|
|
|
|
return map; |
|
|
|
|
private boolean isMapType(ConfigurationMetadataProperty property) { |
|
|
|
|
String type = property.getType(); |
|
|
|
|
return type != null && type.startsWith(Map.class.getName()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private String determinePropertySourceName(ConfigurationPropertySource source) { |
|
|
|
|
if (source.getUnderlyingSource() instanceof PropertySource) { |
|
|
|
|
return ((PropertySource<?>) source.getUnderlyingSource()).getName(); |
|
|
|
|
private PropertySource<?> mapPropertiesWithReplacement(PropertiesMigrationReport report, String name, |
|
|
|
|
List<PropertyMigration> properties) { |
|
|
|
|
report.add(name, properties); |
|
|
|
|
List<PropertyMigration> renamed = properties.stream().filter(PropertyMigration::isCompatibleType).toList(); |
|
|
|
|
if (renamed.isEmpty()) { |
|
|
|
|
return null; |
|
|
|
|
} |
|
|
|
|
NameTrackingPropertySource nameTrackingPropertySource = new NameTrackingPropertySource(); |
|
|
|
|
this.environment.getPropertySources().addFirst(nameTrackingPropertySource); |
|
|
|
|
try { |
|
|
|
|
String target = "migrate-" + name; |
|
|
|
|
Map<String, OriginTrackedValue> content = new LinkedHashMap<>(); |
|
|
|
|
for (PropertyMigration candidate : renamed) { |
|
|
|
|
String newPropertyName = candidate.getNewPropertyName(); |
|
|
|
|
Object value = candidate.getProperty().getValue(); |
|
|
|
|
if (nameTrackingPropertySource.isPlaceholderThatAccessesName(value, newPropertyName)) { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
OriginTrackedValue originTrackedValue = OriginTrackedValue.of(value, |
|
|
|
|
candidate.getProperty().getOrigin()); |
|
|
|
|
content.put(newPropertyName, originTrackedValue); |
|
|
|
|
} |
|
|
|
|
return new OriginTrackedMapPropertySource(target, content); |
|
|
|
|
} |
|
|
|
|
finally { |
|
|
|
|
this.environment.getPropertySources().remove(nameTrackingPropertySource.getName()); |
|
|
|
|
} |
|
|
|
|
return source.getUnderlyingSource().toString(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|