diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/GatewayMvcProperties.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/GatewayMvcProperties.java index 728ee4b8..fac05cb0 100644 --- a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/GatewayMvcProperties.java +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/GatewayMvcProperties.java @@ -36,7 +36,7 @@ public class GatewayMvcProperties { /** * Properties prefix. */ - public static final String PREFIX = "spring.cloud.gateway.mvc"; + public static final String PREFIX = "spring.cloud.gateway.server.webmvc"; /** * List of Routes. diff --git a/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/GatewayServerWebMvcPropertiesMigrationListener.java b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/GatewayServerWebMvcPropertiesMigrationListener.java new file mode 100644 index 00000000..8fdf6de4 --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/main/java/org/springframework/cloud/gateway/server/mvc/config/GatewayServerWebMvcPropertiesMigrationListener.java @@ -0,0 +1,193 @@ +/* + * Copyright 2013-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.gateway.server.mvc.config; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.boot.context.event.ApplicationFailedEvent; +import org.springframework.boot.context.event.ApplicationPreparedEvent; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.boot.context.event.SpringApplicationEvent; +import org.springframework.boot.context.properties.source.ConfigurationProperty; +import org.springframework.boot.context.properties.source.ConfigurationPropertyName; +import org.springframework.boot.context.properties.source.ConfigurationPropertySource; +import org.springframework.boot.context.properties.source.ConfigurationPropertySources; +import org.springframework.boot.context.properties.source.IterableConfigurationPropertySource; +import org.springframework.boot.env.OriginTrackedMapPropertySource; +import org.springframework.boot.origin.Origin; +import org.springframework.boot.origin.OriginTrackedValue; +import org.springframework.boot.origin.PropertySourceOrigin; +import org.springframework.boot.origin.TextResourceOrigin; +import org.springframework.context.ApplicationListener; +import org.springframework.core.env.ConfigurableEnvironment; +import org.springframework.core.env.PropertySource; +import org.springframework.util.ClassUtils; +import org.springframework.util.LinkedMultiValueMap; + +class GatewayServerWebMvcPropertiesMigrationListener implements ApplicationListener { + + private static final Log logger = LogFactory.getLog(GatewayServerWebMvcPropertiesMigrationListener.class); + + private static final String PROPERTIES_MIGRATOR_CLASS = "org.springframework.boot.context.properties.migrator.PropertiesMigrationListener"; + + private static final String DEPRECATED_ROOT = "spring.cloud.gateway.mvc"; + + private static final String DEPRECATED_ROUTES_LIST_KEY = DEPRECATED_ROOT + ".routes"; + + private static final String DEPRECATED_ROUTES_MAP_KEY = DEPRECATED_ROOT + ".routes-map"; + + private static final String DEPRECATED_ROUTESMAP_KEY = DEPRECATED_ROOT + ".routesMap"; + + private static final String GATEWAY_PROPERTY_SOURCE_PREFIX = "migrategatewaymvc"; + + private static final String NEW_ROUTES_LIST_KEY = GatewayMvcProperties.PREFIX + ".routes"; + + private static final String NEW_ROUTES_MAP_KEY = GatewayMvcProperties.PREFIX + ".routes-map"; + + private final List routesMigrations = new ArrayList<>(); + + @Override + public void onApplicationEvent(SpringApplicationEvent event) { + // only run if spring-boot-properties-migrator is on the classpath + if (!ClassUtils.isPresent(PROPERTIES_MIGRATOR_CLASS, null)) { + return; + } + if (event instanceof ApplicationPreparedEvent preparedEvent) { + onApplicationPreparedEvent(preparedEvent); + } + if (event instanceof ApplicationReadyEvent || event instanceof ApplicationFailedEvent) { + logLegacyPropertiesReport(); + } + } + + private void onApplicationPreparedEvent(ApplicationPreparedEvent event) { + // find deprecated keys + ConfigurableEnvironment env = event.getApplicationContext().getEnvironment(); + + ConfigurationPropertySources.get(env).forEach(propertySource -> { + routesMigrations.addAll(migrate(env, propertySource, GATEWAY_PROPERTY_SOURCE_PREFIX + "routes-", + DEPRECATED_ROUTES_LIST_KEY, NEW_ROUTES_LIST_KEY)); + routesMigrations.addAll(migrate(env, propertySource, GATEWAY_PROPERTY_SOURCE_PREFIX + "routes-map-", + DEPRECATED_ROUTES_MAP_KEY, NEW_ROUTES_MAP_KEY)); + routesMigrations.addAll(migrate(env, propertySource, GATEWAY_PROPERTY_SOURCE_PREFIX + "routesMap-", + DEPRECATED_ROUTES_MAP_KEY, NEW_ROUTES_MAP_KEY)); + }); + } + + private List migrate(ConfigurableEnvironment env, ConfigurationPropertySource propertySource, + String propertySourcePrefix, String deprecatedKey, String newKeyPrefix) { + List migrations = new ArrayList<>(); + + if (propertySource instanceof IterableConfigurationPropertySource iterableSource) { + ConfigurationPropertyName routesParentName = ConfigurationPropertyName.of(deprecatedKey); + List matchingConfigProps = iterableSource.filter(n -> { + if (n.getNumberOfElements() < routesParentName.getNumberOfElements()) { + return false; + } + ConfigurationPropertyName chop = n.chop(routesParentName.getNumberOfElements()); + return routesParentName.equals(chop); + }).stream().toList(); + if (!matchingConfigProps.isEmpty()) { + String originalPropertySourceName; + if (propertySource.getUnderlyingSource() instanceof PropertySource underlyingSource) { + originalPropertySourceName = underlyingSource.getName(); + } + else { + originalPropertySourceName = propertySource.getUnderlyingSource().toString(); + } + String newPropertySourceName = propertySourcePrefix + originalPropertySourceName; + Map content = new LinkedHashMap<>(); + // migrate to new keys + for (ConfigurationPropertyName originalPropertyName : matchingConfigProps) { + ConfigurationPropertyName suffix = originalPropertyName + .subName(routesParentName.getNumberOfElements()); + ConfigurationPropertyName newProperty = ConfigurationPropertyName.of(newKeyPrefix).append(suffix); + ConfigurationProperty configurationProperty = propertySource + .getConfigurationProperty(originalPropertyName); + Object value = configurationProperty.getValue(); + OriginTrackedValue originTrackedValue = OriginTrackedValue.of(value, + configurationProperty.getOrigin()); + content.put(newProperty.toString(), originTrackedValue); + migrations.add(new Migration(originalPropertySourceName, originalPropertyName, + configurationProperty, newProperty)); + } + env.getPropertySources() + .addBefore(originalPropertySourceName, + new OriginTrackedMapPropertySource(newPropertySourceName, content)); + } + } + return migrations; + } + + private void logLegacyPropertiesReport() { + // log warnings + if (!routesMigrations.isEmpty()) { + LinkedMultiValueMap content = new LinkedMultiValueMap<>(); + routesMigrations.forEach(migration -> content.add(migration.originalPropertySourceName(), migration)); + + StringBuilder report = new StringBuilder(); + report.append(String + .format("%nThe use of configuration keys that have been renamed was found in the environment:%n%n")); + + content.forEach((name, properties) -> { + report.append(String.format("Property source '%s':%n", name)); + // properties.sort(PropertyMigration.COMPARATOR); + properties.forEach((property) -> { + ConfigurationPropertyName originalPropertyName = property.originalPropertyName(); + report.append(String.format("\tKey: %s%n", originalPropertyName)); + Integer lineNumber = property.determineLineNumber(); + if (lineNumber != null) { + report.append(String.format("\t\tLine: %d%n", lineNumber)); + } + report.append(String.format("\t\tReplacement: %s%n", property.newProperty().toString())); + }); + report.append(String.format("%n")); + }); + + report.append(String.format("%n")); + report.append("Each configuration key has been temporarily mapped to its " + + "replacement for your convenience. To silence this warning, please " + + "update your configuration to use the new keys."); + report.append(String.format("%n")); + logger.warn(report.toString()); + } + } + + private record Migration(String originalPropertySourceName, ConfigurationPropertyName originalPropertyName, + ConfigurationProperty originalProperty, ConfigurationPropertyName newProperty) { + + private Integer determineLineNumber() { + Origin origin = originalProperty.getOrigin(); + if (origin instanceof PropertySourceOrigin propertySourceOrigin) { + origin = propertySourceOrigin.getOrigin(); + } + if (origin instanceof TextResourceOrigin textOrigin) { + if (textOrigin.getLocation() != null) { + return textOrigin.getLocation().getLine() + 1; + } + } + return null; + } + } + +} diff --git a/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/additional-spring-configuration-metadata.json index ff054ce3..ce35f3ef 100644 --- a/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,46 +1,370 @@ { "properties": [ { - "name": "spring.cloud.gateway.mvc.form-filter.enabled", + "name": "spring.cloud.gateway.server.webmvc.form-filter.enabled", "type": "java.lang.Boolean", "description": "Enables the form-filter.", "defaultValue": "true" }, { - "name": "spring.cloud.gateway.mvc.forwarded-request-headers-filter.enabled", + "name": "spring.cloud.gateway.server.webmvc.forwarded-request-headers-filter.enabled", "type": "java.lang.Boolean", "description": "Enables the forwarded-request-headers-filter.", "defaultValue": "true" }, { - "name": "spring.cloud.gateway.mvc.remove-content-length-request-headers-filter.enabled", + "name": "spring.cloud.gateway.server.webmvc.remove-content-length-request-headers-filter.enabled", "type": "java.lang.Boolean", "description": "Enables the remove-content-length-request-headers-filter.", "defaultValue": "true" }, { - "name": "spring.cloud.gateway.mvc.remove-hop-by-hop-request-headers-filter.enabled", + "name": "spring.cloud.gateway.server.webmvc.remove-hop-by-hop-request-headers-filter.enabled", "type": "java.lang.Boolean", - "description": "Enables the forwarded-request-headers-filter.", + "description": "Enables the remove-hop-by-hop-request-headers-filter.", "defaultValue": "true" }, { - "name": "spring.cloud.gateway.mvc.remove-hop-by-hop-response-headers-filter.enabled", + "name": "spring.cloud.gateway.server.webmvc.remove-hop-by-hop-response-headers-filter.enabled", "type": "java.lang.Boolean", - "description": "Enables the forwarded-request-headers-filter.", + "description": "Enables the remove-hop-by-hop-response-headers-filter.", "defaultValue": "true" }, { - "name": "spring.cloud.gateway.mvc.transfer-encoding-normalization-request-headers-filter.enabled", + "name": "spring.cloud.gateway.server.webmvc.remove-http2-status-response-headers-filter.enabled", + "type": "java.lang.Boolean", + "description": "Enables the remove-http2-status-response-headers-filter.", + "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.server.webmvc.transfer-encoding-normalization-request-headers-filter.enabled", "type": "java.lang.Boolean", "description": "Enables the transfer-encoding-normalization-request-headers-filter.", "defaultValue": "true" }, { - "name": "spring.cloud.gateway.mvc.weight-calculator-filter.enabled", + "name": "spring.cloud.gateway.server.webmvc.weight-calculator-filter.enabled", "type": "java.lang.Boolean", "description": "Enables the weight-calculator-filter.", "defaultValue": "true" + }, + { + "name": "spring.cloud.gateway.mvc.form-filter.enabled", + "type": "java.lang.Boolean", + "description": "Enables the form-filter.", + "defaultValue": "true", + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.form-filter.enabled", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.forwarded-request-headers-filter.enabled", + "type": "java.lang.Boolean", + "description": "Enables the forwarded-request-headers-filter.", + "defaultValue": "true", + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.forwarded-request-headers-filter.enabled", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.remove-content-length-request-headers-filter.enabled", + "type": "java.lang.Boolean", + "description": "Enables the remove-content-length-request-headers-filter.", + "defaultValue": "true", + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.remove-content-length-request-headers-filter.enabled", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.remove-hop-by-hop-request-headers-filter.enabled", + "type": "java.lang.Boolean", + "description": "Enables the remove-hop-by-hop-request-headers-filter.", + "defaultValue": "true", + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.remove-hop-by-hop-request-headers-filter.enabled", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.remove-hop-by-hop-response-headers-filter.enabled", + "type": "java.lang.Boolean", + "description": "Enables the remove-hop-by-hop-response-headers-filter.", + "defaultValue": "true", + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.remove-hop-by-hop-response-headers-filter.enabled", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.remove-http2-status-response-headers-filter.enabled", + "type": "java.lang.Boolean", + "description": "Enables the remove-http2-status-response-headers-filter.", + "defaultValue": "true", + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.remove-http2-status-response-headers-filter.enabled", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.routes", + "type": "java.util.List", + "description": "List of Routes.", + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.routes", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.routes-map", + "type": "java.util.LinkedHashMap", + "description": "Map of Routes.", + "sourceType": "org.springframework.cloud.gateway.server.mvc.config.GatewayMvcProperties", + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.routes-map", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.streaming-buffer-size", + "type": "java.lang.Integer", + "description": "Buffer size for streaming media mime-types.", + "sourceType": "org.springframework.cloud.gateway.server.mvc.config.GatewayMvcProperties", + "defaultValue": 16384, + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.streaming-buffer-size", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.streaming-media-types", + "type": "java.util.List", + "description": "Mime-types that are streaming.", + "sourceType": "org.springframework.cloud.gateway.server.mvc.config.GatewayMvcProperties", + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.streaming-media-types", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.transfer-encoding-normalization-request-headers-filter.enabled", + "type": "java.lang.Boolean", + "description": "Enables the transfer-encoding-normalization-request-headers-filter.", + "defaultValue": "true", + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.transfer-encoding-normalization-request-headers-filter.enabled", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.weight-calculator-filter.enabled", + "type": "java.lang.Boolean", + "description": "Enables the weight-calculator-filter.", + "defaultValue": "true", + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.weight-calculator-filter.enabled", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.x-forwarded-request-headers-filter.enabled", + "type": "java.lang.Boolean", + "description": "If the XForwardedHeadersFilter is enabled.", + "sourceType": "org.springframework.cloud.gateway.server.mvc.filter.XForwardedRequestHeadersFilterProperties", + "defaultValue": true, + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.x-forwarded-request-headers-filter.enabled", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.x-forwarded-request-headers-filter.for-append", + "type": "java.lang.Boolean", + "description": "If appending X-Forwarded-For as a list is enabled.", + "sourceType": "org.springframework.cloud.gateway.server.mvc.filter.XForwardedRequestHeadersFilterProperties", + "defaultValue": true, + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.x-forwarded-request-headers-filter.for-append", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.x-forwarded-request-headers-filter.for-enabled", + "type": "java.lang.Boolean", + "description": "If X-Forwarded-For is enabled.", + "sourceType": "org.springframework.cloud.gateway.server.mvc.filter.XForwardedRequestHeadersFilterProperties", + "defaultValue": true, + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.x-forwarded-request-headers-filter.for-enabled", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.x-forwarded-request-headers-filter.host-append", + "type": "java.lang.Boolean", + "description": "If appending X-Forwarded-Host as a list is enabled.", + "sourceType": "org.springframework.cloud.gateway.server.mvc.filter.XForwardedRequestHeadersFilterProperties", + "defaultValue": true, + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.x-forwarded-request-headers-filter.host-append", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.x-forwarded-request-headers-filter.host-enabled", + "type": "java.lang.Boolean", + "description": "If X-Forwarded-Host is enabled.", + "sourceType": "org.springframework.cloud.gateway.server.mvc.filter.XForwardedRequestHeadersFilterProperties", + "defaultValue": true, + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.x-forwarded-request-headers-filter.host-enabled", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.x-forwarded-request-headers-filter.order", + "type": "java.lang.Integer", + "description": "The order of the XForwardedHeadersFilter.", + "sourceType": "org.springframework.cloud.gateway.server.mvc.filter.XForwardedRequestHeadersFilterProperties", + "defaultValue": 0, + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.x-forwarded-request-headers-filter.order", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.x-forwarded-request-headers-filter.port-append", + "type": "java.lang.Boolean", + "description": "If appending X-Forwarded-Port as a list is enabled.", + "sourceType": "org.springframework.cloud.gateway.server.mvc.filter.XForwardedRequestHeadersFilterProperties", + "defaultValue": true, + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.x-forwarded-request-headers-filter.port-append", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.x-forwarded-request-headers-filter.port-enabled", + "type": "java.lang.Boolean", + "description": "If X-Forwarded-Port is enabled.", + "sourceType": "org.springframework.cloud.gateway.server.mvc.filter.XForwardedRequestHeadersFilterProperties", + "defaultValue": true, + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.x-forwarded-request-headers-filter.port-enabled", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.x-forwarded-request-headers-filter.prefix-append", + "type": "java.lang.Boolean", + "description": "If appending X-Forwarded-Prefix as a list is enabled.", + "sourceType": "org.springframework.cloud.gateway.server.mvc.filter.XForwardedRequestHeadersFilterProperties", + "defaultValue": true, + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.x-forwarded-request-headers-filter.prefix-append", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.x-forwarded-request-headers-filter.prefix-enabled", + "type": "java.lang.Boolean", + "description": "If X-Forwarded-Prefix is enabled.", + "sourceType": "org.springframework.cloud.gateway.server.mvc.filter.XForwardedRequestHeadersFilterProperties", + "defaultValue": true, + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.x-forwarded-request-headers-filter.prefix-enabled", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.x-forwarded-request-headers-filter.proto-append", + "type": "java.lang.Boolean", + "description": "If appending X-Forwarded-Proto as a list is enabled.", + "sourceType": "org.springframework.cloud.gateway.server.mvc.filter.XForwardedRequestHeadersFilterProperties", + "defaultValue": true, + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.x-forwarded-request-headers-filter.proto-append", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.x-forwarded-request-headers-filter.proto-enabled", + "type": "java.lang.Boolean", + "description": "If X-Forwarded-Proto is enabled.", + "sourceType": "org.springframework.cloud.gateway.server.mvc.filter.XForwardedRequestHeadersFilterProperties", + "defaultValue": true, + "deprecated": true, + "deprecation": { + "replacement": "spring.cloud.gateway.server.webmvc.x-forwarded-request-headers-filter.proto-enabled", + "since": "4.3.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.http-client.connect-timeout", + "type": "java.time.Duration", + "description": "The HttpClient connect timeout.", + "sourceType": "org.springframework.cloud.gateway.server.mvc.config.GatewayMvcProperties$HttpClient", + "deprecated": true, + "deprecation": { + "replacement": "spring.http.client.connect-timeout", + "since": "4.2.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.http-client.read-timeout", + "type": "java.time.Duration", + "description": "The HttpClient read timeout.", + "sourceType": "org.springframework.cloud.gateway.server.mvc.config.GatewayMvcProperties$HttpClient", + "deprecated": true, + "deprecation": { + "replacement": "spring.http.client.read-timeout", + "since": "4.2.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.http-client.ssl-bundle", + "type": "java.lang.String", + "description": "The name of the SSL bundle to use.", + "sourceType": "org.springframework.cloud.gateway.server.mvc.config.GatewayMvcProperties$HttpClient", + "deprecated": true, + "deprecation": { + "replacement": "spring.http.client.ssl.bundle", + "since": "4.2.0" + } + }, + { + "name": "spring.cloud.gateway.mvc.http-client.type", + "type": "org.springframework.cloud.gateway.server.mvc.config.GatewayMvcProperties$HttpClientType", + "description": "The HttpClient type. Defaults to JDK.", + "sourceType": "org.springframework.cloud.gateway.server.mvc.config.GatewayMvcProperties$HttpClient", + "defaultValue": "jdk", + "deprecated": true, + "deprecation": {} } ] } diff --git a/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/spring.factories b/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/spring.factories index c208d466..6ce983dc 100644 --- a/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-gateway-server-mvc/src/main/resources/META-INF/spring.factories @@ -18,3 +18,7 @@ org.springframework.boot.env.EnvironmentPostProcessor=\ org.springframework.cloud.gateway.server.mvc.GatewayServerMvcAutoConfiguration.GatewayHttpClientEnvironmentPostProcessor,\ org.springframework.cloud.gateway.server.mvc.common.MultipartEnvironmentPostProcessor + +# Application Listeners +org.springframework.context.ApplicationListener=\ + org.springframework.cloud.gateway.server.mvc.config.GatewayServerWebMvcPropertiesMigrationListener \ No newline at end of file diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/GatewayMvcPropertiesMigrationTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/GatewayMvcPropertiesMigrationTests.java new file mode 100644 index 00000000..2043fc73 --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/GatewayMvcPropertiesMigrationTests.java @@ -0,0 +1,82 @@ +/* + * Copyright 2013-2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.gateway.server.mvc; + +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.cloud.gateway.server.mvc.config.GatewayMvcProperties; +import org.springframework.cloud.gateway.server.mvc.filter.FormFilter; +import org.springframework.cloud.gateway.server.mvc.filter.ForwardedRequestHeadersFilter; +import org.springframework.cloud.gateway.server.mvc.filter.RemoveContentLengthRequestHeadersFilter; +import org.springframework.cloud.gateway.server.mvc.filter.RemoveHopByHopRequestHeadersFilter; +import org.springframework.cloud.gateway.server.mvc.filter.RemoveHopByHopResponseHeadersFilter; +import org.springframework.cloud.gateway.server.mvc.filter.RemoveHttp2StatusResponseHeadersFilter; +import org.springframework.cloud.gateway.server.mvc.filter.TransferEncodingNormalizationRequestHeadersFilter; +import org.springframework.cloud.gateway.server.mvc.filter.WeightCalculatorFilter; +import org.springframework.cloud.gateway.server.mvc.filter.XForwardedRequestHeadersFilter; +import org.springframework.context.ApplicationContext; +import org.springframework.test.context.ActiveProfiles; + +import static org.assertj.core.api.Assertions.assertThat; + +@SuppressWarnings("unchecked") +@SpringBootTest(properties = {}, webEnvironment = WebEnvironment.RANDOM_PORT) +@ActiveProfiles("propertiesmigrationtests") +public class GatewayMvcPropertiesMigrationTests { + + @Autowired + ApplicationContext context; + + @Autowired + GatewayMvcProperties properties; + + @SuppressWarnings("rawtypes") + @Test + public void deprecatedFilterEnabledPropertiesWork() { + assertBeanDoesNotExist(FormFilter.class); + assertBeanDoesNotExist(ForwardedRequestHeadersFilter.class); + assertBeanDoesNotExist(RemoveContentLengthRequestHeadersFilter.class); + assertBeanDoesNotExist(RemoveHopByHopRequestHeadersFilter.class); + assertBeanDoesNotExist(RemoveHopByHopResponseHeadersFilter.class); + assertBeanDoesNotExist(RemoveHttp2StatusResponseHeadersFilter.class); + assertBeanDoesNotExist(TransferEncodingNormalizationRequestHeadersFilter.class); + assertBeanDoesNotExist(WeightCalculatorFilter.class); + assertBeanDoesNotExist(XForwardedRequestHeadersFilter.class); + } + + @Test + public void deprecatedRoutePropertiesWork() { + assertThat(properties.getRoutes()).hasSize(2); + assertThat(properties.getRoutesMap()).hasSize(2); + } + + private void assertBeanDoesNotExist(Class type) { + assertThat(context.getBeanNamesForType(type)).isEmpty(); + } + + @SpringBootConfiguration + @EnableAutoConfiguration + protected static class TestConfiguration { + + } + +} diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfigurationTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfigurationTests.java index 2ffaa203..886a7429 100644 --- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfigurationTests.java +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/GatewayServerMvcAutoConfigurationTests.java @@ -116,15 +116,15 @@ public class GatewayServerMvcAutoConfigurationTests { HandlerFunctionAutoConfiguration.class, GatewayServerMvcAutoConfiguration.class, HttpClientAutoConfiguration.class, RestTemplateAutoConfiguration.class, RestClientAutoConfiguration.class, SslAutoConfiguration.class)) - .withPropertyValues("spring.cloud.gateway.mvc.form-filter.enabled=false", - "spring.cloud.gateway.mvc.forwarded-request-headers-filter.enabled=false", - "spring.cloud.gateway.mvc.remove-content-length-request-headers-filter.enabled=false", - "spring.cloud.gateway.mvc.remove-hop-by-hop-request-headers-filter.enabled=false", - "spring.cloud.gateway.mvc.remove-hop-by-hop-response-headers-filter.enabled=false", - "spring.cloud.gateway.mvc.remove-http2-status-response-headers-filter.enabled=false", - "spring.cloud.gateway.mvc.transfer-encoding-normalization-request-headers-filter.enabled=false", - "spring.cloud.gateway.mvc.weight-calculator-filter.enabled=false", - "spring.cloud.gateway.mvc.x-forwarded-request-headers-filter.enabled=false") + .withPropertyValues("spring.cloud.gateway.server.webmvc.form-filter.enabled=false", + "spring.cloud.gateway.server.webmvc.forwarded-request-headers-filter.enabled=false", + "spring.cloud.gateway.server.webmvc.remove-content-length-request-headers-filter.enabled=false", + "spring.cloud.gateway.server.webmvc.remove-hop-by-hop-request-headers-filter.enabled=false", + "spring.cloud.gateway.server.webmvc.remove-hop-by-hop-response-headers-filter.enabled=false", + "spring.cloud.gateway.server.webmvc.remove-http2-status-response-headers-filter.enabled=false", + "spring.cloud.gateway.server.webmvc.transfer-encoding-normalization-request-headers-filter.enabled=false", + "spring.cloud.gateway.server.webmvc.weight-calculator-filter.enabled=false", + "spring.cloud.gateway.server.webmvc.x-forwarded-request-headers-filter.enabled=false") .run(context -> { assertThat(context).doesNotHaveBean(FormFilter.class); assertThat(context).doesNotHaveBean(ForwardedRequestHeadersFilter.class); diff --git a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/config/GatewayMvcPropertiesBeanDefinitionRegistrarTests.java b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/config/GatewayMvcPropertiesBeanDefinitionRegistrarTests.java index 41f6bff8..f0d34697 100644 --- a/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/config/GatewayMvcPropertiesBeanDefinitionRegistrarTests.java +++ b/spring-cloud-gateway-server-mvc/src/test/java/org/springframework/cloud/gateway/server/mvc/config/GatewayMvcPropertiesBeanDefinitionRegistrarTests.java @@ -199,14 +199,13 @@ public class GatewayMvcPropertiesBeanDefinitionRegistrarTests { void refreshWorks(ConfigurableApplicationContext context) { Map routerFunctions = getRouterFunctions(context); assertThat(routerFunctions).hasSize(6); - TestPropertyValues - .of("spring.cloud.gateway.mvc.routesMap.route3.uri=https://example3.com", - "spring.cloud.gateway.mvc.routesMap.route3.predicates[0].name=Path", - "spring.cloud.gateway.mvc.routesMap.route3.predicates[0].args.pattern=/anything/mapRoute3", - "spring.cloud.gateway.mvc.routesMap.route3.filters[0].Name=HttpbinUriResolver", - "spring.cloud.gateway.mvc.routesMap.route3.filters[1].Name=AddRequestHeader", - "spring.cloud.gateway.mvc.routesMap.route3.filters[1].args.name=X-Test", - "spring.cloud.gateway.mvc.routesMap.route3.filters[1].args.values=mapRoute3") + TestPropertyValues.of("spring.cloud.gateway.server.webmvc.routesMap.route3.uri=https://example3.com", + "spring.cloud.gateway.server.webmvc.routesMap.route3.predicates[0].name=Path", + "spring.cloud.gateway.server.webmvc.routesMap.route3.predicates[0].args.pattern=/anything/mapRoute3", + "spring.cloud.gateway.server.webmvc.routesMap.route3.filters[0].Name=HttpbinUriResolver", + "spring.cloud.gateway.server.webmvc.routesMap.route3.filters[1].Name=AddRequestHeader", + "spring.cloud.gateway.server.webmvc.routesMap.route3.filters[1].args.name=X-Test", + "spring.cloud.gateway.server.webmvc.routesMap.route3.filters[1].args.values=mapRoute3") .applyTo(context); ContextRefresher contextRefresher = context.getBean(ContextRefresher.class); contextRefresher.refresh(); diff --git a/spring-cloud-gateway-server-mvc/src/test/resources/application-propertiesmigrationtests.yml b/spring-cloud-gateway-server-mvc/src/test/resources/application-propertiesmigrationtests.yml new file mode 100644 index 00000000..32dc4133 --- /dev/null +++ b/spring-cloud-gateway-server-mvc/src/test/resources/application-propertiesmigrationtests.yml @@ -0,0 +1,47 @@ +spring.cloud.gateway.mvc: + form-filter.enabled: false + forwarded-request-headers-filter.enabled: false + remove-content-length-request-headers-filter.enabled: false + remove-hop-by-hop-request-headers-filter.enabled: false + remove-hop-by-hop-response-headers-filter.enabled: false + remove-http2-status-response-headers-filter.enabled: false + transfer-encoding-normalization-request-headers-filter.enabled: false + weight-calculator-filter.enabled: false + x-forwarded-request-headers-filter.enabled: false + routes: + - id: listRoute1 + uri: https://examplel1.com + predicates: + - name: Method + args: + methods: GET + - name: Path + args: + pattern: /anything/listRoute1 + filters: + - HttpbinUriResolver= + - AddRequestHeader=X-Test,listRoute1 + - id: listRoute2 + uri: https://examplel2.com + predicates: + - name: Method + args: + methods: GET + - name: Path + args: + pattern: /anything/listRoute2 + filters: + - HttpbinUriResolver= + - AddRequestHeader=X-Test,listRoute2 + routesMap: + route1: + uri: https://example1.com + predicates: + - Path=/anything/example1 + route2: + uri: https://example2.com + predicates: + - Path=/anything/example2 +logging: + level: + org.springframework.cloud.gateway.server.mvc: TRACE