diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java index 08eee2a0d2..db8d1ec461 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/config/DiscoveryClientOptionalArgsConfiguration.java @@ -20,6 +20,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; @@ -30,6 +31,7 @@ import org.springframework.cloud.netflix.eureka.http.WebClientDiscoveryClientOptionalArgs; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.web.reactive.function.client.WebClient; /** * @author Daniel Lavoie @@ -37,7 +39,8 @@ @Configuration(proxyBeanMethods = false) public class DiscoveryClientOptionalArgsConfiguration { - protected final Log logger = LogFactory.getLog(getClass()); + protected static final Log logger = LogFactory + .getLog(DiscoveryClientOptionalArgsConfiguration.class); @Bean @ConditionalOnClass(name = "org.springframework.web.client.RestTemplate") @@ -51,21 +54,6 @@ public RestTemplateDiscoveryClientOptionalArgs restTemplateDiscoveryClientOption return new RestTemplateDiscoveryClientOptionalArgs(); } - @Bean - @ConditionalOnMissingClass("com.sun.jersey.api.client.filter.ClientFilter") - @ConditionalOnClass( - name = "org.springframework.web.reactive.function.client.WebClient") - @ConditionalOnMissingBean( - value = { AbstractDiscoveryClientOptionalArgs.class, - RestTemplateDiscoveryClientOptionalArgs.class }, - search = SearchStrategy.CURRENT) - @ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", - havingValue = "true") - public WebClientDiscoveryClientOptionalArgs webClientDiscoveryClientOptionalArgs() { - logger.info("Eureka HTTP Client uses WebClient."); - return new WebClientDiscoveryClientOptionalArgs(); - } - @Bean @ConditionalOnClass(name = "com.sun.jersey.api.client.filter.ClientFilter") @ConditionalOnMissingBean(value = AbstractDiscoveryClientOptionalArgs.class, @@ -75,6 +63,26 @@ public MutableDiscoveryClientOptionalArgs discoveryClientOptionalArgs() { return new MutableDiscoveryClientOptionalArgs(); } + @ConditionalOnMissingClass("com.sun.jersey.api.client.filter.ClientFilter") + @ConditionalOnClass( + name = "org.springframework.web.reactive.function.client.WebClient") + @ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", + havingValue = "true") + protected static class WebClientConfiguration { + + @Bean + @ConditionalOnMissingBean( + value = { AbstractDiscoveryClientOptionalArgs.class, + RestTemplateDiscoveryClientOptionalArgs.class }, + search = SearchStrategy.CURRENT) + public WebClientDiscoveryClientOptionalArgs webClientDiscoveryClientOptionalArgs( + ObjectProvider builder) { + logger.info("Eureka HTTP Client uses WebClient."); + return new WebClientDiscoveryClientOptionalArgs(builder::getIfAvailable); + } + + } + @Configuration @ConditionalOnMissingClass({ "com.sun.jersey.api.client.filter.ClientFilter", "org.springframework.web.reactive.function.client.WebClient" }) 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 bced10eb3a..f2e6f40183 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 @@ -29,10 +29,14 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.ImportAutoConfiguration; 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.autoconfigure.condition.SearchStrategy; +import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration; +import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.config.client.ConfigServerInstanceProvider; @@ -48,6 +52,7 @@ import org.springframework.core.env.Environment; import org.springframework.http.HttpStatus; import org.springframework.util.StringUtils; +import org.springframework.web.reactive.function.client.WebClient; /** * Bootstrap configuration for config client that wants to lookup the config server via @@ -72,18 +77,6 @@ public EurekaClientConfigBean eurekaClientConfigBean() { return new EurekaClientConfigBean(); } - @Bean - @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, Environment env) { - return (WebClientEurekaHttpClient) new WebClientTransportClientFactory() - .newClient(new DefaultEndpoint(getEurekaUrl(config, env))); - } - @Bean @ConditionalOnMissingBean(EurekaHttpClient.class) @ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", @@ -94,7 +87,7 @@ public RestTemplateEurekaHttpClient configDiscoveryRestTemplateEurekaHttpClient( .newClient(new DefaultEndpoint(getEurekaUrl(config, env))); } - private String getEurekaUrl(EurekaClientConfigBean config, Environment env) { + private static String getEurekaUrl(EurekaClientConfigBean config, Environment env) { List urls = EndpointUtils.getDiscoveryServiceUrls(config, EurekaClientConfigBean.DEFAULT_ZONE, new HostnameBasedUrlRandomizer( env.getProperty("eureka.instance.hostname"))); @@ -170,4 +163,25 @@ public void randomize(List urlList) { } + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass( + name = "org.springframework.web.reactive.function.client.WebClient") + @ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled", + havingValue = "true") + @ImportAutoConfiguration({ CodecsAutoConfiguration.class, + WebClientAutoConfiguration.class }) + protected static class WebClientConfiguration { + + @Bean + @ConditionalOnMissingBean(EurekaHttpClient.class) + public WebClientEurekaHttpClient configDiscoveryWebClientEurekaHttpClient( + EurekaClientConfigBean config, ObjectProvider builder, + Environment env) { + return (WebClientEurekaHttpClient) new WebClientTransportClientFactory( + builder::getIfAvailable) + .newClient(new DefaultEndpoint(getEurekaUrl(config, env))); + } + + } + } diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientDiscoveryClientOptionalArgs.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientDiscoveryClientOptionalArgs.java index 19d30ba1e4..90680daaf9 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientDiscoveryClientOptionalArgs.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientDiscoveryClientOptionalArgs.java @@ -16,8 +16,12 @@ package org.springframework.cloud.netflix.eureka.http; +import java.util.function.Supplier; + import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs; +import org.springframework.web.reactive.function.client.WebClient; + /** * @author Daniel Lavoie * @author Haytham Mohamed @@ -25,8 +29,13 @@ public class WebClientDiscoveryClientOptionalArgs extends AbstractDiscoveryClientOptionalArgs { + @Deprecated public WebClientDiscoveryClientOptionalArgs() { - setTransportClientFactories(new WebClientTransportClientFactories()); + this(WebClient::builder); + } + + public WebClientDiscoveryClientOptionalArgs(Supplier builder) { + setTransportClientFactories(new WebClientTransportClientFactories(builder)); } } diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientEurekaHttpClient.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientEurekaHttpClient.java index 397e949ac1..dd7b6785de 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientEurekaHttpClient.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientEurekaHttpClient.java @@ -209,6 +209,10 @@ public void shutdown() { // Nothing to do } + public WebClient getWebClient() { + return this.webClient; + } + private static Map headersOf(ClientResponse response) { ClientResponse.Headers httpHeaders = response.headers(); if (httpHeaders == null) { diff --git a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientTransportClientFactories.java b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientTransportClientFactories.java index d35ce4ec7f..22cc2d1d05 100644 --- a/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientTransportClientFactories.java +++ b/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/http/WebClientTransportClientFactories.java @@ -18,6 +18,7 @@ import java.util.Collection; import java.util.Optional; +import java.util.function.Supplier; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.SSLContext; @@ -28,12 +29,20 @@ import com.netflix.discovery.shared.transport.jersey.EurekaJerseyClient; import com.netflix.discovery.shared.transport.jersey.TransportClientFactories; +import org.springframework.web.reactive.function.client.WebClient; + /** * @author Daniel Lavoie * @author Haytham Mohamed */ public class WebClientTransportClientFactories implements TransportClientFactories { + private final Supplier builder; + + public WebClientTransportClientFactories(Supplier builder) { + this.builder = builder; + } + @Override public TransportClientFactory newTransportClientFactory( Collection additionalFilters, EurekaJerseyClient providedJerseyClient) { @@ -44,7 +53,7 @@ public TransportClientFactory newTransportClientFactory( public TransportClientFactory newTransportClientFactory( EurekaClientConfig clientConfig, Collection additionalFilters, InstanceInfo myInstanceInfo) { - return new WebClientTransportClientFactory(); + return new WebClientTransportClientFactory(builder); } @Override @@ -53,7 +62,7 @@ public TransportClientFactory newTransportClientFactory( final Collection additionalFilters, final InstanceInfo myInstanceInfo, final Optional sslContext, final Optional hostnameVerifier) { - return new WebClientTransportClientFactory(); + return new WebClientTransportClientFactory(builder); } } 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 fa6de993a6..95f49f3460 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 @@ -18,6 +18,7 @@ import java.net.URI; import java.net.URISyntaxException; +import java.util.function.Supplier; import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -41,13 +42,13 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.http.codec.ClientCodecConfigurer; import org.springframework.http.codec.json.Jackson2JsonDecoder; import org.springframework.http.codec.json.Jackson2JsonEncoder; import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.ExchangeFilterFunction; import org.springframework.web.reactive.function.client.ExchangeFilterFunctions; -import org.springframework.web.reactive.function.client.ExchangeStrategies; import org.springframework.web.reactive.function.client.WebClient; /** @@ -60,17 +61,29 @@ */ public class WebClientTransportClientFactory implements TransportClientFactory { + private final Supplier builderSupplier; + + @Deprecated + public WebClientTransportClientFactory() { + this(WebClient::builder); + } + + public WebClientTransportClientFactory(Supplier builderSupplier) { + this.builderSupplier = builderSupplier; + } + @Override - public EurekaHttpClient newClient(EurekaEndpoint serviceUrl) { - WebClient.Builder builder = of(serviceUrl.getServiceUrl()); - this.setExchangeStrategies(builder); - this.skipHttp400Error(builder); + public EurekaHttpClient newClient(EurekaEndpoint endpoint) { + // we want a copy to modify. Don't change the original + WebClient.Builder builder = this.builderSupplier.get().clone(); + setUrl(builder, endpoint.getServiceUrl()); + setCodecs(builder); + builder.filter(http4XxErrorExchangeFilterFunction()); return new WebClientEurekaHttpClient(builder.build()); } - private WebClient.Builder of(String serviceUrl) { + private WebClient.Builder setUrl(WebClient.Builder builder, String serviceUrl) { String url = serviceUrl; - WebClient.Builder builder = WebClient.builder(); try { URI serviceURI = new URI(serviceUrl); if (serviceURI.getUserInfo() != null) { @@ -88,23 +101,17 @@ private WebClient.Builder of(String serviceUrl) { return builder.baseUrl(url); } - private void setExchangeStrategies(WebClient.Builder builder) { + private void setCodecs(WebClient.Builder builder) { ObjectMapper objectMapper = objectMapper(); - ExchangeStrategies strategies = ExchangeStrategies.builder() - .codecs(clientDefaultCodecsConfigurer -> { - clientDefaultCodecsConfigurer.defaultCodecs() - .jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper, - MediaType.APPLICATION_JSON)); - clientDefaultCodecsConfigurer.defaultCodecs() - .jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper, - MediaType.APPLICATION_JSON)); - - }).build(); - builder.exchangeStrategies(strategies); - } + builder.codecs(configurer -> { + ClientCodecConfigurer.ClientDefaultCodecs defaults = configurer + .defaultCodecs(); + defaults.jackson2JsonEncoder( + new Jackson2JsonEncoder(objectMapper, MediaType.APPLICATION_JSON)); + defaults.jackson2JsonDecoder( + new Jackson2JsonDecoder(objectMapper, MediaType.APPLICATION_JSON)); - private void skipHttp400Error(WebClient.Builder builder) { - builder.filter(http4XxErrorExchangeFilterFunction()); + }); } // Skip over 4xx http errors @@ -114,9 +121,8 @@ private ExchangeFilterFunction http4XxErrorExchangeFilterFunction() { if (clientResponse.statusCode().value() == 400) { ClientResponse newResponse = ClientResponse.from(clientResponse) .statusCode(HttpStatus.OK).build(); - newResponse.body((clientHttpResponse, context) -> { - return clientHttpResponse.getBody(); - }); + newResponse.body( + (clientHttpResponse, context) -> clientHttpResponse.getBody()); return Mono.just(newResponse); } return Mono.just(clientResponse); diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfigurationWebClientIntegrationTests.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfigurationWebClientIntegrationTests.java new file mode 100644 index 0000000000..17179473ce --- /dev/null +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/config/EurekaConfigServerBootstrapConfigurationWebClientIntegrationTests.java @@ -0,0 +1,84 @@ +/* + * Copyright 2013-2020 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.netflix.eureka.config; + +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.web.server.LocalServerPort; +import org.springframework.cloud.netflix.eureka.http.WebClientEurekaHttpClient; +import org.springframework.http.HttpStatus; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.reactive.function.client.ClientResponse; +import org.springframework.web.reactive.function.client.WebClient; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +/** + * @author Spencer Gibb + */ +@SpringBootTest(properties = { "spring.cloud.config.discovery.enabled=true", + "eureka.client.webclient.enabled=true", + "spring.codec.max-in-memory-size=310000" }, webEnvironment = RANDOM_PORT) +public class EurekaConfigServerBootstrapConfigurationWebClientIntegrationTests { + + @LocalServerPort + private int port; + + @Autowired + private WebClientEurekaHttpClient eurekaHttpClient; + + @Test + public void webClientRespectsCodecProperties() { + WebClient webClient = eurekaHttpClient.getWebClient(); + ClientResponse response = webClient.get().uri("http://localhost:" + port) + .exchange().block(); + assertThat(response).isNotNull(); + assertThat(response.statusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.bodyToMono(String.class).block()).startsWith("....") + .hasSize(300000); + } + + @SpringBootConfiguration + @EnableAutoConfiguration + @RestController + static class WebClientController extends WebSecurityConfigurerAdapter { + + @GetMapping + public String hello() { + StringBuilder s = new StringBuilder(); + for (int i = 0; i < 300000; i++) { + s.append("."); + } + return s.toString(); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests().anyRequest().permitAll().and().csrf().disable(); + } + + } + +} diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/WebClientEurekaHttpClientTest.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/WebClientEurekaHttpClientTest.java index baaa2239a6..5f356cca7e 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/WebClientEurekaHttpClientTest.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/WebClientEurekaHttpClientTest.java @@ -29,6 +29,7 @@ import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.web.reactive.function.client.WebClient; /** * @author Daniel Lavoie @@ -49,7 +50,7 @@ public class WebClientEurekaHttpClientTest extends AbstractEurekaHttpClientTest @Before public void setup() { - eurekaHttpClient = new WebClientTransportClientFactory() + eurekaHttpClient = new WebClientTransportClientFactory(WebClient::builder) .newClient(new DefaultEndpoint(serviceUrl)); EurekaInstanceConfigBean config = new EurekaInstanceConfigBean(inetUtils); diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/WebClientTransportClientFactoriesTest.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/WebClientTransportClientFactoriesTest.java index 234b0fed08..21437dbb0e 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/WebClientTransportClientFactoriesTest.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/WebClientTransportClientFactoriesTest.java @@ -18,6 +18,8 @@ import org.junit.Test; +import org.springframework.web.reactive.function.client.WebClient; + /** * @author Daniel Lavoie */ @@ -25,7 +27,8 @@ public class WebClientTransportClientFactoriesTest { @Test(expected = UnsupportedOperationException.class) public void testJerseyIsUnsuported() { - new WebClientTransportClientFactories().newTransportClientFactory(null, null); + new WebClientTransportClientFactories(WebClient::builder) + .newTransportClientFactory(null, null); } } diff --git a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/WebClientTransportClientFactoryTest.java b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/WebClientTransportClientFactoryTest.java index 949d4085f2..24363f8a77 100644 --- a/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/WebClientTransportClientFactoryTest.java +++ b/spring-cloud-netflix-eureka-client/src/test/java/org/springframework/cloud/netflix/eureka/http/WebClientTransportClientFactoryTest.java @@ -21,6 +21,8 @@ import org.junit.Before; import org.junit.Test; +import org.springframework.web.reactive.function.client.WebClient; + /** * @author Daniel Lavoie */ @@ -30,7 +32,7 @@ public class WebClientTransportClientFactoryTest { @Before public void setup() { - transportClientFatory = new WebClientTransportClientFactory(); + transportClientFatory = new WebClientTransportClientFactory(WebClient::builder); } @Test