From a14d6449e8eafa3da13e8683cdba1836e7c4502f Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Wed, 31 Jul 2024 17:04:56 +0200 Subject: [PATCH 1/9] Reapply "Resolve bean post processor warnings (#1361)" This reverts commit 246bc321c748c76b8f222f7201cc5e5dae487237. --- .../loadbalancer/LoadBalancerAutoConfiguration.java | 8 +++++--- ...oadBalancerBeanPostProcessorAutoConfiguration.java | 11 +++++++---- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerAutoConfiguration.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerAutoConfiguration.java index a2cb39ce..2a1101a6 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerAutoConfiguration.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerAutoConfiguration.java @@ -37,6 +37,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.retry.support.RetryTemplate; import org.springframework.web.client.RestTemplate; @@ -49,6 +50,7 @@ import org.springframework.web.client.RestTemplate; * @author Will Tran * @author Gang Li * @author Olga Maciaszek-Sharma + * @author Henning Pöttker */ @AutoConfiguration @Conditional(BlockingRestClassesPresentCondition.class) @@ -86,7 +88,7 @@ public class LoadBalancerAutoConfiguration { @Bean @ConditionalOnMissingBean - public DeferringLoadBalancerInterceptor deferringLoadBalancerInterceptor( + public static DeferringLoadBalancerInterceptor deferringLoadBalancerInterceptor( ObjectProvider loadBalancerInterceptorObjectProvider) { return new DeferringLoadBalancerInterceptor(loadBalancerInterceptorObjectProvider); } @@ -94,8 +96,8 @@ public class LoadBalancerAutoConfiguration { @Bean @ConditionalOnBean(DeferringLoadBalancerInterceptor.class) @ConditionalOnMissingBean - LoadBalancerRestClientBuilderBeanPostProcessor lbRestClientPostProcessor( - DeferringLoadBalancerInterceptor loadBalancerInterceptor, ApplicationContext context) { + static LoadBalancerRestClientBuilderBeanPostProcessor lbRestClientPostProcessor( + @Lazy DeferringLoadBalancerInterceptor loadBalancerInterceptor, ApplicationContext context) { return new LoadBalancerRestClientBuilderBeanPostProcessor(loadBalancerInterceptor, context); } diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerBeanPostProcessorAutoConfiguration.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerBeanPostProcessorAutoConfiguration.java index 8e61f33c..3f61c373 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerBeanPostProcessorAutoConfiguration.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerBeanPostProcessorAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2024 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. @@ -27,6 +27,7 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Primary; import org.springframework.web.reactive.function.client.WebClient; @@ -39,6 +40,7 @@ import org.springframework.web.reactive.function.client.WebClient; * beans. * * @author Olga Maciaszek-Sharma + * @author Henning Pöttker * @since 2.2.0 */ @Configuration(proxyBeanMethods = false) @@ -47,8 +49,9 @@ import org.springframework.web.reactive.function.client.WebClient; public class LoadBalancerBeanPostProcessorAutoConfiguration { @Bean - public LoadBalancerWebClientBuilderBeanPostProcessor loadBalancerWebClientBuilderBeanPostProcessor( - DeferringLoadBalancerExchangeFilterFunction deferringExchangeFilterFunction, ApplicationContext context) { + public static LoadBalancerWebClientBuilderBeanPostProcessor loadBalancerWebClientBuilderBeanPostProcessor( + @Lazy DeferringLoadBalancerExchangeFilterFunction deferringExchangeFilterFunction, + ApplicationContext context) { return new LoadBalancerWebClientBuilderBeanPostProcessor(deferringExchangeFilterFunction, context); } @@ -58,7 +61,7 @@ public class LoadBalancerBeanPostProcessorAutoConfiguration { @Bean @Primary - DeferringLoadBalancerExchangeFilterFunction reactorDeferringLoadBalancerExchangeFilterFunction( + static DeferringLoadBalancerExchangeFilterFunction reactorDeferringLoadBalancerExchangeFilterFunction( ObjectProvider exchangeFilterFunctionProvider) { return new DeferringLoadBalancerExchangeFilterFunction<>(exchangeFilterFunctionProvider); } From f92bdf7df5d8523e71039519ae4bc00f617ffdd0 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Wed, 31 Jul 2024 17:05:27 +0200 Subject: [PATCH 2/9] Reapply "Use ObjectProvider for DeferringLoadBalancerExchangeFilterFunction." This reverts commit c1b08f8b9bcb9a762218b78b9416f6b7ab3fe31a. --- .../loadbalancer/SimpleObjectProvider.java | 60 +++++++++++++++++++ ...ingLoadBalancerExchangeFilterFunction.java | 4 +- ...cerBeanPostProcessorAutoConfiguration.java | 4 +- ...ncerWebClientBuilderBeanPostProcessor.java | 28 +++++++-- 4 files changed, 88 insertions(+), 8 deletions(-) create mode 100644 spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/SimpleObjectProvider.java diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/SimpleObjectProvider.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/SimpleObjectProvider.java new file mode 100644 index 00000000..b12ce977 --- /dev/null +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/SimpleObjectProvider.java @@ -0,0 +1,60 @@ +/* + * Copyright 2012-2024 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.client.loadbalancer; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerWebClientBuilderBeanPostProcessor; + +/** + * Wrapper for {@link ObjectProvider}. Added to use for a workaround in + * {@link LoadBalancerWebClientBuilderBeanPostProcessor}. + * + * @param type of the object to fetch + * @author Spencer Gibb + * @deprecated for removal in 4.0 + */ +@Deprecated(forRemoval = true) +public class SimpleObjectProvider implements ObjectProvider { + + private final T object; + + public SimpleObjectProvider(T object) { + this.object = object; + } + + @Override + public T getObject(Object... args) throws BeansException { + return this.object; + } + + @Override + public T getIfAvailable() throws BeansException { + return this.object; + } + + @Override + public T getIfUnique() throws BeansException { + return this.object; + } + + @Override + public T getObject() throws BeansException { + return this.object; + } + +} diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/DeferringLoadBalancerExchangeFilterFunction.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/DeferringLoadBalancerExchangeFilterFunction.java index 4e419770..d370b981 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/DeferringLoadBalancerExchangeFilterFunction.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/DeferringLoadBalancerExchangeFilterFunction.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2024 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. @@ -55,7 +55,7 @@ public class DeferringLoadBalancerExchangeFilterFunction deferringExchangeFilterFunction, ApplicationContext context) { return new LoadBalancerWebClientBuilderBeanPostProcessor(deferringExchangeFilterFunction, context); } diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerWebClientBuilderBeanPostProcessor.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerWebClientBuilderBeanPostProcessor.java index bb8db3ff..e07f9590 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerWebClientBuilderBeanPostProcessor.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/LoadBalancerWebClientBuilderBeanPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-2024 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. @@ -17,8 +17,10 @@ package org.springframework.cloud.client.loadbalancer.reactive; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.cloud.client.loadbalancer.LoadBalanced; +import org.springframework.cloud.client.loadbalancer.SimpleObjectProvider; import org.springframework.context.ApplicationContext; import org.springframework.web.reactive.function.client.WebClient; @@ -30,15 +32,28 @@ import org.springframework.web.reactive.function.client.WebClient; * @author Olga Maciaszek-Sharma * @since 2.2.0 */ +@SuppressWarnings({ "removal", "rawtypes" }) public class LoadBalancerWebClientBuilderBeanPostProcessor implements BeanPostProcessor { - private final DeferringLoadBalancerExchangeFilterFunction exchangeFilterFunction; + private final ObjectProvider exchangeFilterFunctionObjectProvider; private final ApplicationContext context; + /** + * @deprecated in favour of + * {@link LoadBalancerWebClientBuilderBeanPostProcessor#LoadBalancerWebClientBuilderBeanPostProcessor(ObjectProvider, ApplicationContext)} + */ + @Deprecated(forRemoval = true) public LoadBalancerWebClientBuilderBeanPostProcessor( DeferringLoadBalancerExchangeFilterFunction exchangeFilterFunction, ApplicationContext context) { - this.exchangeFilterFunction = exchangeFilterFunction; + this.exchangeFilterFunctionObjectProvider = new SimpleObjectProvider<>(exchangeFilterFunction); + this.context = context; + } + + public LoadBalancerWebClientBuilderBeanPostProcessor( + ObjectProvider exchangeFilterFunction, + ApplicationContext context) { + this.exchangeFilterFunctionObjectProvider = exchangeFilterFunction; this.context = context; } @@ -48,7 +63,12 @@ public class LoadBalancerWebClientBuilderBeanPostProcessor implements BeanPostPr if (context.findAnnotationOnBean(beanName, LoadBalanced.class) == null) { return bean; } - ((WebClient.Builder) bean).filter(exchangeFilterFunction); + DeferringLoadBalancerExchangeFilterFunction exchangeFilterFunction = exchangeFilterFunctionObjectProvider + .getIfAvailable(); + if (exchangeFilterFunction == null) { + throw new IllegalStateException("LoadBalancerExchangeFilterFunction not found"); + } + ((WebClient.Builder) bean).filter(exchangeFilterFunctionObjectProvider.getIfAvailable()); } return bean; } From 815f34e954fa254a29d870982922563824d50e06 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Mar 2025 15:41:57 -0400 Subject: [PATCH 3/9] Bump @springio/asciidoctor-extensions in /docs (#1482) Bumps [@springio/asciidoctor-extensions](https://github.com/spring-io/asciidoctor-extensions) from 1.0.0-alpha.16 to 1.0.0-alpha.17. - [Changelog](https://github.com/spring-io/asciidoctor-extensions/blob/main/CHANGELOG.adoc) - [Commits](https://github.com/spring-io/asciidoctor-extensions/compare/v1.0.0-alpha.16...v1.0.0-alpha.17) --- updated-dependencies: - dependency-name: "@springio/asciidoctor-extensions" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- docs/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/package.json b/docs/package.json index 9e455a6d..45cdd10b 100644 --- a/docs/package.json +++ b/docs/package.json @@ -5,6 +5,6 @@ "@antora/collector-extension": "1.0.1", "@asciidoctor/tabs": "1.0.0-beta.6", "@springio/antora-extensions": "1.14.4", - "@springio/asciidoctor-extensions": "1.0.0-alpha.16" + "@springio/asciidoctor-extensions": "1.0.0-alpha.17" } } From 10ed7213ecb0210f4ef8cb86e94fcf80b3f5a104 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Fri, 11 Apr 2025 15:50:10 +0200 Subject: [PATCH 4/9] Fix checkstyle. Revert debug changes. Refactor. Signed-off-by: Olga Maciaszek-Sharma --- .../loadbalancer/LoadBalancerUriTools.java | 2 +- .../client/loadbalancer/RequestData.java | 1 - .../RetryLoadBalancerInterceptorTests.java | 16 +- .../RefreshScopeIntegrationTests.java | 62 ++--- .../config/BootstrapConfigurationTests.java | 239 +++++++++--------- .../encrypt/EncryptorFactoryTests.java | 7 +- .../refresh/RefreshScopeIntegrationTests.java | 62 ++--- ...rviceInstanceListSupplierBuilderTests.java | 4 +- 8 files changed, 197 insertions(+), 196 deletions(-) diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerUriTools.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerUriTools.java index 7715875d..c23d0f29 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerUriTools.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerUriTools.java @@ -56,7 +56,7 @@ public final class LoadBalancerUriTools { boolean encoded = (uri.getRawQuery() != null && uri.getRawQuery().contains(PERCENTAGE_SIGN)) || (uri.getRawPath() != null && uri.getRawPath().contains(PERCENTAGE_SIGN)) || (uri.getRawFragment() != null && uri.getRawFragment().contains(PERCENTAGE_SIGN)); - // Verify if it is really fully encoded. Treat partial encoded as unencoded. + // Verify if it is real ly fully encoded. Treat partial encoded as unencoded. if (encoded) { try { UriComponentsBuilder.fromUri(uri).build(true); diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/RequestData.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/RequestData.java index 8c320190..94821a70 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/RequestData.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/RequestData.java @@ -19,7 +19,6 @@ package org.springframework.cloud.client.loadbalancer; import java.net.URI; import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Objects; diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/RetryLoadBalancerInterceptorTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/RetryLoadBalancerInterceptorTests.java index 62205271..3f3f2936 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/RetryLoadBalancerInterceptorTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/RetryLoadBalancerInterceptorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-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. @@ -29,7 +29,6 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -56,6 +55,9 @@ import org.springframework.retry.backoff.NoBackOffPolicy; import org.springframework.retry.listener.RetryListenerSupport; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIOException; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; import static org.assertj.core.api.BDDAssertions.then; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -117,7 +119,7 @@ public class RetryLoadBalancerInterceptorTests { when(lbRequestFactory.createRequest(any(), any(), any())).thenReturn(mock(LoadBalancerRequest.class)); - Assertions.assertThrows(IOException.class, () -> interceptor.intercept(request, body, execution)); + assertThatIOException().isThrownBy(() -> interceptor.intercept(request, body, execution)); verify(lbRequestFactory).createRequest(request, body, execution); } @@ -130,7 +132,7 @@ public class RetryLoadBalancerInterceptorTests { loadBalancedRetryFactory, lbFactory); byte[] body = new byte[] {}; ClientHttpRequestExecution execution = mock(ClientHttpRequestExecution.class); - Assertions.assertThrows(IllegalStateException.class, () -> interceptor.intercept(request, body, execution)); + assertThatIllegalStateException().isThrownBy(() -> interceptor.intercept(request, body, execution)); } @Test @@ -288,7 +290,7 @@ public class RetryLoadBalancerInterceptorTests { new MyLoadBalancedRetryFactory(policy), lbFactory); byte[] body = new byte[] {}; ClientHttpRequestExecution execution = mock(ClientHttpRequestExecution.class); - Assertions.assertThrows(IOException.class, () -> interceptor.intercept(request, body, execution)); + assertThatIOException().isThrownBy(() -> interceptor.intercept(request, body, execution)); verify(lbRequestFactory).createRequest(request, body, execution); } @@ -370,8 +372,8 @@ public class RetryLoadBalancerInterceptorTests { new MyLoadBalancedRetryFactory(policy, backOffPolicy, new RetryListener[] { myRetryListener }), lbFactory); ClientHttpRequestExecution execution = mock(ClientHttpRequestExecution.class); - Assertions.assertThrows(TerminatedRetryException.class, - () -> interceptor.intercept(request, new byte[] {}, execution)); + assertThatExceptionOfType(TerminatedRetryException.class) + .isThrownBy(() -> interceptor.intercept(request, new byte[] {}, execution)); } @Test diff --git a/spring-cloud-context-integration-tests/src/test/java/org/springframework/cloud/context/integration/RefreshScopeIntegrationTests.java b/spring-cloud-context-integration-tests/src/test/java/org/springframework/cloud/context/integration/RefreshScopeIntegrationTests.java index 57cc81f3..fa6fb5e7 100644 --- a/spring-cloud-context-integration-tests/src/test/java/org/springframework/cloud/context/integration/RefreshScopeIntegrationTests.java +++ b/spring-cloud-context-integration-tests/src/test/java/org/springframework/cloud/context/integration/RefreshScopeIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2020 the original author or authors. + * 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. @@ -19,7 +19,6 @@ package org.springframework.cloud.context.integration; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -42,6 +41,7 @@ import org.springframework.jmx.export.annotation.ManagedAttribute; import org.springframework.jmx.export.annotation.ManagedResource; import org.springframework.test.annotation.DirtiesContext; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.BDDAssertions.then; @SpringBootTest(classes = TestConfiguration.class) @@ -71,12 +71,12 @@ public class RefreshScopeIntegrationTests { @Test @DirtiesContext public void testSimpleProperties() { - then(this.service.getMessage()).isEqualTo("Hello scope!"); - then(this.service instanceof Advised).isTrue(); + then(service.getMessage()).isEqualTo("Hello scope!"); + then(service instanceof Advised).isTrue(); // Change the dynamic property source... - this.properties.setMessage("Foo"); + properties.setMessage("Foo"); // ...but don't refresh, so the bean stays the same: - then(this.service.getMessage()).isEqualTo("Hello scope!"); + then(service.getMessage()).isEqualTo("Hello scope!"); then(ExampleService.getInitCount()).isEqualTo(0); then(ExampleService.getDestroyCount()).isEqualTo(0); } @@ -84,14 +84,14 @@ public class RefreshScopeIntegrationTests { @Test @DirtiesContext public void testRefresh() { - then(this.service.getMessage()).isEqualTo("Hello scope!"); - String id1 = this.service.toString(); + then(service.getMessage()).isEqualTo("Hello scope!"); + String id1 = service.toString(); // Change the dynamic property source... - this.properties.setMessage("Foo"); + properties.setMessage("Foo"); // ...and then refresh, so the bean is re-initialized: - this.scope.refreshAll(); - String id2 = this.service.toString(); - then(this.service.getMessage()).isEqualTo("Foo"); + scope.refreshAll(); + String id2 = service.toString(); + then(service.getMessage()).isEqualTo("Foo"); then(ExampleService.getInitCount()).isEqualTo(1); then(ExampleService.getDestroyCount()).isEqualTo(1); then(id2).isNotSameAs(id1); @@ -102,15 +102,15 @@ public class RefreshScopeIntegrationTests { @Test @DirtiesContext public void testRefreshBean() { - then(this.service.getMessage()).isEqualTo("Hello scope!"); - String id1 = this.service.toString(); + then(service.getMessage()).isEqualTo("Hello scope!"); + String id1 = service.toString(); // Change the dynamic property source... - this.properties.setMessage("Foo"); + properties.setMessage("Foo"); // ...and then refresh, so the bean is re-initialized: - this.scope.refresh("service"); - String id2 = this.service.toString(); - then(this.service.getMessage()).isEqualTo("Foo"); - then(this.service.getMessage()).isEqualTo("Foo"); + scope.refresh("service"); + String id2 = service.toString(); + then(service.getMessage()).isEqualTo("Foo"); + then(service.getMessage()).isEqualTo("Foo"); then(ExampleService.getInitCount()).isEqualTo(1); then(ExampleService.getDestroyCount()).isEqualTo(1); then(id2).isNotSameAs(id1); @@ -122,7 +122,7 @@ public class RefreshScopeIntegrationTests { @Test @DirtiesContext public void testCheckedException() { - Assertions.assertThrows(ServiceException.class, () -> this.service.throwsException()); + assertThatExceptionOfType(ServiceException.class).isThrownBy(() -> service.throwsException()); } public interface Service { @@ -168,28 +168,28 @@ public class RefreshScopeIntegrationTests { @Override public void afterPropertiesSet() { - logger.debug("Initializing message: " + this.message); + logger.debug("Initializing message: " + message); initCount++; } @Override public void destroy() { - logger.debug("Destroying message: " + this.message); + logger.debug("Destroying message: " + message); destroyCount++; - this.message = null; + message = null; } @Override public String getMessage() { - logger.debug("Getting message: " + this.message); + logger.debug("Getting message: " + message); try { - Thread.sleep(this.delay); + Thread.sleep(delay); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } - logger.info("Returning message: " + this.message); - return this.message; + logger.info("Returning message: " + message); + return message; } public void setMessage(String message) { @@ -226,8 +226,8 @@ public class RefreshScopeIntegrationTests { @RefreshScope public ExampleService service() { ExampleService service = new ExampleService(); - service.setMessage(this.properties.getMessage()); - service.setDelay(this.properties.getDelay()); + service.setMessage(properties.getMessage()); + service.setDelay(properties.getDelay()); return service; } @@ -243,7 +243,7 @@ public class RefreshScopeIntegrationTests { @ManagedAttribute public String getMessage() { - return this.message; + return message; } public void setMessage(String message) { @@ -252,7 +252,7 @@ public class RefreshScopeIntegrationTests { @ManagedAttribute public int getDelay() { - return this.delay; + return delay; } public void setDelay(int delay) { diff --git a/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/config/BootstrapConfigurationTests.java b/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/config/BootstrapConfigurationTests.java index a9ac9132..8a81f7d2 100644 --- a/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/config/BootstrapConfigurationTests.java +++ b/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/config/BootstrapConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-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. @@ -22,7 +22,6 @@ import java.util.List; import java.util.Map; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -45,11 +44,12 @@ import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySource; import org.springframework.core.env.StandardEnvironment; +import static org.assertj.core.api.Assertions.assertThatRuntimeException; import static org.assertj.core.api.BDDAssertions.then; /** * @author Dave Syer - * + * @author Olga Maciaszek-Sharma */ public class BootstrapConfigurationTests { @@ -67,11 +67,11 @@ public class BootstrapConfigurationTests { PropertySourceConfiguration.MAP.clear(); CompositePropertySourceConfiguration.MAP1.clear(); CompositePropertySourceConfiguration.MAP2.clear(); - if (this.context != null) { - this.context.close(); + if (context != null) { + context.close(); } - if (this.sibling != null) { - this.sibling.close(); + if (sibling != null) { + sibling.close(); } } @@ -90,13 +90,13 @@ public class BootstrapConfigurationTests { } private void pickupOnlyExternalBootstrapProperties(String... properties) { - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .sources(BareConfiguration.class) .properties(properties) .run(); - then(this.context.getEnvironment().getProperty("info.name")).isEqualTo("externalPropertiesInfoName"); - then(this.context.getEnvironment().getProperty("info.desc")).isNull(); - then(this.context.getEnvironment() + then(context.getEnvironment().getProperty("info.name")).isEqualTo("externalPropertiesInfoName"); + then(context.getEnvironment().getProperty("info.desc")).isNull(); + then(context.getEnvironment() .getPropertySources() .contains(PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME + "-testBootstrap")).isTrue(); } @@ -118,13 +118,13 @@ public class BootstrapConfigurationTests { } private void pickupAdditionalExternalBootstrapProperties(String... properties) { - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .sources(BareConfiguration.class) .properties(properties) .run(); - then(this.context.getEnvironment().getProperty("info.name")).isEqualTo("externalPropertiesInfoName"); - then(this.context.getEnvironment().getProperty("info.desc")).isEqualTo("defaultPropertiesInfoDesc"); - then(this.context.getEnvironment() + then(context.getEnvironment().getProperty("info.name")).isEqualTo("externalPropertiesInfoName"); + then(context.getEnvironment().getProperty("info.desc")).isEqualTo("defaultPropertiesInfoDesc"); + then(context.getEnvironment() .getPropertySources() .contains(PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME + "-testBootstrap")).isTrue(); } @@ -141,7 +141,7 @@ public class BootstrapConfigurationTests { } private void bootstrapPropertiesAvailableInInitializer(String... properties) { - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) .initializers(applicationContext -> { @@ -149,7 +149,7 @@ public class BootstrapConfigurationTests { then(applicationContext.getEnvironment().getProperty("info.name")).isEqualTo("child"); }) .run(); - then(this.context.getEnvironment() + then(context.getEnvironment() .getPropertySources() .contains(PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME + "-testBootstrap")).isTrue(); } @@ -177,12 +177,12 @@ public class BootstrapConfigurationTests { private void picksUpAdditionalPropertySource(String... properties) { PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); - then(this.context.getEnvironment() + then(context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); + then(context.getEnvironment() .getPropertySources() .contains(PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME + "-testBootstrap")).isTrue(); } @@ -200,13 +200,12 @@ public class BootstrapConfigurationTests { private void failsOnPropertySource(String... properties) { System.setProperty("expected.fail", "true"); - Throwable throwable = Assertions.assertThrows(RuntimeException.class, () -> { - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + assertThatRuntimeException() + .isThrownBy(() -> context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) - .run(); - }); - then(throwable.getMessage().equals("Planned")); + .run()) + .withMessage("Planned"); } @Test @@ -225,11 +224,11 @@ public class BootstrapConfigurationTests { private void overrideSystemPropertySourceByDefault(String... properties) { PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); System.setProperty("bootstrap.foo", "system"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); + then(context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); } @Test @@ -247,11 +246,11 @@ public class BootstrapConfigurationTests { PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); PropertySourceConfiguration.MAP.put("spring.cloud.config.overrideSystemProperties", "false"); System.setProperty("bootstrap.foo", "system"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("system"); + then(context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("system"); } @Test @@ -273,11 +272,11 @@ public class BootstrapConfigurationTests { // their own remote property source. PropertySourceConfiguration.MAP.put("spring.cloud.config.allowOverride", "false"); System.setProperty("bootstrap.foo", "system"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); + then(context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); } @Test @@ -296,11 +295,11 @@ public class BootstrapConfigurationTests { PropertySourceConfiguration.MAP.put("spring.cloud.config.overrideSystemProperties", "false"); PropertySourceConfiguration.MAP.put("spring.cloud.config.allowOverride", "true"); System.setProperty("bootstrap.foo", "system"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("system"); + then(context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("system"); } @Test @@ -321,12 +320,12 @@ public class BootstrapConfigurationTests { ConfigurableEnvironment environment = new StandardEnvironment(); environment.getPropertySources() .addLast(new MapPropertySource("last", Collections.singletonMap("bootstrap.foo", "splat"))); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .environment(environment) .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("splat"); + then(context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("splat"); } @Test @@ -344,18 +343,18 @@ public class BootstrapConfigurationTests { private void applicationNameInBootstrapAndMain(String... properties) { System.setProperty("expected.name", "main"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().getProperty("spring.application.name")).isEqualTo("app"); + then(context.getEnvironment().getProperty("spring.application.name")).isEqualTo("app"); // The parent is called "main" because spring.application.name is specified in // other.properties (the bootstrap properties) - then(this.context.getParent().getEnvironment().getProperty("spring.application.name")).isEqualTo("main"); + then(context.getParent().getEnvironment().getProperty("spring.application.name")).isEqualTo("main"); // The bootstrap context has the same "bootstrap" property source - then(((ConfigurableEnvironment) this.context.getParent().getEnvironment()).getPropertySources() - .get("bootstrap")).isEqualTo(this.context.getEnvironment().getPropertySources().get("bootstrap")); - then(this.context.getId()).isEqualTo("main-1"); + then(((ConfigurableEnvironment) context.getParent().getEnvironment()).getPropertySources().get("bootstrap")) + .isEqualTo(context.getEnvironment().getPropertySources().get("bootstrap")); + then(context.getId()).isEqualTo("main-1"); } @Test @@ -373,14 +372,14 @@ public class BootstrapConfigurationTests { private void applicationNameNotInBootstrap(String... properties) { System.setProperty("expected.name", "main"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().getProperty("spring.application.name")).isEqualTo("main"); + then(context.getEnvironment().getProperty("spring.application.name")).isEqualTo("main"); // The parent has no name because spring.application.name is not // defined in the bootstrap properties - then(this.context.getParent().getEnvironment().getProperty("spring.application.name")).isEqualTo(null); + then(context.getParent().getEnvironment().getProperty("spring.application.name")).isEqualTo(null); } @Test @@ -396,17 +395,17 @@ public class BootstrapConfigurationTests { private void applicationNameOnlyInBootstrap(String... properties) { System.setProperty("expected.name", "main"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) .run(); // The main context is called "main" because spring.application.name is specified // in other.properties (and not in the main config file) - then(this.context.getEnvironment().getProperty("spring.application.name")).isEqualTo("main"); + then(context.getEnvironment().getProperty("spring.application.name")).isEqualTo("main"); // The parent is called "main" because spring.application.name is specified in // other.properties (the bootstrap properties this time) - then(this.context.getParent().getEnvironment().getProperty("spring.application.name")).isEqualTo("main"); - then(this.context.getId()).isEqualTo("main-1"); + then(context.getParent().getEnvironment().getProperty("spring.application.name")).isEqualTo("main"); + then(context.getId()).isEqualTo("main-1"); } @Test @@ -422,15 +421,15 @@ public class BootstrapConfigurationTests { private void environmentEnrichedOnceWhenSharedWithChildContext(String... properties) { PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); - this.context = new SpringApplicationBuilder().sources(BareConfiguration.class) + context = new SpringApplicationBuilder().sources(BareConfiguration.class) .properties(properties) .environment(new StandardEnvironment()) .child(BareConfiguration.class) .web(WebApplicationType.NONE) .run(); - then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); - then(this.context.getParent().getEnvironment()).isEqualTo(this.context.getEnvironment()); - MutablePropertySources sources = this.context.getEnvironment().getPropertySources(); + then(context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); + then(context.getParent().getEnvironment()).isEqualTo(context.getEnvironment()); + MutablePropertySources sources = context.getEnvironment().getPropertySources(); PropertySource bootstrap = sources .get(PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME + "-testBootstrap"); then(bootstrap).isNotNull(); @@ -451,16 +450,16 @@ public class BootstrapConfigurationTests { private void onlyOneBootstrapContext(String... properties) { TestHigherPriorityBootstrapConfiguration.count.set(0); PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); - this.context = new SpringApplicationBuilder().sources(BareConfiguration.class) + context = new SpringApplicationBuilder().sources(BareConfiguration.class) .properties(properties) .child(BareConfiguration.class) .web(WebApplicationType.NONE) .run(); then(TestHigherPriorityBootstrapConfiguration.count.get()).isEqualTo(1); - then(this.context.getParent()).isNotNull(); - then(this.context.getParent().getParent().getId()).isEqualTo("bootstrap"); - then(this.context.getParent().getParent().getParent()).isNull(); - then(this.context.getEnvironment().getProperty("custom.foo")).isEqualTo("bar"); + then(context.getParent()).isNotNull(); + then(context.getParent().getParent().getId()).isEqualTo("bootstrap"); + then(context.getParent().getParent().getParent()).isNull(); + then(context.getEnvironment().getProperty("custom.foo")).isEqualTo("bar"); } @Test @@ -475,13 +474,13 @@ public class BootstrapConfigurationTests { } private void listOverride(String... properties) { - this.context = new SpringApplicationBuilder().sources(BareConfiguration.class) + context = new SpringApplicationBuilder().sources(BareConfiguration.class) .properties(properties) .child(BareConfiguration.class) .web(WebApplicationType.NONE) .run(); ListProperties listProperties = new ListProperties(); - Binder.get(this.context.getEnvironment()).bind("list", Bindable.ofInstance(listProperties)); + Binder.get(context.getEnvironment()).bind("list", Bindable.ofInstance(listProperties)); then(listProperties.getFoo().size()).isEqualTo(1); then(listProperties.getFoo().get(0)).isEqualTo("hello world"); } @@ -502,25 +501,25 @@ public class BootstrapConfigurationTests { PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); SpringApplicationBuilder builder = new SpringApplicationBuilder().properties(properties) .sources(BareConfiguration.class); - this.sibling = builder.child(BareConfiguration.class) + sibling = builder.child(BareConfiguration.class) .properties("spring.application.name=sibling") .web(WebApplicationType.NONE) .run(); - this.context = builder.child(BareConfiguration.class) + context = builder.child(BareConfiguration.class) .properties("spring.application.name=context") .web(WebApplicationType.NONE) .run(); then(TestHigherPriorityBootstrapConfiguration.count.get()).isEqualTo(1); - then(this.context.getParent()).isNotNull(); - then(this.context.getParent().getParent().getId()).isEqualTo("bootstrap"); - then(this.context.getParent().getParent().getParent()).isNull(); - then(this.context.getEnvironment().getProperty("custom.foo")).isEqualTo("context"); - then(this.context.getEnvironment().getProperty("spring.application.name")).isEqualTo("context"); - then(this.sibling.getParent()).isNotNull(); - then(this.sibling.getParent().getParent().getId()).isEqualTo("bootstrap"); - then(this.sibling.getParent().getParent().getParent()).isNull(); - then(this.sibling.getEnvironment().getProperty("custom.foo")).isEqualTo("sibling"); - then(this.sibling.getEnvironment().getProperty("spring.application.name")).isEqualTo("sibling"); + then(context.getParent()).isNotNull(); + then(context.getParent().getParent().getId()).isEqualTo("bootstrap"); + then(context.getParent().getParent().getParent()).isNull(); + then(context.getEnvironment().getProperty("custom.foo")).isEqualTo("context"); + then(context.getEnvironment().getProperty("spring.application.name")).isEqualTo("context"); + then(sibling.getParent()).isNotNull(); + then(sibling.getParent().getParent().getId()).isEqualTo("bootstrap"); + then(sibling.getParent().getParent().getParent()).isNull(); + then(sibling.getEnvironment().getProperty("custom.foo")).isEqualTo("sibling"); + then(sibling.getEnvironment().getProperty("spring.application.name")).isEqualTo("sibling"); } @Test @@ -536,17 +535,17 @@ public class BootstrapConfigurationTests { private void environmentEnrichedInParentContext(String... properties) { PropertySourceConfiguration.MAP.put("bootstrap.foo", "bar"); - this.context = new SpringApplicationBuilder().sources(BareConfiguration.class) + context = new SpringApplicationBuilder().sources(BareConfiguration.class) .properties(properties) .child(BareConfiguration.class) .web(WebApplicationType.NONE) .run(); - then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); - then(this.context.getParent().getEnvironment()).isNotSameAs(this.context.getEnvironment()); - then(this.context.getEnvironment() + then(context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); + then(context.getParent().getEnvironment()).isNotSameAs(context.getEnvironment()); + then(context.getEnvironment() .getPropertySources() .contains(PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME + "-testBootstrap")).isTrue(); - then(((ConfigurableEnvironment) this.context.getParent().getEnvironment()).getPropertySources() + then(((ConfigurableEnvironment) context.getParent().getEnvironment()).getPropertySources() .contains(PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME + "-testBootstrap")).isTrue(); } @@ -559,29 +558,29 @@ public class BootstrapConfigurationTests { .profiles("parent") .web(WebApplicationType.NONE) .run(); - this.context = new SpringApplicationBuilder(BareConfiguration.class) + context = new SpringApplicationBuilder(BareConfiguration.class) .properties("spring.config.use-legacy-processing=true") .profiles("child") .parent(parent) .web(WebApplicationType.NONE) .run(); - then(this.context.getParent().getEnvironment()).isNotSameAs(this.context.getEnvironment()); + then(context.getParent().getEnvironment()).isNotSameAs(context.getEnvironment()); // The ApplicationContext merges profiles (profiles and property sources), see // AbstractEnvironment.merge() - then(this.context.getEnvironment().acceptsProfiles("child", "parent")).isTrue(); + then(context.getEnvironment().acceptsProfiles("child", "parent")).isTrue(); // But the parent is not a child - then(this.context.getParent().getEnvironment().acceptsProfiles("child")).isFalse(); - then(this.context.getParent().getEnvironment().acceptsProfiles("parent")).isTrue(); - then(((ConfigurableEnvironment) this.context.getParent().getEnvironment()).getPropertySources() + then(context.getParent().getEnvironment().acceptsProfiles("child")).isFalse(); + then(context.getParent().getEnvironment().acceptsProfiles("parent")).isTrue(); + then(((ConfigurableEnvironment) context.getParent().getEnvironment()).getPropertySources() .contains(PropertySourceBootstrapConfiguration.BOOTSTRAP_PROPERTY_SOURCE_NAME + "-testBootstrap")).isTrue(); - then(this.context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); + then(context.getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); // The "bootstrap" property source is not shared now, but it has the same // properties in it because they are pulled from the PropertySourceConfiguration // below - then(this.context.getParent().getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); + then(context.getParent().getEnvironment().getProperty("bootstrap.foo")).isEqualTo("bar"); // The parent property source is there in the child because they are both in the // "parent" profile (by virtue of the merge in AbstractEnvironment) - then(this.context.getEnvironment().getProperty("info.name")).isEqualTo("parent"); + then(context.getEnvironment().getProperty("info.name")).isEqualTo("parent"); } @Test @@ -597,37 +596,37 @@ public class BootstrapConfigurationTests { private void includeProfileFromBootstrapPropertySource(String... properties) { PropertySourceConfiguration.MAP.put("spring.profiles.include", "bar,baz"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .profiles("foo") .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().acceptsProfiles("baz")).isTrue(); - then(this.context.getEnvironment().acceptsProfiles("bar")).isTrue(); + then(context.getEnvironment().acceptsProfiles("baz")).isTrue(); + then(context.getEnvironment().acceptsProfiles("bar")).isTrue(); } @Test public void activeProfileFromBootstrapPropertySource() { activeProfileFromBootstrapPropertySource("spring.config.use-legacy-processing=true"); - then(this.context.getEnvironment().getActiveProfiles()).contains("foo"); + then(context.getEnvironment().getActiveProfiles()).contains("foo"); } @Test public void activeProfileFromBootstrapPropertySourceWithAppContext() { activeProfileFromBootstrapPropertySource("spring.config.use-legacy-processing=true", "spring.cloud.config.initialize-on-context-refresh=true"); - then(this.context.getEnvironment().getActiveProfiles()).doesNotContain("after"); - then(this.context.getEnvironment().getActiveProfiles()).contains("baz", "bar", "foo"); + then(context.getEnvironment().getActiveProfiles()).doesNotContain("after"); + then(context.getEnvironment().getActiveProfiles()).contains("baz", "bar", "foo"); } private void activeProfileFromBootstrapPropertySource(String... properties) { PropertySourceConfiguration.MAP.put("spring.profiles.active", "bar,baz"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .profiles("foo") .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().acceptsProfiles("baz", "bar", "foo")).isTrue(); + then(context.getEnvironment().acceptsProfiles("baz", "bar", "foo")).isTrue(); } @@ -645,13 +644,13 @@ public class BootstrapConfigurationTests { private void activeAndIncludeProfileFromBootstrapPropertySource(String... properties) { PropertySourceConfiguration.MAP.put("spring.profiles.active", "bar,baz"); PropertySourceConfiguration.MAP.put("spring.profiles.include", "bar,baz,hello"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .profiles("foo") .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().acceptsProfiles("baz", "bar", "hello", "foo")).isTrue(); - then(this.context.getEnvironment().getActiveProfiles()).contains("baz", "bar", "foo", "hello"); + then(context.getEnvironment().acceptsProfiles("baz", "bar", "hello", "foo")).isTrue(); + then(context.getEnvironment().getActiveProfiles()).contains("baz", "bar", "foo", "hello"); } @Test @@ -669,13 +668,13 @@ public class BootstrapConfigurationTests { private void activeAndIncludeProfileFromBootstrapPropertySourceWithReplacement(String... properties) { PropertySourceConfiguration.MAP.put("spring.profiles.active", "${barreplacement},baz"); PropertySourceConfiguration.MAP.put("spring.profiles.include", "${barreplacement},baz,hello"); - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .profiles("foo") .sources(BareConfiguration.class) .run(); - then(this.context.getEnvironment().acceptsProfiles("baz", "bar", "hello", "foo")).isTrue(); - then(this.context.getEnvironment().getActiveProfiles()).contains("baz", "bar", "foo", "hello"); + then(context.getEnvironment().acceptsProfiles("baz", "bar", "hello", "foo")).isTrue(); + then(context.getEnvironment().getActiveProfiles()).contains("baz", "bar", "foo", "hello"); } @Test @@ -691,12 +690,12 @@ public class BootstrapConfigurationTests { } private void includeProfileFromBootstrapProperties(String... properties) { - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .sources(BareConfiguration.class) .properties(properties) .run(); - then(this.context.getEnvironment().acceptsProfiles("local")).isTrue(); - then(this.context.getEnvironment().getProperty("added")).isEqualTo("Hello added!"); + then(context.getEnvironment().acceptsProfiles("local")).isTrue(); + then(context.getEnvironment().getProperty("added")).isEqualTo("Hello added!"); } @Test @@ -712,28 +711,28 @@ public class BootstrapConfigurationTests { } private void nonEnumerablePropertySourceWorks(String... properties) { - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .sources(BareConfiguration.class) .properties(properties) .run(); - then(this.context.getEnvironment().getProperty("foo")).isEqualTo("bar"); + then(context.getEnvironment().getProperty("foo")).isEqualTo("bar"); } @Test void activeAndIncludeProfileFromBootstrapPropertySource_WhenMultiplePlacesHaveActiveProfileProperties_ShouldOnlyAcceptTheTopPriority() { String[] properties = new String[] { "spring.config.use-legacy-processing=true" }; - this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + context = new SpringApplicationBuilder().web(WebApplicationType.NONE) .properties(properties) .sources(BareConfiguration.class) .run("--spring.profiles.active=prod,secure"); - then(this.context.getEnvironment().acceptsProfiles("prod", "secure")).isTrue(); + then(context.getEnvironment().acceptsProfiles("prod", "secure")).isTrue(); // active profile from property sources with lower priority should not be included - then(this.context.getEnvironment().acceptsProfiles("local")).isFalse(); - then(this.context.getEnvironment().getActiveProfiles()).contains("prod", "secure"); - then(this.context.getEnvironment().getActiveProfiles()).doesNotContain("local"); + then(context.getEnvironment().acceptsProfiles("local")).isFalse(); + then(context.getEnvironment().getActiveProfiles()).contains("prod", "secure"); + then(context.getEnvironment().getActiveProfiles()).doesNotContain("local"); // check if active profile value could possibly exist in other property sources // with lower priority - then(this.context.getEnvironment() + then(context.getEnvironment() .getPropertySources() .stream() .map(p -> p.getProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME)) @@ -787,10 +786,10 @@ public class BootstrapConfigurationTests { } } } - if (this.name != null) { - then(this.name).isEqualTo(environment.getProperty("spring.application.name")); + if (name != null) { + then(name).isEqualTo(environment.getProperty("spring.application.name")); } - if (this.fail) { + if (fail) { throw new RuntimeException("Planned"); } @@ -798,7 +797,7 @@ public class BootstrapConfigurationTests { } public String getName() { - return this.name; + return name; } public void setName(String name) { @@ -806,7 +805,7 @@ public class BootstrapConfigurationTests { } public boolean isFail() { - return this.fail; + return fail; } public void setFail(boolean fail) { @@ -836,10 +835,10 @@ public class BootstrapConfigurationTests { @Override public PropertySource locate(Environment environment) { - if (this.name != null) { - then(this.name).isEqualTo(environment.getProperty("spring.application.name")); + if (name != null) { + then(name).isEqualTo(environment.getProperty("spring.application.name")); } - if (this.fail) { + if (fail) { throw new RuntimeException("Planned"); } CompositePropertySource compositePropertySource = new CompositePropertySource("listTestBootstrap"); @@ -849,7 +848,7 @@ public class BootstrapConfigurationTests { } public String getName() { - return this.name; + return name; } public void setName(String name) { @@ -857,7 +856,7 @@ public class BootstrapConfigurationTests { } public boolean isFail() { - return this.fail; + return fail; } public void setFail(boolean fail) { diff --git a/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/encrypt/EncryptorFactoryTests.java b/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/encrypt/EncryptorFactoryTests.java index e6ef9d39..468d7d5d 100644 --- a/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/encrypt/EncryptorFactoryTests.java +++ b/spring-cloud-context/src/test/java/org/springframework/cloud/bootstrap/encrypt/EncryptorFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-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. @@ -18,7 +18,6 @@ package org.springframework.cloud.bootstrap.encrypt; import java.nio.charset.StandardCharsets; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.springframework.cloud.context.encrypt.EncryptorFactory; @@ -26,10 +25,12 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.security.crypto.encrypt.TextEncryptor; import org.springframework.util.StreamUtils; +import static org.assertj.core.api.Assertions.assertThatRuntimeException; import static org.assertj.core.api.BDDAssertions.then; /** * @author Biju Kunjummen + * @author Olga Maciaszek-Sharma */ public class EncryptorFactoryTests { @@ -51,7 +52,7 @@ public class EncryptorFactoryTests { -----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAwClFgrRa/PUHPIJr9gvIPL6g6Rjp/TVZmVNOf2fL96DYbkj5 """; - Assertions.assertThrows(RuntimeException.class, () -> new EncryptorFactory().create(key)); + assertThatRuntimeException().isThrownBy(() -> new EncryptorFactory().create(key)); } } diff --git a/spring-cloud-context/src/test/java/org/springframework/cloud/context/scope/refresh/RefreshScopeIntegrationTests.java b/spring-cloud-context/src/test/java/org/springframework/cloud/context/scope/refresh/RefreshScopeIntegrationTests.java index 2516b1d7..ededcf34 100644 --- a/spring-cloud-context/src/test/java/org/springframework/cloud/context/scope/refresh/RefreshScopeIntegrationTests.java +++ b/spring-cloud-context/src/test/java/org/springframework/cloud/context/scope/refresh/RefreshScopeIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-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. @@ -19,7 +19,6 @@ package org.springframework.cloud.context.scope.refresh; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -41,6 +40,7 @@ import org.springframework.jmx.export.annotation.ManagedAttribute; import org.springframework.jmx.export.annotation.ManagedResource; import org.springframework.test.annotation.DirtiesContext; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.BDDAssertions.then; @SpringBootTest(classes = TestConfiguration.class) @@ -69,12 +69,12 @@ public class RefreshScopeIntegrationTests { @Test @DirtiesContext public void testSimpleProperties() { - then(this.service.getMessage()).isEqualTo("Hello scope!"); - then(this.service instanceof Advised).isTrue(); + then(service.getMessage()).isEqualTo("Hello scope!"); + then(service instanceof Advised).isTrue(); // Change the dynamic property source... - this.properties.setMessage("Foo"); + properties.setMessage("Foo"); // ...but don't refresh, so the bean stays the same: - then(this.service.getMessage()).isEqualTo("Hello scope!"); + then(service.getMessage()).isEqualTo("Hello scope!"); then(ExampleService.getInitCount()).isEqualTo(0); then(ExampleService.getDestroyCount()).isEqualTo(0); } @@ -82,14 +82,14 @@ public class RefreshScopeIntegrationTests { @Test @DirtiesContext public void testRefresh() { - then(this.service.getMessage()).isEqualTo("Hello scope!"); - String id1 = this.service.toString(); + then(service.getMessage()).isEqualTo("Hello scope!"); + String id1 = service.toString(); // Change the dynamic property source... - this.properties.setMessage("Foo"); + properties.setMessage("Foo"); // ...and then refresh, so the bean is re-initialized: - this.scope.refreshAll(); - String id2 = this.service.toString(); - then(this.service.getMessage()).isEqualTo("Foo"); + scope.refreshAll(); + String id2 = service.toString(); + then(service.getMessage()).isEqualTo("Foo"); then(ExampleService.getInitCount()).isEqualTo(1); then(ExampleService.getDestroyCount()).isEqualTo(1); then(id2).isNotSameAs(id1); @@ -100,15 +100,15 @@ public class RefreshScopeIntegrationTests { @Test @DirtiesContext public void testRefreshBean() { - then(this.service.getMessage()).isEqualTo("Hello scope!"); - String id1 = this.service.toString(); + then(service.getMessage()).isEqualTo("Hello scope!"); + String id1 = service.toString(); // Change the dynamic property source... - this.properties.setMessage("Foo"); + properties.setMessage("Foo"); // ...and then refresh, so the bean is re-initialized: - this.scope.refresh("service"); - String id2 = this.service.toString(); - then(this.service.getMessage()).isEqualTo("Foo"); - then(this.service.getMessage()).isEqualTo("Foo"); + scope.refresh("service"); + String id2 = service.toString(); + then(service.getMessage()).isEqualTo("Foo"); + then(service.getMessage()).isEqualTo("Foo"); then(ExampleService.getInitCount()).isEqualTo(1); then(ExampleService.getDestroyCount()).isEqualTo(1); then(id2).isNotSameAs(id1); @@ -120,7 +120,7 @@ public class RefreshScopeIntegrationTests { @Test @DirtiesContext public void testCheckedException() { - Assertions.assertThrows(ServiceException.class, () -> this.service.throwsException()); + assertThatExceptionOfType(ServiceException.class).isThrownBy(() -> service.throwsException()); } public interface Service { @@ -166,28 +166,28 @@ public class RefreshScopeIntegrationTests { @Override public void afterPropertiesSet() throws Exception { - logger.debug("Initializing message: " + this.message); + logger.debug("Initializing message: " + message); initCount++; } @Override public void destroy() throws Exception { - logger.debug("Destroying message: " + this.message); + logger.debug("Destroying message: " + message); destroyCount++; - this.message = null; + message = null; } @Override public String getMessage() { - logger.debug("Getting message: " + this.message); + logger.debug("Getting message: " + message); try { - Thread.sleep(this.delay); + Thread.sleep(delay); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } - logger.info("Returning message: " + this.message); - return this.message; + logger.info("Returning message: " + message); + return message; } public void setMessage(String message) { @@ -224,8 +224,8 @@ public class RefreshScopeIntegrationTests { @RefreshScope public ExampleService service() { ExampleService service = new ExampleService(); - service.setMessage(this.properties.getMessage()); - service.setDelay(this.properties.getDelay()); + service.setMessage(properties.getMessage()); + service.setDelay(properties.getDelay()); return service; } @@ -241,7 +241,7 @@ public class RefreshScopeIntegrationTests { @ManagedAttribute public String getMessage() { - return this.message; + return message; } public void setMessage(String message) { @@ -250,7 +250,7 @@ public class RefreshScopeIntegrationTests { @ManagedAttribute public int getDelay() { - return this.delay; + return delay; } public void setDelay(int delay) { diff --git a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSupplierBuilderTests.java b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSupplierBuilderTests.java index 7b12ca1a..45f526dc 100644 --- a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSupplierBuilderTests.java +++ b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSupplierBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2023 the original author or authors. + * 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. @@ -28,7 +28,7 @@ import org.springframework.context.annotation.Import; import org.springframework.web.reactive.function.client.WebClient; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.fail; +import static org.assertj.core.api.Fail.fail; import static org.mockito.Mockito.mock; public class ServiceInstanceListSupplierBuilderTests { From f7b088edb7792cc62cdf4ae7163b7ceae1b4e24f Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Fri, 11 Apr 2025 16:17:45 +0200 Subject: [PATCH 5/9] Fix typo. Signed-off-by: Olga Maciaszek-Sharma --- .../cloud/client/loadbalancer/LoadBalancerUriTools.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerUriTools.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerUriTools.java index c23d0f29..7715875d 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerUriTools.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/LoadBalancerUriTools.java @@ -56,7 +56,7 @@ public final class LoadBalancerUriTools { boolean encoded = (uri.getRawQuery() != null && uri.getRawQuery().contains(PERCENTAGE_SIGN)) || (uri.getRawPath() != null && uri.getRawPath().contains(PERCENTAGE_SIGN)) || (uri.getRawFragment() != null && uri.getRawFragment().contains(PERCENTAGE_SIGN)); - // Verify if it is real ly fully encoded. Treat partial encoded as unencoded. + // Verify if it is really fully encoded. Treat partial encoded as unencoded. if (encoded) { try { UriComponentsBuilder.fromUri(uri).build(true); From 999a323a1b261759bde025d2daadabc03e9270e4 Mon Sep 17 00:00:00 2001 From: Yanming Zhou Date: Wed, 16 Apr 2025 23:35:01 +0800 Subject: [PATCH 6/9] Improve GenericScope to remove unused evaluationContext (#1486) Signed-off-by: Yanming Zhou --- .../cloud/context/scope/GenericScope.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/GenericScope.java b/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/GenericScope.java index b75a564f..449c390e 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/GenericScope.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/context/scope/GenericScope.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-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. @@ -57,7 +57,6 @@ import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.ParseException; import org.springframework.expression.spel.standard.SpelExpressionParser; -import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.util.ReflectionUtils; import org.springframework.util.StringUtils; @@ -67,6 +66,7 @@ import org.springframework.util.StringUtils; *

* * @author Dave Syer + * @author Yanming Zhou * @since 3.1 * */ @@ -81,13 +81,11 @@ public class GenericScope private ConfigurableListableBeanFactory beanFactory; - private StandardEvaluationContext evaluationContext; - private String id; - private Map errors = new ConcurrentHashMap<>(); + private final Map errors = new ConcurrentHashMap<>(); - private ConcurrentMap locks = new ConcurrentHashMap<>(); + private final ConcurrentMap locks = new ConcurrentHashMap<>(); static RuntimeException wrapIfNecessary(Throwable throwable) { if (throwable instanceof RuntimeException) { @@ -213,7 +211,7 @@ public class GenericScope @Override public Object resolveContextualObject(String key) { Expression expression = parseExpression(key); - return expression.getValue(this.evaluationContext, this.beanFactory); + return expression.getValue(this.beanFactory); } private Expression parseExpression(String input) { From 383d0672ea2ccbde2ad3006b5eb421cb70c68c4f Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Tue, 22 Apr 2025 19:09:12 +0200 Subject: [PATCH 7/9] Refactor LoadBalancerExchangeFilterFunction integration tests. Signed-off-by: Olga Maciaszek-Sharma --- ...changeFilterFunctionIntegrationTests.java} | 197 ++++++------- ...xchangeFilterFunctionIntegrationTests.java | 67 +++++ ...xchangeFilterFunctionIntegrationTests.java | 266 ++---------------- 3 files changed, 186 insertions(+), 344 deletions(-) rename spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/{ReactorLoadBalancerExchangeFilterFunctionTests.java => AbstractLoadBalancerExchangeFilterFunctionIntegrationTests.java} (59%) create mode 100644 spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/ReactorLoadBalancerExchangeFilterFunctionIntegrationTests.java diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/ReactorLoadBalancerExchangeFilterFunctionTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/AbstractLoadBalancerExchangeFilterFunctionIntegrationTests.java similarity index 59% rename from spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/ReactorLoadBalancerExchangeFilterFunctionTests.java rename to spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/AbstractLoadBalancerExchangeFilterFunctionIntegrationTests.java index d5f1c291..06c84d18 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/ReactorLoadBalancerExchangeFilterFunctionTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/AbstractLoadBalancerExchangeFilterFunctionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-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. @@ -27,16 +27,13 @@ import java.util.concurrent.ConcurrentHashMap; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import reactor.core.publisher.Mono; 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.web.server.LocalServerPort; import org.springframework.cloud.client.DefaultServiceInstance; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; -import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryProperties; import org.springframework.cloud.client.loadbalancer.CompletionContext; import org.springframework.cloud.client.loadbalancer.DefaultRequestContext; @@ -45,51 +42,47 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties; import org.springframework.cloud.client.loadbalancer.Request; import org.springframework.cloud.client.loadbalancer.Response; import org.springframework.cloud.client.loadbalancer.ResponseData; -import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; -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.http.ResponseEntity; import org.springframework.web.reactive.function.client.WebClient; import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatCode; import static org.assertj.core.api.BDDAssertions.then; -import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** - * Tests for {@link ReactorLoadBalancerExchangeFilterFunction}. + * Base class for {@link LoadBalancedExchangeFilterFunction} integration tests. * * @author Olga Maciaszek-Sharma - * @author Charu Covindane */ -@SuppressWarnings("ConstantConditions") -@SpringBootTest(webEnvironment = RANDOM_PORT) -class ReactorLoadBalancerExchangeFilterFunctionTests { +@SuppressWarnings("DataFlowIssue") +abstract class AbstractLoadBalancerExchangeFilterFunctionIntegrationTests { @Autowired - private ReactorLoadBalancerExchangeFilterFunction loadBalancerFunction; + protected LoadBalancedExchangeFilterFunction loadBalancerFunction; @Autowired - private SimpleDiscoveryProperties properties; + protected SimpleDiscoveryProperties properties; @Autowired - private LoadBalancerProperties loadBalancerProperties; + protected LoadBalancerProperties loadBalancerProperties; @Autowired - private ReactiveLoadBalancer.Factory factory; + protected ReactiveLoadBalancer.Factory factory; @LocalServerPort - private int port; + protected int port; @BeforeEach - void setUp() { + protected void setUp() { DefaultServiceInstance instance = new DefaultServiceInstance(); instance.setServiceId("testservice"); - instance.setUri(URI.create("http://localhost:" + this.port)); + instance.setUri(URI.create("http://localhost:" + port)); DefaultServiceInstance instanceWithNoLifecycleProcessors = new DefaultServiceInstance(); instanceWithNoLifecycleProcessors.setServiceId("serviceWithNoLifecycleProcessors"); - instanceWithNoLifecycleProcessors.setUri(URI.create("http://localhost:" + this.port)); + instanceWithNoLifecycleProcessors.setUri(URI.create("http://localhost:" + port)); properties.getInstances().put("testservice", Collections.singletonList(instance)); properties.getInstances() .put("serviceWithNoLifecycleProcessors", Collections.singletonList(instanceWithNoLifecycleProcessors)); @@ -97,41 +90,50 @@ class ReactorLoadBalancerExchangeFilterFunctionTests { @Test void correctResponseReturnedForExistingHostAndInstancePresent() { - ClientResponse clientResponse = WebClient.builder() + ResponseEntity response = WebClient.builder() .baseUrl("http://testservice") .filter(loadBalancerFunction) .build() .get() .uri("/hello") - .exchange() + .retrieve() + .toEntity(String.class) .block(); - then(clientResponse.statusCode()).isEqualTo(HttpStatus.OK); - then(clientResponse.bodyToMono(String.class).block()).isEqualTo("Hello World"); + then(response.getStatusCode()).isEqualTo(HttpStatus.OK); + then(response.getBody()).isEqualTo("Hello World"); } @Test void serviceUnavailableReturnedWhenNoInstancePresent() { - ClientResponse clientResponse = WebClient.builder() - .baseUrl("http://xxx") - .filter(this.loadBalancerFunction) - .build() - .get() - .exchange() - .block(); - then(clientResponse.statusCode()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); + assertThatIllegalStateException() + .isThrownBy(() -> WebClient.builder() + .baseUrl("http://xxx") + .filter(loadBalancerFunction) + .defaultStatusHandler(httpStatusCode -> httpStatusCode.equals(HttpStatus.SERVICE_UNAVAILABLE), + clientResponse -> Mono.just(new IllegalStateException("503"))) + .build() + .get() + .retrieve() + .toBodilessEntity() + .block()) + .withMessage("503"); } @Test @Disabled // FIXME 3.0.0 void badRequestReturnedForIncorrectHost() { - ClientResponse clientResponse = WebClient.builder() - .baseUrl("http:///xxx") - .filter(this.loadBalancerFunction) - .build() - .get() - .exchange() - .block(); - then(clientResponse.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST); + assertThatIllegalStateException() + .isThrownBy(() -> WebClient.builder() + .baseUrl("http:///xxx") + .filter(loadBalancerFunction) + .defaultStatusHandler(httpStatusCode -> httpStatusCode.equals(HttpStatus.BAD_REQUEST), + response -> Mono.just(new IllegalStateException("400"))) + .build() + .get() + .retrieve() + .toBodilessEntity() + .block()) + .withMessage("400"); } @Test @@ -142,7 +144,7 @@ class ReactorLoadBalancerExchangeFilterFunctionTests { .build() .get() .uri("/hello") - .exchange() + .exchangeToMono(clientResponse -> clientResponse.bodyToMono(String.class)) .block()).doesNotThrowAnyException(); } @@ -150,89 +152,80 @@ class ReactorLoadBalancerExchangeFilterFunctionTests { void loadBalancerLifecycleCallbacksExecuted() { final String callbackTestHint = "callbackTestHint"; loadBalancerProperties.getHint().put("testservice", "callbackTestHint"); - ClientResponse clientResponse = WebClient.builder() + + ResponseEntity response = WebClient.builder() .baseUrl("http://testservice") .filter(loadBalancerFunction) .build() .get() .uri("/callback") - .exchange() + .retrieve() + .toBodilessEntity() .block(); Collection> lifecycleLogRequests = ((TestLoadBalancerLifecycle) factory .getInstances("testservice", LoadBalancerLifecycle.class) .get("loadBalancerLifecycle")).getStartLog().values(); - Collection> lifecycleStartedLogRequests = ((TestLoadBalancerLifecycle) factory + Collection> lifecycleLogStartRequests = ((TestLoadBalancerLifecycle) factory .getInstances("testservice", LoadBalancerLifecycle.class) .get("loadBalancerLifecycle")).getStartRequestLog().values(); Collection> anotherLifecycleLogRequests = ((AnotherLoadBalancerLifecycle) factory .getInstances("testservice", LoadBalancerLifecycle.class) .get("anotherLoadBalancerLifecycle")).getCompleteLog().values(); - then(clientResponse.statusCode()).isEqualTo(HttpStatus.OK); + then(response.getStatusCode()).isEqualTo(HttpStatus.OK); assertThat(lifecycleLogRequests).extracting(request -> ((DefaultRequestContext) request.getContext()).getHint()) .contains(callbackTestHint); - assertThat(lifecycleStartedLogRequests) + assertThat(lifecycleLogStartRequests) .extracting(request -> ((DefaultRequestContext) request.getContext()).getHint()) .contains(callbackTestHint); assertThat(anotherLifecycleLogRequests) .extracting(completionContext -> ((ResponseData) completionContext.getClientResponse()).getRequestData() - .getUrl() - .toString()) - .contains("http://testservice/callback"); + .getHttpMethod()) + .contains(HttpMethod.GET); } - @SuppressWarnings({ "unchecked", "rawtypes" }) - @EnableDiscoveryClient - @EnableAutoConfiguration - @SpringBootConfiguration(proxyBeanMethods = false) - @RestController - static class Config { + protected static class TestLoadBalancerFactory implements ReactiveLoadBalancer.Factory { - @GetMapping("/hello") - public String hello() { - return "Hello World"; + private final ReactorLoadBalancerExchangeFilterFunctionIntegrationTests.TestLoadBalancerLifecycle testLoadBalancerLifecycle; + + private final ReactorLoadBalancerExchangeFilterFunctionIntegrationTests.TestLoadBalancerLifecycle anotherLoadBalancerLifecycle; + + private final DiscoveryClient discoveryClient; + + private final LoadBalancerProperties properties; + + public TestLoadBalancerFactory(DiscoveryClient discoveryClient, LoadBalancerProperties properties) { + this.discoveryClient = discoveryClient; + this.properties = properties; + testLoadBalancerLifecycle = new ReactorLoadBalancerExchangeFilterFunctionIntegrationTests.TestLoadBalancerLifecycle(); + anotherLoadBalancerLifecycle = new ReactorLoadBalancerExchangeFilterFunctionIntegrationTests.AnotherLoadBalancerLifecycle(); } - @GetMapping("/callback") - String callbackTestResult() { - return "callbackTestResult"; + @Override + public ReactiveLoadBalancer getInstance(String serviceId) { + return new DiscoveryClientBasedReactiveLoadBalancer(serviceId, discoveryClient); } - @Bean - ReactiveLoadBalancer.Factory reactiveLoadBalancerFactory(DiscoveryClient discoveryClient, - LoadBalancerProperties properties) { - return new ReactiveLoadBalancer.Factory<>() { + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public Map getInstances(String name, Class type) { + if (name.equals("serviceWithNoLifecycleProcessors")) { + return null; + } + Map lifecycleProcessors = new HashMap<>(); + lifecycleProcessors.put("loadBalancerLifecycle", testLoadBalancerLifecycle); + lifecycleProcessors.put("anotherLoadBalancerLifecycle", anotherLoadBalancerLifecycle); + return lifecycleProcessors; + } - private final TestLoadBalancerLifecycle testLoadBalancerLifecycle = new TestLoadBalancerLifecycle(); + @Override + public X getInstance(String name, Class clazz, Class... generics) { + return null; + } - private final TestLoadBalancerLifecycle anotherLoadBalancerLifecycle = new AnotherLoadBalancerLifecycle(); - - @Override - public ReactiveLoadBalancer getInstance(String serviceId) { - return new DiscoveryClientBasedReactiveLoadBalancer(serviceId, discoveryClient); - } - - @Override - public Map getInstances(String name, Class type) { - if (name.equals("serviceWithNoLifecycleProcessors")) { - return null; - } - Map lifecycleProcessors = new HashMap<>(); - lifecycleProcessors.put("loadBalancerLifecycle", testLoadBalancerLifecycle); - lifecycleProcessors.put("anotherLoadBalancerLifecycle", anotherLoadBalancerLifecycle); - return lifecycleProcessors; - } - - @Override - public X getInstance(String name, Class clazz, Class... generics) { - return null; - } - - @Override - public LoadBalancerProperties getProperties(String serviceId) { - return properties; - } - }; + @Override + public LoadBalancerProperties getProperties(String serviceId) { + return properties; } } @@ -257,6 +250,7 @@ class ReactorLoadBalancerExchangeFilterFunctionTests { @Override public void onComplete(CompletionContext completionContext) { + completeLog.clear(); completeLog.put(getName() + UUID.randomUUID(), completionContext); } @@ -273,18 +267,13 @@ class ReactorLoadBalancerExchangeFilterFunctionTests { } protected String getName() { - return this.getClass().getSimpleName(); + return getClass().getSimpleName(); } } protected static class AnotherLoadBalancerLifecycle extends TestLoadBalancerLifecycle { - @Override - protected String getName() { - return this.getClass().getSimpleName(); - } - } } diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/ReactorLoadBalancerExchangeFilterFunctionIntegrationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/ReactorLoadBalancerExchangeFilterFunctionIntegrationTests.java new file mode 100644 index 00000000..5b83151f --- /dev/null +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/ReactorLoadBalancerExchangeFilterFunctionIntegrationTests.java @@ -0,0 +1,67 @@ +/* + * Copyright 2012-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.client.loadbalancer.reactive; + +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.client.ServiceInstance; +import org.springframework.cloud.client.discovery.DiscoveryClient; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; + +/** + * Tests for {@link ReactorLoadBalancerExchangeFilterFunction}. + * + * @author Olga Maciaszek-Sharma + * @author Charu Covindane + */ +@SuppressWarnings("ConstantConditions") +@SpringBootTest(webEnvironment = RANDOM_PORT) +class ReactorLoadBalancerExchangeFilterFunctionIntegrationTests + extends AbstractLoadBalancerExchangeFilterFunctionIntegrationTests { + + @EnableDiscoveryClient + @EnableAutoConfiguration + @SpringBootConfiguration(proxyBeanMethods = false) + @RestController + static class Config { + + @GetMapping("/hello") + public String hello() { + return "Hello World"; + } + + @GetMapping("/callback") + String callbackTestResult() { + return "callbackTestResult"; + } + + @Bean + ReactiveLoadBalancer.Factory reactiveLoadBalancerFactory(DiscoveryClient discoveryClient, + LoadBalancerProperties properties) { + return new TestLoadBalancerFactory(discoveryClient, properties); + } + + } + +} diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/RetryableLoadBalancerExchangeFilterFunctionIntegrationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/RetryableLoadBalancerExchangeFilterFunctionIntegrationTests.java index 7cd4333c..9ef145e6 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/RetryableLoadBalancerExchangeFilterFunctionIntegrationTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/RetryableLoadBalancerExchangeFilterFunctionIntegrationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-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. @@ -18,45 +18,27 @@ package org.springframework.cloud.client.loadbalancer.reactive; import java.net.URI; import java.util.Arrays; -import java.util.Collection; import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; 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.web.server.LocalServerPort; import org.springframework.cloud.client.DefaultServiceInstance; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; -import org.springframework.cloud.client.discovery.simple.SimpleDiscoveryProperties; -import org.springframework.cloud.client.loadbalancer.CompletionContext; -import org.springframework.cloud.client.loadbalancer.DefaultRequestContext; -import org.springframework.cloud.client.loadbalancer.LoadBalancerLifecycle; import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties; -import org.springframework.cloud.client.loadbalancer.Request; -import org.springframework.cloud.client.loadbalancer.Response; -import org.springframework.cloud.client.loadbalancer.ResponseData; import org.springframework.context.annotation.Bean; -import org.springframework.http.HttpMethod; +import org.springframework.context.annotation.Primary; import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; 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.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.BDDAssertions.then; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; @@ -66,103 +48,28 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen * @author Olga Maciaszek-Sharma * @since 3.0.0 */ +@SuppressWarnings("DataFlowIssue") @SpringBootTest(webEnvironment = RANDOM_PORT) -class RetryableLoadBalancerExchangeFilterFunctionIntegrationTests { - - @Autowired - private RetryableLoadBalancerExchangeFilterFunction loadBalancerFunction; - - @Autowired - private SimpleDiscoveryProperties properties; - - @Autowired - private LoadBalancerProperties loadBalancerProperties; - - @Autowired - private ReactiveLoadBalancer.Factory factory; - - @LocalServerPort - private int port; - - @BeforeEach - void setUp() { - DefaultServiceInstance instance = new DefaultServiceInstance(); - instance.setServiceId("testservice"); - instance.setUri(URI.create("http://localhost:" + port)); - DefaultServiceInstance instanceWithNoLifecycleProcessors = new DefaultServiceInstance(); - instanceWithNoLifecycleProcessors.setServiceId("serviceWithNoLifecycleProcessors"); - instanceWithNoLifecycleProcessors.setUri(URI.create("http://localhost:" + port)); - properties.getInstances().put("testservice", Collections.singletonList(instance)); - properties.getInstances() - .put("serviceWithNoLifecycleProcessors", Collections.singletonList(instanceWithNoLifecycleProcessors)); - } - - @Test - void loadBalancerLifecycleCallbacksExecuted() { - final String callbackTestHint = "callbackTestHint"; - loadBalancerProperties.getHint().put("testservice", "callbackTestHint"); - - ClientResponse clientResponse = WebClient.builder() - .baseUrl("http://testservice") - .filter(this.loadBalancerFunction) - .build() - .get() - .uri("/callback") - .exchange() - .block(); - - Collection> lifecycleLogRequests = ((TestLoadBalancerLifecycle) factory - .getInstances("testservice", LoadBalancerLifecycle.class) - .get("loadBalancerLifecycle")).getStartLog().values(); - Collection> lifecycleLogStartRequests = ((TestLoadBalancerLifecycle) factory - .getInstances("testservice", LoadBalancerLifecycle.class) - .get("loadBalancerLifecycle")).getStartRequestLog().values(); - Collection> anotherLifecycleLogRequests = ((AnotherLoadBalancerLifecycle) factory - .getInstances("testservice", LoadBalancerLifecycle.class) - .get("anotherLoadBalancerLifecycle")).getCompleteLog().values(); - then(clientResponse.statusCode()).isEqualTo(HttpStatus.OK); - assertThat(lifecycleLogRequests).extracting(request -> ((DefaultRequestContext) request.getContext()).getHint()) - .contains(callbackTestHint); - assertThat(lifecycleLogStartRequests) - .extracting(request -> ((DefaultRequestContext) request.getContext()).getHint()) - .contains(callbackTestHint); - assertThat(anotherLifecycleLogRequests) - .extracting(completionContext -> ((ResponseData) completionContext.getClientResponse()).getRequestData() - .getHttpMethod()) - .contains(HttpMethod.GET); - } - - @Test - void correctResponseReturnedForExistingHostAndInstancePresent() { - ClientResponse clientResponse = WebClient.builder() - .baseUrl("http://testservice") - .filter(this.loadBalancerFunction) - .build() - .get() - .uri("/hello") - .exchange() - .block(); - - then(clientResponse.statusCode()).isEqualTo(HttpStatus.OK); - then(clientResponse.bodyToMono(String.class).block()).isEqualTo("Hello World"); - } +class RetryableLoadBalancerExchangeFilterFunctionIntegrationTests + extends AbstractLoadBalancerExchangeFilterFunctionIntegrationTests { @Test void correctResponseReturnedAfterRetryingOnSameServiceInstance() { loadBalancerProperties.getRetry().setMaxRetriesOnSameServiceInstance(1); loadBalancerProperties.getRetry().getRetryableStatusCodes().add(500); - ClientResponse clientResponse = WebClient.builder() + ResponseEntity response = WebClient.builder() .baseUrl("http://testservice") - .filter(this.loadBalancerFunction) + .filter(loadBalancerFunction) .build() .get() .uri("/exception") - .exchange() + .retrieve() + .toEntity(String.class) .block(); - then(clientResponse.statusCode()).isEqualTo(HttpStatus.OK); - then(clientResponse.bodyToMono(String.class).block()).isEqualTo("Hello World!"); + then(response.getStatusCode()).isEqualTo(HttpStatus.OK); + then(response.getBody()).isEqualTo("Hello World!"); } @Test @@ -178,72 +85,33 @@ class RetryableLoadBalancerExchangeFilterFunctionIntegrationTests { properties.getInstances().put("retrytest", Arrays.asList(badRetryTestInstance, goodRetryTestInstance)); loadBalancerProperties.getRetry().getRetryableStatusCodes().add(500); - ClientResponse clientResponse = WebClient.builder() + ResponseEntity response = WebClient.builder() .baseUrl("http://retrytest") - .filter(this.loadBalancerFunction) + .filter(loadBalancerFunction) .build() .get() .uri("/hello") - .exchange() + .retrieve() + .toEntity(String.class) .block(); - then(clientResponse.statusCode()).isEqualTo(HttpStatus.OK); - then(clientResponse.bodyToMono(String.class).block()).isEqualTo("Hello World"); + then(response.getStatusCode()).isEqualTo(HttpStatus.OK); + then(response.getBody()).isEqualTo("Hello World"); - ClientResponse secondClientResponse = WebClient.builder() + ResponseEntity secondResponse = WebClient.builder() .baseUrl("http://retrytest") - .filter(this.loadBalancerFunction) + .filter(loadBalancerFunction) .build() .get() .uri("/hello") - .exchange() + .retrieve() + .toEntity(String.class) .block(); - then(secondClientResponse.statusCode()).isEqualTo(HttpStatus.OK); - then(secondClientResponse.bodyToMono(String.class).block()).isEqualTo("Hello World"); + then(secondResponse.getStatusCode()).isEqualTo(HttpStatus.OK); + then(secondResponse.getBody()).isEqualTo("Hello World"); } - @Test - void serviceUnavailableReturnedWhenNoInstancePresent() { - ClientResponse clientResponse = WebClient.builder() - .baseUrl("http://xxx") - .filter(this.loadBalancerFunction) - .build() - .get() - .exchange() - .block(); - - then(clientResponse.statusCode()).isEqualTo(HttpStatus.SERVICE_UNAVAILABLE); - } - - @Test - @Disabled - // FIXME 3.0.0 - void badRequestReturnedForIncorrectHost() { - ClientResponse clientResponse = WebClient.builder() - .baseUrl("http:///xxx") - .filter(this.loadBalancerFunction) - .build() - .get() - .exchange() - .block(); - - then(clientResponse.statusCode()).isEqualTo(HttpStatus.BAD_REQUEST); - } - - @Test - void exceptionNotThrownWhenFactoryReturnsNullLifecycleProcessorsMap() { - assertThatCode(() -> WebClient.builder() - .baseUrl("http://serviceWithNoLifecycleProcessors") - .filter(this.loadBalancerFunction) - .build() - .get() - .uri("/hello") - .exchange() - .block()).doesNotThrowAnyException(); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) @EnableDiscoveryClient @EnableAutoConfiguration @SpringBootConfiguration(proxyBeanMethods = false) @@ -274,42 +142,11 @@ class RetryableLoadBalancerExchangeFilterFunctionIntegrationTests { @Bean ReactiveLoadBalancer.Factory reactiveLoadBalancerFactory(DiscoveryClient discoveryClient, LoadBalancerProperties properties) { - return new ReactiveLoadBalancer.Factory<>() { - - private final TestLoadBalancerLifecycle testLoadBalancerLifecycle = new TestLoadBalancerLifecycle(); - - private final TestLoadBalancerLifecycle anotherLoadBalancerLifecycle = new AnotherLoadBalancerLifecycle(); - - @Override - public ReactiveLoadBalancer getInstance(String serviceId) { - return new org.springframework.cloud.client.loadbalancer.reactive.DiscoveryClientBasedReactiveLoadBalancer( - serviceId, discoveryClient); - } - - @Override - public Map getInstances(String name, Class type) { - if (name.equals("serviceWithNoLifecycleProcessors")) { - return null; - } - Map lifecycleProcessors = new HashMap<>(); - lifecycleProcessors.put("loadBalancerLifecycle", testLoadBalancerLifecycle); - lifecycleProcessors.put("anotherLoadBalancerLifecycle", anotherLoadBalancerLifecycle); - return lifecycleProcessors; - } - - @Override - public X getInstance(String name, Class clazz, Class... generics) { - return null; - } - - @Override - public LoadBalancerProperties getProperties(String serviceId) { - return properties; - } - }; + return new TestLoadBalancerFactory(discoveryClient, properties); } @Bean + @Primary RetryableLoadBalancerExchangeFilterFunction exchangeFilterFunction( ReactiveLoadBalancer.Factory factory) { return new RetryableLoadBalancerExchangeFilterFunction( @@ -319,55 +156,4 @@ class RetryableLoadBalancerExchangeFilterFunctionIntegrationTests { } - protected static class TestLoadBalancerLifecycle implements LoadBalancerLifecycle { - - Map> startLog = new ConcurrentHashMap<>(); - - Map> startRequestLog = new ConcurrentHashMap<>(); - - Map> completeLog = new ConcurrentHashMap<>(); - - @Override - public void onStart(Request request) { - startLog.put(getName() + UUID.randomUUID(), request); - } - - @Override - public void onStartRequest(Request request, Response lbResponse) { - startRequestLog.put(getName() + UUID.randomUUID(), request); - } - - @Override - public void onComplete(CompletionContext completionContext) { - completeLog.clear(); - completeLog.put(getName() + UUID.randomUUID(), completionContext); - } - - Map> getStartLog() { - return startLog; - } - - Map> getCompleteLog() { - return completeLog; - } - - Map> getStartRequestLog() { - return startRequestLog; - } - - protected String getName() { - return this.getClass().getSimpleName(); - } - - } - - protected static class AnotherLoadBalancerLifecycle extends TestLoadBalancerLifecycle { - - @Override - protected String getName() { - return this.getClass().getSimpleName(); - } - - } - } From 64e44ce8ca0c9a21c1c7dffbed18d8ad2409235a Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Tue, 22 Apr 2025 19:14:50 +0200 Subject: [PATCH 8/9] Re-enable tests. Signed-off-by: Olga Maciaszek-Sharma --- ...tractLoadBalancerExchangeFilterFunctionIntegrationTests.java | 2 -- ...yableLoadBalancerExchangeFilterFunctionIntegrationTests.java | 1 - 2 files changed, 3 deletions(-) diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/AbstractLoadBalancerExchangeFilterFunctionIntegrationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/AbstractLoadBalancerExchangeFilterFunctionIntegrationTests.java index 06c84d18..06cf64a4 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/AbstractLoadBalancerExchangeFilterFunctionIntegrationTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/AbstractLoadBalancerExchangeFilterFunctionIntegrationTests.java @@ -25,7 +25,6 @@ import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import reactor.core.publisher.Mono; @@ -120,7 +119,6 @@ abstract class AbstractLoadBalancerExchangeFilterFunctionIntegrationTests { } @Test - @Disabled // FIXME 3.0.0 void badRequestReturnedForIncorrectHost() { assertThatIllegalStateException() .isThrownBy(() -> WebClient.builder() diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/RetryableLoadBalancerExchangeFilterFunctionIntegrationTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/RetryableLoadBalancerExchangeFilterFunctionIntegrationTests.java index ff2d2cd6..9ef145e6 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/RetryableLoadBalancerExchangeFilterFunctionIntegrationTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/RetryableLoadBalancerExchangeFilterFunctionIntegrationTests.java @@ -73,7 +73,6 @@ class RetryableLoadBalancerExchangeFilterFunctionIntegrationTests } @Test - @Disabled void correctResponseReturnedAfterRetryingOnNextServiceInstanceWithBackoff() { loadBalancerProperties.getRetry().getBackoff().setEnabled(true); loadBalancerProperties.getRetry().setMaxRetriesOnSameServiceInstance(1); From 37fa7a7ae8240a7f7d640446b28c50a7060ad160 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Thu, 24 Apr 2025 19:43:01 +0200 Subject: [PATCH 9/9] Adjust to changes in FW. Signed-off-by: Olga Maciaszek-Sharma --- .../cloud/client/loadbalancer/RequestData.java | 7 ++++--- .../CompatibilityVerifierProperties.java | 5 +++-- .../configuration/SpringBootVersionVerifier.java | 10 +++++----- ...eLoadBalancerExchangeFilterFunctionTests.java | 5 +++-- .../configuration/SpringBootDependencyTests.java | 15 ++++++++------- .../core/ServiceInstanceListSupplierBuilder.java | 9 +++++---- .../XForwardedHeadersTransformerTests.java | 10 +++++----- ...lthCheckServiceInstanceListSupplierTests.java | 6 +++--- ...cerServiceInstanceCookieTransformerTests.java | 8 ++++---- ...ySessionServiceInstanceListSupplierTests.java | 16 +++++++++------- .../ServiceInstanceListSuppliersTestUtils.java | 10 ++++++---- .../core/XForwardedHeadersTransformerTests.java | 10 +++++----- ...icrometerStatsLoadBalancerLifecycleTests.java | 14 ++++++++------ 13 files changed, 68 insertions(+), 57 deletions(-) diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/RequestData.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/RequestData.java index 8c320190..2bd21ab6 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/RequestData.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/RequestData.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-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. @@ -29,6 +29,7 @@ import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpRequest; import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.client.ClientRequest; @@ -80,7 +81,7 @@ public class RequestData { } private static MultiValueMap buildCookies(MultiValueMap cookies) { - HttpHeaders newCookies = new HttpHeaders(); + MultiValueMap newCookies = new LinkedMultiValueMap<>(); if (cookies != null) { cookies.forEach((key, value) -> value .forEach(cookie -> newCookies.put(cookie.getName(), Collections.singletonList(cookie.getValue())))); @@ -89,7 +90,7 @@ public class RequestData { } private static MultiValueMap buildCookiesFromHeaders(HttpHeaders headers) { - HttpHeaders newCookies = new HttpHeaders(); + MultiValueMap newCookies = new LinkedMultiValueMap<>(); if (headers == null) { return newCookies; } diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityVerifierProperties.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityVerifierProperties.java index a5663ab7..27cb50da 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityVerifierProperties.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/CompatibilityVerifierProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-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. @@ -22,6 +22,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; /** * @author Marcin Grzejszczak + * @author Olga Maciaszek-Sharma */ @ConfigurationProperties("spring.cloud.compatibility-verifier") public class CompatibilityVerifierProperties { @@ -36,7 +37,7 @@ public class CompatibilityVerifierProperties { * the patch version if you don't want to specify a concrete value. Example: * {@code 3.5.x} */ - private List compatibleBootVersions = List.of("3.5.x"); + private List compatibleBootVersions = List.of("4.0.x"); public boolean isEnabled() { return this.enabled; diff --git a/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/SpringBootVersionVerifier.java b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/SpringBootVersionVerifier.java index 880f49f9..f1e6cb69 100644 --- a/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/SpringBootVersionVerifier.java +++ b/spring-cloud-commons/src/main/java/org/springframework/cloud/configuration/SpringBootVersionVerifier.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-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. @@ -27,7 +27,7 @@ import org.springframework.boot.SpringBootVersion; import org.springframework.util.StringUtils; /** - * Verifies if Spring Boot has proper version. + * Verifies if Spring Boot has the proper version. */ class SpringBootVersionVerifier implements CompatibilityVerifier { @@ -35,7 +35,7 @@ class SpringBootVersionVerifier implements CompatibilityVerifier { final Map ACCEPTED_VERSIONS = new HashMap<>() { { - this.put("3.5", is3_5()); + this.put("4.0", is4_0()); } }; @@ -70,12 +70,12 @@ class SpringBootVersionVerifier implements CompatibilityVerifier { return SpringBootVersion.getVersion(); } - CompatibilityPredicate is3_5() { + CompatibilityPredicate is4_0() { return new CompatibilityPredicate() { @Override public String toString() { - return "Predicate for Boot 3.5"; + return "Predicate for Boot 4.0"; } @Override diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/RetryableLoadBalancerExchangeFilterFunctionTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/RetryableLoadBalancerExchangeFilterFunctionTests.java index 29bef5bc..5c84e5c4 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/RetryableLoadBalancerExchangeFilterFunctionTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/client/loadbalancer/reactive/RetryableLoadBalancerExchangeFilterFunctionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-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. @@ -31,6 +31,7 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; +import org.springframework.util.LinkedMultiValueMap; import org.springframework.web.reactive.function.client.ClientRequest; import org.springframework.web.reactive.function.client.ClientResponse; import org.springframework.web.reactive.function.client.ExchangeFunction; @@ -75,7 +76,7 @@ class RetryableLoadBalancerExchangeFilterFunctionTests { when(factory.getInstance("test")).thenReturn(new TestReactiveLoadBalancer()); when(factory.getProperties(any())).thenReturn(properties); when(clientRequest.headers()).thenReturn(new HttpHeaders()); - when(clientRequest.cookies()).thenReturn(new HttpHeaders()); + when(clientRequest.cookies()).thenReturn(new LinkedMultiValueMap<>()); } diff --git a/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/SpringBootDependencyTests.java b/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/SpringBootDependencyTests.java index 825ddc6a..0bc8910a 100644 --- a/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/SpringBootDependencyTests.java +++ b/spring-cloud-commons/src/test/java/org/springframework/cloud/configuration/SpringBootDependencyTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2020 the original author or authors. + * Copyright 2012-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. @@ -27,6 +27,7 @@ import static org.assertj.core.api.BDDAssertions.then; /** * @author Marcin Grzejszczak + * @author Olga Maciaszek-Sharma */ public class SpringBootDependencyTests { @@ -176,8 +177,8 @@ public class SpringBootDependencyTests { @Test public void should_match_against_current_manifest() { try { - verifyCurrentVersionFromManifest("3.5"); - verifyCurrentVersionFromManifest("3.5.x"); + verifyCurrentVersionFromManifest("4.0"); + verifyCurrentVersionFromManifest("4.0.x"); } catch (AssertionError e) { // if (e.getMessage() != null && e.getMessage().contains("3.3.")) { @@ -204,7 +205,7 @@ public class SpringBootDependencyTests { @Test public void should_match_against_current_predicate() { - List acceptedVersions = Collections.singletonList("3.0"); + List acceptedVersions = Collections.singletonList("4.0"); SpringBootVersionVerifier versionVerifier = new SpringBootVersionVerifier(acceptedVersions) { @Override String getVersionFromManifest() { @@ -212,7 +213,7 @@ public class SpringBootDependencyTests { } }; versionVerifier.ACCEPTED_VERSIONS.clear(); - versionVerifier.ACCEPTED_VERSIONS.put("3.0", versionVerifier.is3_5()); + versionVerifier.ACCEPTED_VERSIONS.put("4.0", versionVerifier.is4_0()); VerificationResult verificationResult = versionVerifier.verify(); @@ -222,7 +223,7 @@ public class SpringBootDependencyTests { @Test public void should_match_against_current_predicate_with_version_ending_with_x() { - List acceptedVersions = Collections.singletonList("3.0.x"); + List acceptedVersions = Collections.singletonList("4.0.x"); SpringBootVersionVerifier versionVerifier = new SpringBootVersionVerifier(acceptedVersions) { @Override String getVersionFromManifest() { @@ -230,7 +231,7 @@ public class SpringBootDependencyTests { } }; versionVerifier.ACCEPTED_VERSIONS.clear(); - versionVerifier.ACCEPTED_VERSIONS.put("3.0", versionVerifier.is3_5()); + versionVerifier.ACCEPTED_VERSIONS.put("4.0", versionVerifier.is4_0()); VerificationResult verificationResult = versionVerifier.verify(); diff --git a/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSupplierBuilder.java b/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSupplierBuilder.java index 19a3c72c..efe1cdab 100644 --- a/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSupplierBuilder.java +++ b/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSupplierBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2023 the original author or authors. + * 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. @@ -380,9 +380,10 @@ public final class ServiceInstanceListSupplierBuilder { return new HealthCheckServiceInstanceListSupplier(delegate, loadBalancerClientFactory, (serviceInstance, healthCheckPath) -> webClient.get() .uri(UriComponentsBuilder.fromUriString(getUri(serviceInstance, healthCheckPath)).build().toUri()) - .exchange() - .flatMap(clientResponse -> clientResponse.releaseBody() - .thenReturn(HttpStatus.OK.equals(clientResponse.statusCode())))); + .retrieve() + .toBodilessEntity() + .map(response -> HttpStatus.OK.equals(response.getStatusCode())) + .onErrorReturn(false)); } private ServiceInstanceListSupplier blockingHealthCheckServiceInstanceListSupplier(RestTemplate restTemplate, diff --git a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/blocking/XForwardedHeadersTransformerTests.java b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/blocking/XForwardedHeadersTransformerTests.java index 6899772b..183cd7d0 100644 --- a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/blocking/XForwardedHeadersTransformerTests.java +++ b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/blocking/XForwardedHeadersTransformerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-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. @@ -65,9 +65,9 @@ class XForwardedHeadersTransformerTests { HttpRequest newRequest = transformer.transformRequest(request, serviceInstance); - assertThat(newRequest.getHeaders()).containsKey("X-Forwarded-Host"); + assertThat(newRequest.getHeaders().containsHeader("X-Forwarded-Host")).isTrue(); assertThat(newRequest.getHeaders().getFirst("X-Forwarded-Host")).isEqualTo("google.com"); - assertThat(newRequest.getHeaders()).containsKey("X-Forwarded-Proto"); + assertThat(newRequest.getHeaders().containsHeader("X-Forwarded-Proto")).isTrue(); assertThat(newRequest.getHeaders().getFirst("X-Forwarded-Proto")).isEqualTo("https"); } @@ -78,8 +78,8 @@ class XForwardedHeadersTransformerTests { XForwardedHeadersTransformer transformer = new XForwardedHeadersTransformer(loadBalancerClientFactory); HttpRequest newRequest = transformer.transformRequest(request, serviceInstance); - assertThat(newRequest.getHeaders()).doesNotContainKey("X-Forwarded-Host"); - assertThat(newRequest.getHeaders()).doesNotContainKey("X-Forwarded-Proto"); + assertThat(newRequest.getHeaders().containsHeader("X-Forwarded-Host")).isFalse(); + assertThat(newRequest.getHeaders().containsHeader("X-Forwarded-Proto")).isFalse(); } } diff --git a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/HealthCheckServiceInstanceListSupplierTests.java b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/HealthCheckServiceInstanceListSupplierTests.java index 1507fbf1..f5889529 100644 --- a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/HealthCheckServiceInstanceListSupplierTests.java +++ b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/HealthCheckServiceInstanceListSupplierTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2023 the original author or authors. + * Copyright 2012-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. @@ -703,7 +703,7 @@ class HealthCheckServiceInstanceListSupplierTests { } @Test - void shouldCheckUseProvidedPortForHealthCheckRequest() { + void shouldCheckUserProvidedPortForHealthCheckRequest() { Throwable exception = catchThrowable(() -> { String serviceId = "ignored-service"; properties.getHealthCheck().setPort(8888); @@ -718,7 +718,7 @@ class HealthCheckServiceInstanceListSupplierTests { listSupplier.isAlive(serviceInstance).block(); }); - assertThat(exception).hasMessageContaining("Connection refused: /127.0.0.1:888"); + assertThat(exception).hasMessageContaining("Connection refused: /127.0.0.1:8888"); } private static Stream healthCheckFunctions() { diff --git a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/LoadBalancerServiceInstanceCookieTransformerTests.java b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/LoadBalancerServiceInstanceCookieTransformerTests.java index a7d8a376..a10f922d 100644 --- a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/LoadBalancerServiceInstanceCookieTransformerTests.java +++ b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/LoadBalancerServiceInstanceCookieTransformerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-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. @@ -74,7 +74,7 @@ class LoadBalancerServiceInstanceCookieTransformerTests { void shouldReturnPassedRequestWhenNoServiceInstance() { HttpRequest newRequest = transformer.transformRequest(request, null); - assertThat(newRequest.getHeaders()).doesNotContainKey(HttpHeaders.COOKIE); + assertThat(newRequest.getHeaders().containsHeader(HttpHeaders.COOKIE)).isFalse(); } @Test @@ -82,7 +82,7 @@ class LoadBalancerServiceInstanceCookieTransformerTests { properties.getStickySession().setInstanceIdCookieName(null); HttpRequest newRequest = transformer.transformRequest(request, serviceInstance); - assertThat(newRequest.getHeaders()).doesNotContainKey(HttpHeaders.COOKIE); + assertThat(newRequest.getHeaders().containsHeader(HttpHeaders.COOKIE)).isFalse(); } @Test @@ -90,7 +90,7 @@ class LoadBalancerServiceInstanceCookieTransformerTests { properties.getStickySession().setInstanceIdCookieName(""); HttpRequest newRequest = transformer.transformRequest(request, serviceInstance); - assertThat(newRequest.getHeaders()).doesNotContainKey(HttpHeaders.COOKIE); + assertThat(newRequest.getHeaders().containsHeader(HttpHeaders.COOKIE)).isFalse(); } diff --git a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/RequestBasedStickySessionServiceInstanceListSupplierTests.java b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/RequestBasedStickySessionServiceInstanceListSupplierTests.java index ad2aee6e..21500ca7 100644 --- a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/RequestBasedStickySessionServiceInstanceListSupplierTests.java +++ b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/RequestBasedStickySessionServiceInstanceListSupplierTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-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. @@ -17,6 +17,7 @@ package org.springframework.cloud.loadbalancer.core; import java.util.Arrays; +import java.util.Collections; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -31,7 +32,8 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties; import org.springframework.cloud.client.loadbalancer.Request; import org.springframework.cloud.client.loadbalancer.RequestData; import org.springframework.cloud.client.loadbalancer.RequestDataContext; -import org.springframework.http.HttpHeaders; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; import org.springframework.web.reactive.function.client.ClientRequest; import static org.assertj.core.api.Assertions.assertThat; @@ -72,8 +74,8 @@ class RequestBasedStickySessionServiceInstanceListSupplierTests { @Test void shouldReturnInstanceBasedOnCookieFromClientRequest() { - HttpHeaders headers = new HttpHeaders(); - headers.add(properties.getStickySession().getInstanceIdCookieName(), "test-1"); + MultiValueMap headers = new LinkedMultiValueMap<>(); + headers.put(properties.getStickySession().getInstanceIdCookieName(), Collections.singletonList("test-1")); when(clientRequest.cookies()).thenReturn(headers); Request request = new DefaultRequest<>( new RequestDataContext(new RequestData(clientRequest))); @@ -86,8 +88,8 @@ class RequestBasedStickySessionServiceInstanceListSupplierTests { @Test void shouldReturnAllDelegateInstancesIfInstanceBasedOnCookieFromClientRequestNotFound() { - HttpHeaders headers = new HttpHeaders(); - headers.add(properties.getStickySession().getInstanceIdCookieName(), "test-4"); + MultiValueMap headers = new LinkedMultiValueMap<>(); + headers.put(properties.getStickySession().getInstanceIdCookieName(), Collections.singletonList("test-4")); when(clientRequest.cookies()).thenReturn(headers); Request request = new DefaultRequest<>( new RequestDataContext(new RequestData(clientRequest))); @@ -99,7 +101,7 @@ class RequestBasedStickySessionServiceInstanceListSupplierTests { @Test void shouldReturnAllInstancesFromDelegateIfClientRequestHasNoCookie() { - when(clientRequest.cookies()).thenReturn(new HttpHeaders()); + when(clientRequest.cookies()).thenReturn(new LinkedMultiValueMap<>()); Request request = new DefaultRequest<>( new RequestDataContext(new RequestData(clientRequest))); diff --git a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSuppliersTestUtils.java b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSuppliersTestUtils.java index 8dceeee7..068ab9f1 100644 --- a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSuppliersTestUtils.java +++ b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/ServiceInstanceListSuppliersTestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2023 the original author or authors. + * 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. @@ -26,6 +26,7 @@ import org.springframework.http.HttpStatus; import org.springframework.web.client.RestClient; import org.springframework.web.client.RestTemplate; import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientRequestException; import org.springframework.web.util.UriComponentsBuilder; import static org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplierBuilder.getUri; @@ -46,9 +47,10 @@ final class ServiceInstanceListSuppliersTestUtils { static BiFunction> healthCheckFunction(WebClient webClient) { return (serviceInstance, healthCheckPath) -> webClient.get() .uri(UriComponentsBuilder.fromUriString(getUri(serviceInstance, healthCheckPath)).build().toUri()) - .exchange() - .flatMap(clientResponse -> clientResponse.releaseBody() - .thenReturn(HttpStatus.OK.equals(clientResponse.statusCode()))); + .retrieve() + .toBodilessEntity() + .map(response -> HttpStatus.OK.equals(response.getStatusCode())) + .onErrorReturn(throwable -> !(throwable instanceof WebClientRequestException), false); } static BiFunction> healthCheckFunction(RestTemplate restTemplate) { diff --git a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/XForwardedHeadersTransformerTests.java b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/XForwardedHeadersTransformerTests.java index 3c6b500c..12cf270d 100644 --- a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/XForwardedHeadersTransformerTests.java +++ b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/core/XForwardedHeadersTransformerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-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. @@ -66,9 +66,9 @@ class XForwardedHeadersTransformerTests { ClientRequest newRequest = transformer.transformRequest(request, serviceInstance); - assertThat(newRequest.headers()).containsKey("X-Forwarded-Host"); + assertThat(newRequest.headers().containsHeader("X-Forwarded-Host")).isTrue(); assertThat(newRequest.headers().getFirst("X-Forwarded-Host")).isEqualTo("spring.io"); - assertThat(newRequest.headers()).containsKey("X-Forwarded-Proto"); + assertThat(newRequest.headers().containsHeader("X-Forwarded-Host")).isTrue(); assertThat(newRequest.headers().getFirst("X-Forwarded-Proto")).isEqualTo("https"); } @@ -79,8 +79,8 @@ class XForwardedHeadersTransformerTests { ClientRequest newRequest = transformer.transformRequest(request, serviceInstance); - assertThat(newRequest.headers()).doesNotContainKey("X-Forwarded-Host"); - assertThat(newRequest.headers()).doesNotContainKey("X-Forwarded-Proto"); + assertThat(newRequest.headers().containsHeader("X-Forwarded-Host")).isFalse(); + assertThat(newRequest.headers().containsHeader("X-Forwarded-Proto")).isFalse(); } } diff --git a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/stats/MicrometerStatsLoadBalancerLifecycleTests.java b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/stats/MicrometerStatsLoadBalancerLifecycleTests.java index c5d7bc5c..e6b14386 100644 --- a/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/stats/MicrometerStatsLoadBalancerLifecycleTests.java +++ b/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/stats/MicrometerStatsLoadBalancerLifecycleTests.java @@ -44,6 +44,7 @@ import org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalanc import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; +import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMapAdapter; import static org.assertj.core.api.Assertions.assertThat; @@ -71,7 +72,7 @@ class MicrometerStatsLoadBalancerLifecycleTests { @Test void shouldRecordSuccessfulTimedRequest() { RequestData requestData = new RequestData(HttpMethod.GET, URI.create("http://test.org/test"), new HttpHeaders(), - new HttpHeaders(), new HashMap<>()); + new LinkedMultiValueMap<>(), new HashMap<>()); Request lbRequest = new DefaultRequest<>(new RequestDataContext(requestData)); Response lbResponse = new DefaultResponse( new DefaultServiceInstance("test-1", "test", "test.org", 8080, false, new HashMap<>())); @@ -93,6 +94,7 @@ class MicrometerStatsLoadBalancerLifecycleTests { Tag.of("serviceInstance.port", "8080"), Tag.of("status", "200"), Tag.of("uri", "/test")); } + @SuppressWarnings("unchecked") @Test void shouldNotAddPathValueWhenDisabled() { ReactiveLoadBalancer.Factory factory = mock(ReactiveLoadBalancer.Factory.class); @@ -102,7 +104,7 @@ class MicrometerStatsLoadBalancerLifecycleTests { MicrometerStatsLoadBalancerLifecycle statsLifecycle = new MicrometerStatsLoadBalancerLifecycle(meterRegistry, factory); RequestData requestData = new RequestData(HttpMethod.GET, URI.create("http://test.org/test"), new HttpHeaders(), - new HttpHeaders(), new HashMap<>()); + new LinkedMultiValueMap<>(), new HashMap<>()); Request lbRequest = new DefaultRequest<>(new RequestDataContext(requestData)); Response lbResponse = new DefaultResponse( new DefaultServiceInstance("test-1", "test", "test.org", 8080, false, new HashMap<>())); @@ -126,7 +128,7 @@ class MicrometerStatsLoadBalancerLifecycleTests { String uriTemplate = "/test/{pathParam}/test"; attributes.put(attributeName, uriTemplate); RequestData requestData = new RequestData(HttpMethod.GET, URI.create("http://test.org/test/123/test"), - new HttpHeaders(), new HttpHeaders(), attributes); + new HttpHeaders(), new LinkedMultiValueMap<>(), attributes); Request lbRequest = new DefaultRequest<>(new RequestDataContext(requestData)); Response lbResponse = new DefaultResponse( new DefaultServiceInstance("test-1", "test", "test.org", 8080, false, new HashMap<>())); @@ -152,7 +154,7 @@ class MicrometerStatsLoadBalancerLifecycleTests { @Test void shouldRecordFailedTimedRequest() { RequestData requestData = new RequestData(HttpMethod.GET, URI.create("http://test.org/test"), new HttpHeaders(), - new HttpHeaders(), new HashMap<>()); + new LinkedMultiValueMap<>(), new HashMap<>()); Request lbRequest = new DefaultRequest<>(new RequestDataContext(requestData)); Response lbResponse = new DefaultResponse( new DefaultServiceInstance("test-1", "test", "test.org", 8080, false, new HashMap<>())); @@ -175,7 +177,7 @@ class MicrometerStatsLoadBalancerLifecycleTests { @Test void shouldNotRecordDiscardedRequest() { RequestData requestData = new RequestData(HttpMethod.GET, URI.create("http://test.org/test"), new HttpHeaders(), - new HttpHeaders(), new HashMap<>()); + new LinkedMultiValueMap<>(), new HashMap<>()); Request lbRequest = new DefaultRequest<>(new RequestDataContext(requestData)); Response lbResponse = new EmptyResponse(); statsLifecycle.onStartRequest(lbRequest, lbResponse); @@ -247,7 +249,7 @@ class MicrometerStatsLoadBalancerLifecycleTests { @Test void shouldHandleNullLoadBalancerResponse() { RequestData requestData = new RequestData(HttpMethod.GET, URI.create("http://test.org/test"), new HttpHeaders(), - new HttpHeaders(), new HashMap<>()); + new LinkedMultiValueMap<>(), new HashMap<>()); Request lbRequest = new DefaultRequest<>(new RequestDataContext(requestData)); assertThatCode(() -> { statsLifecycle.onStartRequest(lbRequest, null);