diff --git a/docs/pom.xml b/docs/pom.xml index 871ebc596..9705ded86 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -41,11 +41,9 @@ git-commit-id-plugin - org.apache.maven.plugins maven-dependency-plugin - org.apache.maven.plugins maven-resources-plugin @@ -57,7 +55,6 @@ asciidoctor-maven-plugin - org.apache.maven.plugins maven-antrun-plugin diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java index eeb09549b..28203aea6 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfiguration.java @@ -31,7 +31,6 @@ import org.apache.commons.logging.LogFactory; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.cloud.client.ServiceInstance; @@ -73,6 +72,8 @@ public class EurekaConfigServerBootstrapConfiguration { @ConditionalOnMissingBean(EurekaHttpClient.class) @ConditionalOnClass( name = "org.springframework.web.reactive.function.client.WebClient") + @ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", + havingValue = "true") public WebClientEurekaHttpClient configDiscoveryWebClientEurekaHttpClient( EurekaClientConfigBean config) { return (WebClientEurekaHttpClient) new WebClientTransportClientFactory() @@ -81,7 +82,8 @@ public class EurekaConfigServerBootstrapConfiguration { @Bean @ConditionalOnMissingBean(EurekaHttpClient.class) - @ConditionalOnMissingClass("org.springframework.web.reactive.function.client.WebClient") + @ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", + matchIfMissing = true, havingValue = "false") public RestTemplateEurekaHttpClient configDiscoveryRestTemplateEurekaHttpClient( EurekaClientConfigBean config) { return (RestTemplateEurekaHttpClient) new RestTemplateTransportClientFactory() diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientTransportClientFactory.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientTransportClientFactory.java index 876c0cdcc..fa6de993a 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientTransportClientFactory.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientTransportClientFactory.java @@ -89,8 +89,7 @@ public class WebClientTransportClientFactory implements TransportClientFactory { } private void setExchangeStrategies(WebClient.Builder builder) { - ObjectMapper objectMapper = mappingJacksonHttpMessageConverter() - .getObjectMapper(); + ObjectMapper objectMapper = objectMapper(); ExchangeStrategies strategies = ExchangeStrategies.builder() .codecs(clientDefaultCodecsConfigurer -> { clientDefaultCodecsConfigurer.defaultCodecs() @@ -105,11 +104,11 @@ public class WebClientTransportClientFactory implements TransportClientFactory { } private void skipHttp400Error(WebClient.Builder builder) { - builder.filter(Http4xxErrorExchangeFilterFunction()); + builder.filter(http4XxErrorExchangeFilterFunction()); } // Skip over 4xx http errors - private ExchangeFilterFunction Http4xxErrorExchangeFilterFunction() { + private ExchangeFilterFunction http4XxErrorExchangeFilterFunction() { return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> { // literally 400 pass the tests, not 4xxClientError if (clientResponse.statusCode().value() == 400) { @@ -132,54 +131,48 @@ public class WebClientTransportClientFactory implements TransportClientFactory { * {@link DeserializationFeature#UNWRAP_ROOT_VALUE}. * {@link PropertyNamingStrategy.SnakeCaseStrategy} is applied to the underlying * {@link ObjectMapper}. + * @deprecated to be removed. * @return a {@link MappingJackson2HttpMessageConverter} object */ + @Deprecated public MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter() { MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); - converter.setObjectMapper(new ObjectMapper() - .setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)); - - SimpleModule jsonModule = new SimpleModule(); - jsonModule.setSerializerModifier(createJsonSerializerModifier()); // keyFormatter, - // compact)); - converter.getObjectMapper().registerModule(jsonModule); - - converter.getObjectMapper().configure(SerializationFeature.WRAP_ROOT_VALUE, true); - converter.getObjectMapper().configure(DeserializationFeature.UNWRAP_ROOT_VALUE, - true); - converter.getObjectMapper().addMixIn(Applications.class, - ApplicationsJsonMixIn.class); - converter.getObjectMapper().addMixIn(InstanceInfo.class, - InstanceInfoJsonMixIn.class); - - // converter.getObjectMapper().addMixIn(DataCenterInfo.class, - // DataCenterInfoXmlMixIn.class); - // converter.getObjectMapper().addMixIn(InstanceInfo.PortWrapper.class, - // PortWrapperXmlMixIn.class); - // converter.getObjectMapper().addMixIn(Application.class, - // ApplicationXmlMixIn.class); - // converter.getObjectMapper().addMixIn(Applications.class, - // ApplicationsXmlMixIn.class); - + converter.setObjectMapper(objectMapper()); return converter; } - public static BeanSerializerModifier createJsonSerializerModifier() { // final - // KeyFormatter - // keyFormatter, - // final - // boolean - // compactMode) - // { + /** + * Provides the serialization configurations required by the Eureka Server. JSON + * content exchanged with eureka requires a root node matching the entity being + * serialized or deserialized. Achieved with + * {@link SerializationFeature#WRAP_ROOT_VALUE} and + * {@link DeserializationFeature#UNWRAP_ROOT_VALUE}. + * {@link PropertyNamingStrategy.SnakeCaseStrategy} is applied to the underlying + * {@link ObjectMapper}. + * @return a {@link ObjectMapper} object + */ + private ObjectMapper objectMapper() { + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE); + + SimpleModule jsonModule = new SimpleModule(); + jsonModule.setSerializerModifier(createJsonSerializerModifier()); + objectMapper.registerModule(jsonModule); + + objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true); + objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true); + objectMapper.addMixIn(Applications.class, ApplicationsJsonMixIn.class); + objectMapper.addMixIn(InstanceInfo.class, InstanceInfoJsonMixIn.class); + + return objectMapper; + } + + @Deprecated // reduce visibility in future release + public static BeanSerializerModifier createJsonSerializerModifier() { return new BeanSerializerModifier() { @Override public JsonSerializer modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer serializer) { - /* - * if (beanDesc.getBeanClass().isAssignableFrom(Applications.class)) { - * return new ApplicationsJsonBeanSerializer((BeanSerializerBase) - * serializer, keyFormatter); } - */ if (beanDesc.getBeanClass().isAssignableFrom(InstanceInfo.class)) { return new InstanceInfoJsonBeanSerializer( (BeanSerializerBase) serializer, false); diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/loadbalancer/LoadBalancerEurekaAutoConfiguration.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/loadbalancer/LoadBalancerEurekaAutoConfiguration.java index 1622059ad..97cfea726 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/loadbalancer/LoadBalancerEurekaAutoConfiguration.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/loadbalancer/LoadBalancerEurekaAutoConfiguration.java @@ -18,6 +18,7 @@ package org.springframework.cloud.netflix.eureka.loadbalancer; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientConfigurationRegistrar; import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients; @@ -34,9 +35,10 @@ import org.springframework.core.env.Environment; * @see EurekaLoadBalancerClientConfiguration */ @Configuration(proxyBeanMethods = false) -@EnableConfigurationProperties(EurekaLoadBalancerProperties.class) +@EnableConfigurationProperties @ConditionalOnClass(LoadBalancerClientConfigurationRegistrar.class) @LoadBalancerClients(defaultConfiguration = EurekaLoadBalancerClientConfiguration.class) +@ConditionalOnProperty(name = "eureka.client.enabled", matchIfMissing = true) public class LoadBalancerEurekaAutoConfiguration { /** diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/metadata/DefaultManagementMetadataProvider.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/metadata/DefaultManagementMetadataProvider.java index e209d50c1..ed364896b 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/metadata/DefaultManagementMetadataProvider.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/metadata/DefaultManagementMetadataProvider.java @@ -123,7 +123,7 @@ public class DefaultManagementMetadataProvider implements ManagementMetadataProv String refinedContextPath = '/' + StringUtils.trimLeadingCharacter(contextPath, '/'); URL base = new URL(scheme, hostname, port, refinedContextPath); - String refinedStatusPath = StringUtils.trimLeadingCharacter(statusPath, '/'); + String refinedStatusPath = refinedStatusPath(statusPath, contextPath); return new URL(base, refinedStatusPath).toString(); } catch (MalformedURLException e) { @@ -133,6 +133,13 @@ public class DefaultManagementMetadataProvider implements ManagementMetadataProv } } + private String refinedStatusPath(String statusPath, String contextPath) { + if (statusPath.startsWith(contextPath) && !"/".equals(contextPath)) { + statusPath = StringUtils.replace(statusPath, contextPath, ""); + } + return StringUtils.trimLeadingCharacter(statusPath, '/'); + } + private String getErrorMessage(String scheme, String hostname, int port, String contextPath, String statusPath) { return String.format( diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfigurationTests.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfigurationTests.java index 1c67d08e8..306b40eca 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfigurationTests.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/EurekaClientAutoConfigurationTests.java @@ -325,6 +325,20 @@ public class EurekaClientAutoConfigurationTests { .as("Wrong health check: " + instance.getHealthCheckUrl()).isTrue(); } + @Test + public void healthCheckUrlPathWithServerPortAndContextPathKebobCase() { + TestPropertyValues.of("server.port=8989", + "server.servlet.context-path=/servletContextPath", + "eureka.instance.health-check-url-path=${server.servlet.context-path:}/myHealthCheck") + .applyTo(this.context); + setupContext(RefreshAutoConfiguration.class); + EurekaInstanceConfigBean instance = this.context + .getBean(EurekaInstanceConfigBean.class); + assertThat(instance.getHealthCheckUrl()) + .as("Wrong health check: " + instance.getHealthCheckUrl()) + .endsWith(":8989/servletContextPath/myHealthCheck"); + } + @Test public void statusPageUrlPathAndManagementPortKabobCase() { TestPropertyValues diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfigurationWebClientTests.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfigurationWebClientTests.java index bd3ed87e0..98a825090 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfigurationWebClientTests.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfigurationWebClientTests.java @@ -22,6 +22,7 @@ import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.cloud.config.client.ConfigServerInstanceProvider; import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean; +import org.springframework.cloud.netflix.eureka.http.RestTemplateEurekaHttpClient; import org.springframework.cloud.netflix.eureka.http.WebClientEurekaHttpClient; import static org.assertj.core.api.Assertions.assertThat; @@ -36,7 +37,8 @@ public class EurekaConfigServerBootstrapConfigurationWebClientTests { new ApplicationContextRunner() .withConfiguration(AutoConfigurations .of(EurekaConfigServerBootstrapConfiguration.class)) - .withPropertyValues("spring.cloud.config.discovery.enabled=true") + .withPropertyValues("spring.cloud.config.discovery.enabled=true", + "eureka.client.webclient.enabled=true") .run(context -> { assertThat(context).hasSingleBean(EurekaClientConfigBean.class); assertThat(context).hasSingleBean(WebClientEurekaHttpClient.class); @@ -45,4 +47,19 @@ public class EurekaConfigServerBootstrapConfigurationWebClientTests { }); } + @Test + public void properBeansCreatedWhenEnabledWebClientDisabled() { + new ApplicationContextRunner() + .withConfiguration(AutoConfigurations + .of(EurekaConfigServerBootstrapConfiguration.class)) + .withPropertyValues("spring.cloud.config.discovery.enabled=true") + .run(context -> { + assertThat(context).hasSingleBean(EurekaClientConfigBean.class); + assertThat(context).doesNotHaveBean(WebClientEurekaHttpClient.class); + assertThat(context).hasSingleBean(RestTemplateEurekaHttpClient.class); + assertThat(context) + .hasSingleBean(ConfigServerInstanceProvider.Function.class); + }); + } + } diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/loadbalancer/EurekaLoadBalancerClientConfigurationTests.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/loadbalancer/EurekaLoadBalancerClientConfigurationTests.java index eeb31cbd3..3cc9eea23 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/loadbalancer/EurekaLoadBalancerClientConfigurationTests.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/loadbalancer/EurekaLoadBalancerClientConfigurationTests.java @@ -18,6 +18,8 @@ package org.springframework.cloud.netflix.eureka.loadbalancer; import org.junit.jupiter.api.Test; +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; import org.springframework.cloud.commons.util.InetUtils; import org.springframework.cloud.commons.util.InetUtilsProperties; import org.springframework.cloud.loadbalancer.config.LoadBalancerZoneConfig; @@ -72,4 +74,16 @@ class EurekaLoadBalancerClientConfigurationTests { assertThat(zoneConfig.getZone()).isEqualTo("is.a.test.com"); } + @Test + public void disabledViaProperty() { + new ApplicationContextRunner() + .withConfiguration( + AutoConfigurations.of(LoadBalancerEurekaAutoConfiguration.class)) + .withPropertyValues("eureka.client.enabled=false").run(context -> { + assertThat(context) + .doesNotHaveBean(EurekaLoadBalancerProperties.class); + assertThat(context).doesNotHaveBean(LoadBalancerZoneConfig.class); + }); + } + }