|
|
|
|
@@ -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
|
|
|
|
|
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 {
|
|
|
|
|
*/
|
|
|
|
|
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,6 +81,103 @@ class PropertiesMigrationReporter {
|
|
|
|
|
return report;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 String determinePropertySourceName(ConfigurationPropertySource source) {
|
|
|
|
|
if (source.getUnderlyingSource() instanceof PropertySource<?> underlyingSource) {
|
|
|
|
|
return underlyingSource.getName();
|
|
|
|
|
}
|
|
|
|
|
return source.getUnderlyingSource().toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 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));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean hasSameName(ConfigurationProperty property, ConfigurationMetadataProperty replacement) {
|
|
|
|
|
return (property.getOrigin() instanceof PropertySourceOrigin propertySourceOrigin)
|
|
|
|
|
&& Objects.equals(propertySourceOrigin.getPropertyName(), replacement.getName());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ConfigurationMetadataProperty determineReplacementMetadata(ConfigurationMetadataProperty metadata) {
|
|
|
|
|
String replacementId = metadata.getDeprecation().getReplacement();
|
|
|
|
|
if (StringUtils.hasText(replacementId)) {
|
|
|
|
|
ConfigurationMetadataProperty replacement = this.allProperties.get(replacementId);
|
|
|
|
|
if (replacement != null) {
|
|
|
|
|
return replacement;
|
|
|
|
|
}
|
|
|
|
|
return detectMapValueReplacement(replacementId);
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ConfigurationMetadataProperty detectMapValueReplacement(String fullId) {
|
|
|
|
|
int lastDot = fullId.lastIndexOf('.');
|
|
|
|
|
if (lastDot == -1) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
ConfigurationMetadataProperty metadata = this.allProperties.get(fullId.substring(0, lastDot));
|
|
|
|
|
if (metadata != null && isMapType(metadata)) {
|
|
|
|
|
return metadata;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean isMapType(ConfigurationMetadataProperty property) {
|
|
|
|
|
String type = property.getType();
|
|
|
|
|
return type != null && type.startsWith(Map.class.getName());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private PropertySource<?> mapPropertiesWithReplacement(PropertiesMigrationReport report, String name,
|
|
|
|
|
List<PropertyMigration> properties) {
|
|
|
|
|
report.add(name, properties);
|
|
|
|
|
@@ -107,78 +207,6 @@ class PropertiesMigrationReporter {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private boolean isMapType(ConfigurationMetadataProperty property) {
|
|
|
|
|
String type = property.getType();
|
|
|
|
|
return type != null && type.startsWith(Map.class.getName());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ConfigurationMetadataProperty determineReplacementMetadata(ConfigurationMetadataProperty metadata) {
|
|
|
|
|
String replacementId = metadata.getDeprecation().getReplacement();
|
|
|
|
|
if (StringUtils.hasText(replacementId)) {
|
|
|
|
|
ConfigurationMetadataProperty replacement = this.allProperties.get(replacementId);
|
|
|
|
|
if (replacement != null) {
|
|
|
|
|
return replacement;
|
|
|
|
|
}
|
|
|
|
|
return detectMapValueReplacement(replacementId);
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ConfigurationMetadataProperty detectMapValueReplacement(String fullId) {
|
|
|
|
|
int lastDot = fullId.lastIndexOf('.');
|
|
|
|
|
if (lastDot == -1) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
ConfigurationMetadataProperty metadata = this.allProperties.get(fullId.substring(0, lastDot));
|
|
|
|
|
if (metadata != null && isMapType(metadata)) {
|
|
|
|
|
return metadata;
|
|
|
|
|
}
|
|
|
|
|
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 String determinePropertySourceName(ConfigurationPropertySource source) {
|
|
|
|
|
if (source.getUnderlyingSource() instanceof PropertySource) {
|
|
|
|
|
return ((PropertySource<?>) source.getUnderlyingSource()).getName();
|
|
|
|
|
}
|
|
|
|
|
return source.getUnderlyingSource().toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* {@link PropertySource} used to track accessed properties to protect against
|
|
|
|
|
* circular references.
|
|
|
|
|
|