From 973c048be59c8f12d49360d5c3918534b4881001 Mon Sep 17 00:00:00 2001 From: buildmaster Date: Thu, 17 Feb 2022 16:55:45 +0000 Subject: [PATCH 01/62] Going back to snapshots --- README.adoc | 3 +-- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 4 ++-- spring-cloud-starter-openfeign/pom.xml | 2 +- 6 files changed, 9 insertions(+), 10 deletions(-) diff --git a/README.adoc b/README.adoc index cb097fff2..36916d6ef 100644 --- a/README.adoc +++ b/README.adoc @@ -40,8 +40,7 @@ and binding to the Spring Environment and other Spring programming model idioms. == Building - -:jdkversion: 17 +:jdkversion: 1.8 === Basic Compile and Test diff --git a/docs/pom.xml b/docs/pom.xml index b5cf0503a..f91e9fdb3 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.1 + 3.1.1-SNAPSHOT spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index 52116d830..0410dc394 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.1.1 + 3.1.1-SNAPSHOT pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.1.1 + 3.1.1-SNAPSHOT @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.1.1 + 3.1.1-SNAPSHOT 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 4f102efc3..79e3d7430 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.1 + 3.1.1-SNAPSHOT .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index ef88d8c31..d2c5dc9ae 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 3.1.1 + 3.1.0 spring-cloud-openfeign-dependencies - 3.1.1 + 3.1.1-SNAPSHOT pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index 76da4e1aa..5318f05bf 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.1 + 3.1.1-SNAPSHOT .. spring-cloud-starter-openfeign From aa4b8a280d6817ed02f4819152ca48a5daca0a69 Mon Sep 17 00:00:00 2001 From: buildmaster Date: Thu, 17 Feb 2022 16:55:45 +0000 Subject: [PATCH 02/62] Bumping versions to 3.1.2-SNAPSHOT after release --- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 4 ++-- spring-cloud-starter-openfeign/pom.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index f91e9fdb3..3da8838ae 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index 0410dc394..a32f4a754 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.1.1-SNAPSHOT + 3.1.1 @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 79e3d7430..10865ee15 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index d2c5dc9ae..bf008436e 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 3.1.0 + 3.1.2-SNAPSHOT spring-cloud-openfeign-dependencies - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index 5318f05bf..65d55e912 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.1-SNAPSHOT + 3.1.2-SNAPSHOT .. spring-cloud-starter-openfeign From fe43b6c52b239d62fe4c5f65ac545817845e468d Mon Sep 17 00:00:00 2001 From: buildmaster Date: Thu, 24 Feb 2022 10:52:51 +0000 Subject: [PATCH 03/62] Bumping versions --- README.adoc | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++- pom.xml | 2 +- 2 files changed, 54 insertions(+), 2 deletions(-) diff --git a/README.adoc b/README.adoc index 1637c57eb..cb097fff2 100644 --- a/README.adoc +++ b/README.adoc @@ -40,7 +40,8 @@ and binding to the Spring Environment and other Spring programming model idioms. == Building -:jdkversion: 1.8 + +:jdkversion: 17 === Basic Compile and Test @@ -303,6 +304,57 @@ Go to `File` -> `Settings` -> `Other settings` -> `Checkstyle`. There click on t IMPORTANT: Remember to set the `Scan Scope` to `All sources` since we apply checkstyle rules for production and test sources. +=== Duplicate Finder + +Spring Cloud Build brings along the `basepom:duplicate-finder-maven-plugin`, that enables flagging duplicate and conflicting classes and resources on the java classpath. + +==== Duplicate Finder configuration + +Duplicate finder is *enabled by default* and will run in the `verify` phase of your Maven build, but it will only take effect in your project if you add the `duplicate-finder-maven-plugin` to the `build` section of the projecst's `pom.xml`. + +.pom.xml +[source,xml] +---- + + + + org.basepom.maven + duplicate-finder-maven-plugin + + + +---- + +For other properties, we have set defaults as listed in the https://github.com/basepom/duplicate-finder-maven-plugin/wiki[plugin documentation]. + +You can easily override them but setting the value of the selected property prefixed with `duplicate-finder-maven-plugin`. For example, set `duplicate-finder-maven-plugin.skip` to `true` in order to skip duplicates check in your build. + +If you need to add `ignoredClassPatterns` or `ignoredResourcePatterns` to your setup, make sure to add them in the plugin configuration section of your project: + +[source,xml] +---- + + + + org.basepom.maven + duplicate-finder-maven-plugin + + + org.joda.time.base.BaseDateTime + .*module-info + + + changelog.txt + + + + + + + +---- + + == License The project license file is available https://raw.githubusercontent.com/spring-cloud/spring-cloud-openfeign/main/LICENSE.txt[here]. diff --git a/pom.xml b/pom.xml index afbf00cdb..873196860 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ org.springframework.cloud spring-cloud-build - 3.0.5 + 3.0.6-SNAPSHOT From f662c0547d955683e588c4235ca3b826db01c539 Mon Sep 17 00:00:00 2001 From: buildmaster Date: Thu, 24 Feb 2022 10:53:13 +0000 Subject: [PATCH 04/62] Bumping versions --- README.adoc | 3 ++- pom.xml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/README.adoc b/README.adoc index 36916d6ef..cb097fff2 100644 --- a/README.adoc +++ b/README.adoc @@ -40,7 +40,8 @@ and binding to the Spring Environment and other Spring programming model idioms. == Building -:jdkversion: 1.8 + +:jdkversion: 17 === Basic Compile and Test diff --git a/pom.xml b/pom.xml index a32f4a754..bb395696b 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ org.springframework.cloud spring-cloud-build - 3.1.1 + 3.1.2-SNAPSHOT From 75fc0f6b2eb354645d6ba6b9e54d9778c89b55b2 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Thu, 24 Feb 2022 12:35:02 +0100 Subject: [PATCH 05/62] Unwrap cb exception (#683) --- .../FeignCircuitBreakerInvocationHandler.java | 20 ++++++- .../circuitbreaker/CircuitBreakerTests.java | 54 ++++++++++++++++++- 2 files changed, 71 insertions(+), 3 deletions(-) diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java index da76d1752..a85cf4713 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java @@ -17,6 +17,7 @@ package org.springframework.cloud.openfeign; import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.LinkedHashMap; @@ -29,6 +30,7 @@ import org.springframework.cloud.client.circuitbreaker.CircuitBreaker; import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; +import org.springframework.cloud.client.circuitbreaker.NoFallbackAvailableException; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; @@ -95,15 +97,29 @@ else if ("toString".equals(method.getName())) { try { return this.fallbackMethodMap.get(method).invoke(fallback, args); } - catch (Exception e) { - throw new IllegalStateException(e); + catch (Exception exception) { + unwrapAndRethrow(exception); } + return null; }; return circuitBreaker.run(supplier, fallbackFunction); } return circuitBreaker.run(supplier); } + private void unwrapAndRethrow(Exception exception) { + if (exception instanceof InvocationTargetException || exception instanceof NoFallbackAvailableException) { + Throwable underlyingException = exception.getCause(); + if (underlyingException instanceof RuntimeException) { + throw (RuntimeException) underlyingException; + } + if (underlyingException != null) { + throw new IllegalStateException(underlyingException); + } + throw new IllegalStateException(exception); + } + } + private Supplier asSupplier(final Method method, final Object[] args) { final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); return () -> { diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerTests.java index 1cc4e830b..8ca497247 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerTests.java @@ -16,6 +16,7 @@ package org.springframework.cloud.openfeign.circuitbreaker; +import java.io.IOException; import java.util.function.Function; import org.apache.commons.logging.Log; @@ -47,6 +48,7 @@ import org.springframework.web.bind.annotation.RestController; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; /** * @author Spencer Gibb @@ -62,6 +64,9 @@ class CircuitBreakerTests { @Autowired TestClient testClient; + @Autowired + ExceptionClient exceptionClient; + @Autowired TestClientWithFactory testClientWithFactory; @@ -108,6 +113,17 @@ void test404WithFallbackFactory() { assertThat(testClientWithFactory.getException()).isEqualTo("Fixed response"); } + @Test + void testRuntimeExceptionUnwrapped() { + assertThatExceptionOfType(UnsupportedOperationException.class) + .isThrownBy(() -> exceptionClient.getRuntimeException()); + } + + @Test + void testCheckedExceptionWrapped() { + assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> exceptionClient.getCheckedException()); + } + @FeignClient(name = "test", url = "http://localhost:${server.port}/", fallback = Fallback.class) protected interface TestClient { @@ -119,6 +135,18 @@ protected interface TestClient { } + @FeignClient(name = "exceptionClient", url = "http://localhost:${server.port}/", + fallbackFactory = ExceptionThrowingFallbackFactory.class) + protected interface ExceptionClient { + + @GetMapping("/runtimeException") + Hello getRuntimeException(); + + @GetMapping("/runtimeException") + Hello getCheckedException() throws IOException; + + } + @Component static class Fallback implements TestClient { @@ -156,6 +184,25 @@ public FallbackWithFactory create(Throwable cause) { } + static class ExceptionThrowingFallbackFactory implements FallbackFactory { + + @Override + public ExceptionClient create(Throwable cause) { + return new ExceptionClient() { + @Override + public Hello getRuntimeException() { + throw new UnsupportedOperationException("Not implemented!"); + } + + @Override + public Hello getCheckedException() throws IOException { + throw new IOException(); + } + }; + } + + } + static class FallbackWithFactory implements TestClientWithFactory { @Override @@ -173,7 +220,7 @@ public String getException() { @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration @RestController - @EnableFeignClients(clients = { TestClient.class, TestClientWithFactory.class }) + @EnableFeignClients(clients = { TestClient.class, TestClientWithFactory.class, ExceptionClient.class }) @Import(NoSecurityConfiguration.class) protected static class Application implements TestClient { @@ -225,6 +272,11 @@ TestFallbackFactory testFallbackFactory() { return new TestFallbackFactory(); } + @Bean + ExceptionThrowingFallbackFactory exceptionThrowingFallbackFactory() { + return new ExceptionThrowingFallbackFactory(); + } + } } From 765431dce1a2812eb035345bf5df571f24129148 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Fri, 25 Feb 2022 13:14:16 +0100 Subject: [PATCH 06/62] Fixes gh-680. --- .../openfeign/support/PageJacksonModule.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/PageJacksonModule.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/PageJacksonModule.java index c3b560961..23068bf65 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/PageJacksonModule.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/PageJacksonModule.java @@ -196,6 +196,21 @@ public boolean isEmpty() { return delegate.isEmpty(); } + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return delegate.equals(obj); + } + + @Override + public String toString() { + return delegate.toString(); + } + } } From f2386e365d8228f099d2bbbe596e91f51057f94f Mon Sep 17 00:00:00 2001 From: Ryan Baxter Date: Thu, 10 Mar 2022 19:36:51 -0500 Subject: [PATCH 07/62] Provide an alternate naming convention for CB ids to allow for configuration via configuration properties (#687) Configuration properties cannot contain characters like hash, parens, or commas --- .../main/asciidoc/spring-cloud-openfeign.adoc | 41 ++++++++++ .../openfeign/FeignAutoConfiguration.java | 19 +++++ ...itional-spring-configuration-metadata.json | 6 ++ .../CircuitBreakerAutoConfigurationTests.java | 79 +++++++++++++++++++ 4 files changed, 145 insertions(+) create mode 100644 spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerAutoConfigurationTests.java diff --git a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc index ddc2595f9..371190563 100644 --- a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc +++ b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc @@ -372,6 +372,47 @@ public class FooConfiguration { To enable Spring Cloud CircuitBreaker group set the `feign.circuitbreaker.group.enabled` property to `true` (by default `false`). +[[spring-clou-feign-circuitbreaker-configurationproperties]] +=== Configuring CircuitBreakers With Configuration Properties + +You can configure CircuitBreakers via configuration properties. To do set +`feign.circuitbreaker.alphanumeric-ids.enabled` to `true`. Since +you cannot use characters like `#`, `(`, `)` `,` in configuration property names we need to +change the naming convention for the ids of the circuit breakers generated by OpenFeign. The above +property will do this for you. + +For example, if you had this Feign client + +[source,java,indent=0] +---- +@FeignClienturl = "http://localhost:8080") +public interface DemoClient { + + @GetMapping("demo") + String getDemo(); +} +---- + +You could configure it using configuration properties by doing the following + +[source,yaml,indent=0] +---- +feign: + circuitbreaker: + enabled: true + alphanumeric-ids: + enabled: true +resilience4j: + circuitbreaker: + instances: + DemoClientgetDemo: + minimumNumberOfCalls: 69 + timelimiter: + instances: + DemoClientgetDemo: + timeoutDuration: 10s +---- + [[spring-cloud-feign-circuitbreaker-fallback]] === Feign Spring Cloud CircuitBreaker Fallbacks diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java index 72f536958..ec189e61c 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java @@ -166,10 +166,20 @@ public Targeter defaultFeignTargeter() { @Bean @ConditionalOnMissingBean(CircuitBreakerNameResolver.class) + @ConditionalOnProperty(value = "feign.circuitbreaker.alphanumeric-ids.enabled", + havingValue = "false", matchIfMissing = true) public CircuitBreakerNameResolver circuitBreakerNameResolver() { return new DefaultCircuitBreakerNameResolver(); } + @Bean + @ConditionalOnMissingBean(CircuitBreakerNameResolver.class) + @ConditionalOnProperty(value = "feign.circuitbreaker.alphanumeric-ids.enabled", + havingValue = "true") + public CircuitBreakerNameResolver alphanumericCircuitBreakerNameResolver() { + return new AlphanumericCircuitBreakerNameResolver(); + } + @Bean @ConditionalOnMissingBean @ConditionalOnBean(CircuitBreakerFactory.class) @@ -189,6 +199,15 @@ public String resolveCircuitBreakerName(String feignClientName, Target target } + static class AlphanumericCircuitBreakerNameResolver extends DefaultCircuitBreakerNameResolver { + + @Override + public String resolveCircuitBreakerName(String feignClientName, Target target, Method method) { + return super.resolveCircuitBreakerName(feignClientName, target, method).replaceAll("[^a-zA-Z0-9]", ""); + } + + } + } // the following configuration is for alternate feign clients if diff --git a/spring-cloud-openfeign-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-openfeign-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 90a964687..73ff218b2 100644 --- a/spring-cloud-openfeign-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-openfeign-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -14,6 +14,12 @@ "description": "If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker.", "defaultValue": "false" }, + { + "name": "feign.circuitbreaker.alphanumeric-ids.enabled", + "type": "java.lang.Boolean", + "description": "If true, Circuit Breaker ids will only contain alphanumeric characters to allow for configuration via configuration properties.", + "defaultValue": "false" + }, { "name": "feign.circuitbreaker.group.enabled", "type": "java.lang.Boolean", diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerAutoConfigurationTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerAutoConfigurationTests.java new file mode 100644 index 000000000..983e95586 --- /dev/null +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerAutoConfigurationTests.java @@ -0,0 +1,79 @@ +/* + * Copyright 2013-2022 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.openfeign.circuitbreaker; + +import feign.Target; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.openfeign.CircuitBreakerNameResolver; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * @author Ryan Baxter + */ +public class CircuitBreakerAutoConfigurationTests { + + @SpringBootTest(classes = CircuitBreakerTests.Application.class, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + value = { "spring.application.name=springcircuittest", "spring.jmx.enabled=false", + "feign.circuitbreaker.enabled=true" }) + @Nested + class DefaultNamingStrategy { + + @Autowired + CircuitBreakerNameResolver nameResolver; + + @Test + public void assertDefaultNamingStrategy() throws Exception { + Target target = mock(Target.class); + when(target.type()).thenReturn(CircuitBreakerTests.TestClientWithFactory.class); + assertThat(nameResolver.resolveCircuitBreakerName("foo", target, + CircuitBreakerTests.TestClientWithFactory.class.getMethod("getHello"))) + .isEqualTo("TestClientWithFactory#getHello()"); + } + + } + + @SpringBootTest(classes = CircuitBreakerTests.Application.class, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + value = { "spring.application.name=springcircuittest", "spring.jmx.enabled=false", + "feign.circuitbreaker.enabled=true", + "feign.circuitbreaker.alphanumeric-ids.enabled=true" }) + @Nested + class AlphanumericNamingStrategy { + + @Autowired + CircuitBreakerNameResolver nameResolver; + + @Test + public void assertAlphanumericNamingStrategy() throws Exception { + Target target = mock(Target.class); + when(target.type()).thenReturn(CircuitBreakerTests.TestClientWithFactory.class); + assertThat(nameResolver.resolveCircuitBreakerName("foo", target, + CircuitBreakerTests.TestClientWithFactory.class.getMethod("getHello"))) + .isEqualTo("TestClientWithFactorygetHello"); + } + + } + +} From 965c168c2c2d5f2630ba9b9e780722fc4b6b1550 Mon Sep 17 00:00:00 2001 From: buildmaster Date: Fri, 11 Mar 2022 10:57:35 +0000 Subject: [PATCH 08/62] Bumping versions --- docs/src/main/asciidoc/_configprops.adoc | 1 + .../cloud/openfeign/FeignAutoConfiguration.java | 7 +++---- .../CircuitBreakerAutoConfigurationTests.java | 3 +-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/src/main/asciidoc/_configprops.adoc b/docs/src/main/asciidoc/_configprops.adoc index f9dd180c2..14058c518 100644 --- a/docs/src/main/asciidoc/_configprops.adoc +++ b/docs/src/main/asciidoc/_configprops.adoc @@ -2,6 +2,7 @@ |Name | Default | Description |feign.autoconfiguration.jackson.enabled | `false` | If true, PageJacksonModule and SortJacksonModule bean will be provided for Jackson page decoding. +|feign.circuitbreaker.alphanumeric-ids.enabled | `false` | If true, Circuit Breaker ids will only contain alphanumeric characters to allow for configuration via configuration properties. |feign.circuitbreaker.enabled | `false` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker. |feign.circuitbreaker.group.enabled | `false` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker with with group. |feign.client.config | | diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java index ec189e61c..8daecea0f 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java @@ -166,16 +166,15 @@ public Targeter defaultFeignTargeter() { @Bean @ConditionalOnMissingBean(CircuitBreakerNameResolver.class) - @ConditionalOnProperty(value = "feign.circuitbreaker.alphanumeric-ids.enabled", - havingValue = "false", matchIfMissing = true) + @ConditionalOnProperty(value = "feign.circuitbreaker.alphanumeric-ids.enabled", havingValue = "false", + matchIfMissing = true) public CircuitBreakerNameResolver circuitBreakerNameResolver() { return new DefaultCircuitBreakerNameResolver(); } @Bean @ConditionalOnMissingBean(CircuitBreakerNameResolver.class) - @ConditionalOnProperty(value = "feign.circuitbreaker.alphanumeric-ids.enabled", - havingValue = "true") + @ConditionalOnProperty(value = "feign.circuitbreaker.alphanumeric-ids.enabled", havingValue = "true") public CircuitBreakerNameResolver alphanumericCircuitBreakerNameResolver() { return new AlphanumericCircuitBreakerNameResolver(); } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerAutoConfigurationTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerAutoConfigurationTests.java index 983e95586..3c8052401 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerAutoConfigurationTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerAutoConfigurationTests.java @@ -57,8 +57,7 @@ public void assertDefaultNamingStrategy() throws Exception { @SpringBootTest(classes = CircuitBreakerTests.Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, value = { "spring.application.name=springcircuittest", "spring.jmx.enabled=false", - "feign.circuitbreaker.enabled=true", - "feign.circuitbreaker.alphanumeric-ids.enabled=true" }) + "feign.circuitbreaker.enabled=true", "feign.circuitbreaker.alphanumeric-ids.enabled=true" }) @Nested class AlphanumericNamingStrategy { From 12efc629a3ee4062e4398acdb0b53fdec018059d Mon Sep 17 00:00:00 2001 From: Bhavya Agrawal <72398995+Bhavya-official@users.noreply.github.com> Date: Wed, 16 Mar 2022 18:23:35 +0530 Subject: [PATCH 09/62] [CORRECTION] Add Test Support for @RequestMapping NoPath, OnlySlashPath, MissingSlashLeadingPath. (#692) --- .../support/SpringMvcContractTests.java | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java index 9637d5a69..44f85862f 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java @@ -78,7 +78,9 @@ * @author Olga Maciaszek-Sharma * @author Szymon Linowski * @author Sam Kruglov - */ + * @author Bhavya Agrawal + **/ + class SpringMvcContractTests { private static final Class EXECUTABLE_TYPE; @@ -171,6 +173,41 @@ void testProcessAnnotations_Simple() throws Exception { assertThat(data.indexToName().get(0).iterator().next()).isEqualTo("id"); } + @Test + void testProcessAnnotations_SimpleNoPath() throws Exception { + Method method = TestTemplate_Simple.class.getDeclaredMethod("getTest"); + MethodMetadata data = contract.parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()).isEqualTo("/"); + assertThat(data.template().method()).isEqualTo("GET"); + assertThat(data.template().headers().get("Accept").iterator().next()) + .isEqualTo(MediaType.APPLICATION_JSON_VALUE); + } + + @Test + void testProcessAnnotations_SimplePathIsOnlyASlash() throws Exception { + Method method = TestTemplate_Simple.class.getDeclaredMethod("getSlashPath", String.class); + MethodMetadata data = contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()).isEqualTo("/?id=" + "{id}"); + assertThat(data.template().method()).isEqualTo("GET"); + assertThat(data.template().headers().get("Accept").iterator().next()) + .isEqualTo(MediaType.APPLICATION_JSON_VALUE); + } + + @Test + void testProcessAnnotations_MissingLeadingSlashInPath() throws Exception { + Method method = TestTemplate_Simple.class.getDeclaredMethod("getTestNoLeadingSlash", String.class); + MethodMetadata data = contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()).isEqualTo("/test?name=" + "{name}"); + assertThat(data.template().method()).isEqualTo("GET"); + assertThat(data.template().headers().get("Accept").iterator().next()) + .isEqualTo(MediaType.APPLICATION_JSON_VALUE); + } + @Test void testProcessAnnotations_SimpleGetMapping() throws Exception { Method method = TestTemplate_Simple.class.getDeclaredMethod("getMappingTest", String.class); @@ -616,6 +653,12 @@ public interface TestTemplate_Simple { @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE) TestObject postMappingTest(@RequestBody TestObject object); + @GetMapping(value = "/", produces = MediaType.APPLICATION_JSON_VALUE) + ResponseEntity getSlashPath(@RequestParam("id") String id); + + @GetMapping(path = "test", produces = MediaType.APPLICATION_JSON_VALUE) + ResponseEntity getTestNoLeadingSlash(@RequestParam("name") String name); + } @RequestMapping("/prepend/{classId}") From d76869f2145eeba40128238038fcb42a96d0ba40 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Wed, 16 Mar 2022 13:56:57 +0100 Subject: [PATCH 10/62] Reformat. --- .../openfeign/support/SpringMvcContractTests.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java index 44f85862f..f789bfa70 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java @@ -181,31 +181,29 @@ void testProcessAnnotations_SimpleNoPath() throws Exception { assertThat(data.template().url()).isEqualTo("/"); assertThat(data.template().method()).isEqualTo("GET"); assertThat(data.template().headers().get("Accept").iterator().next()) - .isEqualTo(MediaType.APPLICATION_JSON_VALUE); + .isEqualTo(MediaType.APPLICATION_JSON_VALUE); } @Test void testProcessAnnotations_SimplePathIsOnlyASlash() throws Exception { Method method = TestTemplate_Simple.class.getDeclaredMethod("getSlashPath", String.class); - MethodMetadata data = contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); + MethodMetadata data = contract.parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()).isEqualTo("/?id=" + "{id}"); assertThat(data.template().method()).isEqualTo("GET"); assertThat(data.template().headers().get("Accept").iterator().next()) - .isEqualTo(MediaType.APPLICATION_JSON_VALUE); + .isEqualTo(MediaType.APPLICATION_JSON_VALUE); } @Test void testProcessAnnotations_MissingLeadingSlashInPath() throws Exception { Method method = TestTemplate_Simple.class.getDeclaredMethod("getTestNoLeadingSlash", String.class); - MethodMetadata data = contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); + MethodMetadata data = contract.parseAndValidateMetadata(method.getDeclaringClass(), method); assertThat(data.template().url()).isEqualTo("/test?name=" + "{name}"); assertThat(data.template().method()).isEqualTo("GET"); assertThat(data.template().headers().get("Accept").iterator().next()) - .isEqualTo(MediaType.APPLICATION_JSON_VALUE); + .isEqualTo(MediaType.APPLICATION_JSON_VALUE); } @Test From 573d933b3deea93ad682fa6e3896d101c50449da Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Tue, 29 Mar 2022 17:04:32 +0200 Subject: [PATCH 11/62] Adjust to changes in commons. --- .../FeignBlockingLoadBalancerClient.java | 3 ++- .../openfeign/loadbalancer/LoadBalancerUtils.java | 14 +++++++++----- .../RetryableFeignBlockingLoadBalancerClient.java | 5 +++-- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/FeignBlockingLoadBalancerClient.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/FeignBlockingLoadBalancerClient.java index e0fe6e1e5..6bf5f57d3 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/FeignBlockingLoadBalancerClient.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/FeignBlockingLoadBalancerClient.java @@ -110,8 +110,9 @@ public Response execute(Request request, Request.Options options) throws IOExcep } String reconstructedUrl = loadBalancerClient.reconstructURI(instance, originalUri).toString(); Request newRequest = buildRequest(request, reconstructedUrl); + LoadBalancerProperties loadBalancerProperties = loadBalancerClientFactory.getProperties(serviceId); return executeWithLoadBalancerLifecycleProcessing(delegate, options, newRequest, lbRequest, lbResponse, - supportedLifecycleProcessors); + supportedLifecycleProcessors, loadBalancerProperties.isUseRawStatusCodeInResponseData()); } protected Request buildRequest(Request request, String reconstructedUrl) { diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/LoadBalancerUtils.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/LoadBalancerUtils.java index 829880f6f..66d6c1ee9 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/LoadBalancerUtils.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/LoadBalancerUtils.java @@ -50,14 +50,15 @@ private LoadBalancerUtils() { static Response executeWithLoadBalancerLifecycleProcessing(Client feignClient, Request.Options options, Request feignRequest, org.springframework.cloud.client.loadbalancer.Request lbRequest, org.springframework.cloud.client.loadbalancer.Response lbResponse, - Set supportedLifecycleProcessors, boolean loadBalanced) throws IOException { + Set supportedLifecycleProcessors, boolean loadBalanced, boolean useRawStatusCodes) + throws IOException { supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStartRequest(lbRequest, lbResponse)); try { Response response = feignClient.execute(feignRequest, options); if (loadBalanced) { supportedLifecycleProcessors.forEach( lifecycle -> lifecycle.onComplete(new CompletionContext<>(CompletionContext.Status.SUCCESS, - lbRequest, lbResponse, buildResponseData(response)))); + lbRequest, lbResponse, buildResponseData(response, useRawStatusCodes)))); } return response; } @@ -70,9 +71,12 @@ static Response executeWithLoadBalancerLifecycleProcessing(Client feignClient, R } } - static ResponseData buildResponseData(Response response) { + static ResponseData buildResponseData(Response response, boolean useRawStatusCodes) { HttpHeaders responseHeaders = new HttpHeaders(); response.headers().forEach((key, value) -> responseHeaders.put(key, new ArrayList<>(value))); + if (useRawStatusCodes) { + return new ResponseData(responseHeaders, null, buildRequestData(response.request()), response.status()); + } return new ResponseData(HttpStatus.resolve(response.status()), responseHeaders, null, buildRequestData(response.request())); } @@ -87,9 +91,9 @@ static RequestData buildRequestData(Request request) { static Response executeWithLoadBalancerLifecycleProcessing(Client feignClient, Request.Options options, Request feignRequest, org.springframework.cloud.client.loadbalancer.Request lbRequest, org.springframework.cloud.client.loadbalancer.Response lbResponse, - Set supportedLifecycleProcessors) throws IOException { + Set supportedLifecycleProcessors, boolean useRawStatusCodes) throws IOException { return executeWithLoadBalancerLifecycleProcessing(feignClient, options, feignRequest, lbRequest, lbResponse, - supportedLifecycleProcessors, true); + supportedLifecycleProcessors, true, useRawStatusCodes); } } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClient.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClient.java index f9c3a7c5f..c38a75324 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClient.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClient.java @@ -163,9 +163,10 @@ public Response execute(Request request, Request.Options options) throws IOExcep } org.springframework.cloud.client.loadbalancer.Response lbResponse = new DefaultResponse( retrievedServiceInstance); + LoadBalancerProperties loadBalancerProperties = loadBalancerClientFactory.getProperties(serviceId); Response response = LoadBalancerUtils.executeWithLoadBalancerLifecycleProcessing(delegate, options, - feignRequest, lbRequest, lbResponse, supportedLifecycleProcessors, - retrievedServiceInstance != null); + feignRequest, lbRequest, lbResponse, supportedLifecycleProcessors, retrievedServiceInstance != null, + loadBalancerProperties.isUseRawStatusCodeInResponseData()); int responseStatus = response.status(); if (retryPolicy != null && retryPolicy.retryableStatusCode(responseStatus)) { if (LOG.isDebugEnabled()) { From 86a2f28a9ea90c203e13b9979165b9603a24c4d6 Mon Sep 17 00:00:00 2001 From: buildmaster Date: Tue, 26 Apr 2022 16:00:40 +0000 Subject: [PATCH 12/62] Update SNAPSHOT to 3.1.2 --- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 4 ++-- spring-cloud-starter-openfeign/pom.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 3da8838ae..069bf7667 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2-SNAPSHOT + 3.1.2 spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index bb395696b..a05efcb29 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.1.2-SNAPSHOT + 3.1.2 pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.1.2-SNAPSHOT + 3.1.2 @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.1.2-SNAPSHOT + 3.1.2 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 10865ee15..1af1508cf 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2-SNAPSHOT + 3.1.2 .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index bf008436e..ec51eed67 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 3.1.2-SNAPSHOT + 3.1.2 spring-cloud-openfeign-dependencies - 3.1.2-SNAPSHOT + 3.1.2 pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index 65d55e912..63a61712f 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2-SNAPSHOT + 3.1.2 .. spring-cloud-starter-openfeign From 4cd0c363be6b730c50d77b489b8eeff61e235b0f Mon Sep 17 00:00:00 2001 From: buildmaster Date: Tue, 26 Apr 2022 16:02:51 +0000 Subject: [PATCH 13/62] Going back to snapshots --- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 4 ++-- spring-cloud-starter-openfeign/pom.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 069bf7667..3da8838ae 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2 + 3.1.2-SNAPSHOT spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index a05efcb29..bb395696b 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.1.2 + 3.1.2-SNAPSHOT pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.1.2 + 3.1.2-SNAPSHOT @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.1.2 + 3.1.2-SNAPSHOT 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 1af1508cf..10865ee15 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2 + 3.1.2-SNAPSHOT .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index ec51eed67..bf008436e 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 3.1.2 + 3.1.2-SNAPSHOT spring-cloud-openfeign-dependencies - 3.1.2 + 3.1.2-SNAPSHOT pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index 63a61712f..65d55e912 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2 + 3.1.2-SNAPSHOT .. spring-cloud-starter-openfeign From 909da0afdf94580faaee80e04f4e0dce7390face Mon Sep 17 00:00:00 2001 From: buildmaster Date: Tue, 26 Apr 2022 16:02:51 +0000 Subject: [PATCH 14/62] Bumping versions to 3.1.3-SNAPSHOT after release --- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 4 ++-- spring-cloud-starter-openfeign/pom.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 3da8838ae..019e8334a 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2-SNAPSHOT + 3.1.3-SNAPSHOT spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index bb395696b..409bc69a3 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.1.2-SNAPSHOT + 3.1.3-SNAPSHOT pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.1.2-SNAPSHOT + 3.1.3-SNAPSHOT @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.1.2-SNAPSHOT + 3.1.3-SNAPSHOT 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 10865ee15..a20f3f13d 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2-SNAPSHOT + 3.1.3-SNAPSHOT .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index bf008436e..a4815cf02 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 3.1.2-SNAPSHOT + 3.1.3-SNAPSHOT spring-cloud-openfeign-dependencies - 3.1.2-SNAPSHOT + 3.1.3-SNAPSHOT pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index 65d55e912..b32963354 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2-SNAPSHOT + 3.1.3-SNAPSHOT .. spring-cloud-starter-openfeign From 6704982bcd1d3be22b396cc745483b3f40ab56f7 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Wed, 27 Apr 2022 11:09:30 +0200 Subject: [PATCH 15/62] Revert "Bumping versions to 3.1.3-SNAPSHOT after release" This reverts commit 909da0afdf94580faaee80e04f4e0dce7390face. --- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 4 ++-- spring-cloud-starter-openfeign/pom.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 019e8334a..3da8838ae 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.3-SNAPSHOT + 3.1.2-SNAPSHOT spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index 409bc69a3..bb395696b 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.1.3-SNAPSHOT + 3.1.2-SNAPSHOT pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.1.3-SNAPSHOT + 3.1.2-SNAPSHOT @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.1.3-SNAPSHOT + 3.1.2-SNAPSHOT 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index a20f3f13d..10865ee15 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.3-SNAPSHOT + 3.1.2-SNAPSHOT .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index a4815cf02..bf008436e 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 3.1.3-SNAPSHOT + 3.1.2-SNAPSHOT spring-cloud-openfeign-dependencies - 3.1.3-SNAPSHOT + 3.1.2-SNAPSHOT pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index b32963354..65d55e912 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.3-SNAPSHOT + 3.1.2-SNAPSHOT .. spring-cloud-starter-openfeign From 1ff643f4c199dd3f261bd486d8b3f93447535a64 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Wed, 27 Apr 2022 11:09:33 +0200 Subject: [PATCH 16/62] Revert "Going back to snapshots" This reverts commit 4cd0c363be6b730c50d77b489b8eeff61e235b0f. --- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 4 ++-- spring-cloud-starter-openfeign/pom.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 3da8838ae..069bf7667 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2-SNAPSHOT + 3.1.2 spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index bb395696b..a05efcb29 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.1.2-SNAPSHOT + 3.1.2 pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.1.2-SNAPSHOT + 3.1.2 @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.1.2-SNAPSHOT + 3.1.2 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 10865ee15..1af1508cf 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2-SNAPSHOT + 3.1.2 .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index bf008436e..ec51eed67 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 3.1.2-SNAPSHOT + 3.1.2 spring-cloud-openfeign-dependencies - 3.1.2-SNAPSHOT + 3.1.2 pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index 65d55e912..63a61712f 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2-SNAPSHOT + 3.1.2 .. spring-cloud-starter-openfeign From a3f270d3b44983ca780f8322d261b7108d73af86 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Wed, 27 Apr 2022 11:09:37 +0200 Subject: [PATCH 17/62] Revert "Update SNAPSHOT to 3.1.2" This reverts commit 86a2f28a9ea90c203e13b9979165b9603a24c4d6. --- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 4 ++-- spring-cloud-starter-openfeign/pom.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 069bf7667..3da8838ae 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2 + 3.1.2-SNAPSHOT spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index a05efcb29..bb395696b 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.1.2 + 3.1.2-SNAPSHOT pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.1.2 + 3.1.2-SNAPSHOT @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.1.2 + 3.1.2-SNAPSHOT 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 1af1508cf..10865ee15 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2 + 3.1.2-SNAPSHOT .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index ec51eed67..bf008436e 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 3.1.2 + 3.1.2-SNAPSHOT spring-cloud-openfeign-dependencies - 3.1.2 + 3.1.2-SNAPSHOT pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index 63a61712f..65d55e912 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2 + 3.1.2-SNAPSHOT .. spring-cloud-starter-openfeign From 9f160e7688437a51edce3c66107b8aed386082dc Mon Sep 17 00:00:00 2001 From: buildmaster Date: Wed, 27 Apr 2022 10:51:06 +0000 Subject: [PATCH 18/62] Update SNAPSHOT to 3.1.2 --- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 4 ++-- spring-cloud-starter-openfeign/pom.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 3da8838ae..069bf7667 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2-SNAPSHOT + 3.1.2 spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index bb395696b..a05efcb29 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.1.2-SNAPSHOT + 3.1.2 pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.1.2-SNAPSHOT + 3.1.2 @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.1.2-SNAPSHOT + 3.1.2 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 10865ee15..1af1508cf 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2-SNAPSHOT + 3.1.2 .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index bf008436e..ec51eed67 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 3.1.2-SNAPSHOT + 3.1.2 spring-cloud-openfeign-dependencies - 3.1.2-SNAPSHOT + 3.1.2 pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index 65d55e912..63a61712f 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2-SNAPSHOT + 3.1.2 .. spring-cloud-starter-openfeign From 434ce223286c51eb0cca1cbcbf090b27fb677b6e Mon Sep 17 00:00:00 2001 From: buildmaster Date: Wed, 27 Apr 2022 10:53:18 +0000 Subject: [PATCH 19/62] Going back to snapshots --- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 4 ++-- spring-cloud-starter-openfeign/pom.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 069bf7667..3da8838ae 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2 + 3.1.2-SNAPSHOT spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index a05efcb29..bb395696b 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.1.2 + 3.1.2-SNAPSHOT pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.1.2 + 3.1.2-SNAPSHOT @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.1.2 + 3.1.2-SNAPSHOT 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 1af1508cf..10865ee15 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2 + 3.1.2-SNAPSHOT .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index ec51eed67..bf008436e 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 3.1.2 + 3.1.2-SNAPSHOT spring-cloud-openfeign-dependencies - 3.1.2 + 3.1.2-SNAPSHOT pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index 63a61712f..65d55e912 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2 + 3.1.2-SNAPSHOT .. spring-cloud-starter-openfeign From a142f6f2c07fd05b244486f9527ce53c10ae6983 Mon Sep 17 00:00:00 2001 From: buildmaster Date: Wed, 27 Apr 2022 10:53:18 +0000 Subject: [PATCH 20/62] Bumping versions to 3.1.3-SNAPSHOT after release --- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 4 ++-- spring-cloud-starter-openfeign/pom.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 3da8838ae..019e8334a 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2-SNAPSHOT + 3.1.3-SNAPSHOT spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index bb395696b..b44ead6d0 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.1.2-SNAPSHOT + 3.1.3-SNAPSHOT pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.1.2-SNAPSHOT + 3.1.2 @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.1.2-SNAPSHOT + 3.1.3-SNAPSHOT 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 10865ee15..a20f3f13d 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2-SNAPSHOT + 3.1.3-SNAPSHOT .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index bf008436e..a4815cf02 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 3.1.2-SNAPSHOT + 3.1.3-SNAPSHOT spring-cloud-openfeign-dependencies - 3.1.2-SNAPSHOT + 3.1.3-SNAPSHOT pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index 65d55e912..b32963354 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.2-SNAPSHOT + 3.1.3-SNAPSHOT .. spring-cloud-starter-openfeign From 1bed516e03ac19474c104f6a9cf0b4646363c0e1 Mon Sep 17 00:00:00 2001 From: buildmaster Date: Thu, 28 Apr 2022 10:56:37 +0000 Subject: [PATCH 21/62] Bumping versions --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index b44ead6d0..409bc69a3 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ org.springframework.cloud spring-cloud-build - 3.1.2 + 3.1.3-SNAPSHOT From 4cba1de153b748f483784eaf5eb57287770c9f61 Mon Sep 17 00:00:00 2001 From: Spencer Gibb Date: Thu, 26 May 2022 12:08:43 -0400 Subject: [PATCH 22/62] don't upload docs to maven central --- docs/pom.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/pom.xml b/docs/pom.xml index 019e8334a..33fef54c8 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -17,6 +17,8 @@ ${basedir}/.. feign.* deploy + + none From bc7d39fa0413f517120c7208ce67fc8d135f762f Mon Sep 17 00:00:00 2001 From: buildmaster Date: Thu, 26 May 2022 20:27:01 +0000 Subject: [PATCH 23/62] Update SNAPSHOT to 3.1.3 --- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 4 ++-- spring-cloud-starter-openfeign/pom.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 33fef54c8..46875be7d 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.3-SNAPSHOT + 3.1.3 spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index 409bc69a3..30b222aab 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.1.3-SNAPSHOT + 3.1.3 pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.1.3-SNAPSHOT + 3.1.3 @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.1.3-SNAPSHOT + 3.1.3 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index a20f3f13d..32896f2e3 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.3-SNAPSHOT + 3.1.3 .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index a4815cf02..34b876712 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 3.1.3-SNAPSHOT + 3.1.3 spring-cloud-openfeign-dependencies - 3.1.3-SNAPSHOT + 3.1.3 pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index b32963354..08c9ea7e5 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.3-SNAPSHOT + 3.1.3 .. spring-cloud-starter-openfeign From f8b3ed971e19b90a7c5a74a7ff492f6978505ea2 Mon Sep 17 00:00:00 2001 From: spencergibb Date: Fri, 27 May 2022 13:45:43 -0400 Subject: [PATCH 24/62] Bumps to next snapshot version --- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 4 ++-- spring-cloud-starter-openfeign/pom.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 46875be7d..c3e75e94b 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.3 + 3.1.4-SNAPSHOT spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index 30b222aab..4e5102c5b 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.1.3 + 3.1.4-SNAPSHOT pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.1.3 + 3.1.4-SNAPSHOT @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.1.3 + 3.1.4-SNAPSHOT 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 32896f2e3..1ce746f31 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.3 + 3.1.4-SNAPSHOT .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index 34b876712..a5028d5a6 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 3.1.3 + 3.1.4-SNAPSHOT spring-cloud-openfeign-dependencies - 3.1.3 + 3.1.4-SNAPSHOT pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index 08c9ea7e5..72253452c 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.3 + 3.1.4-SNAPSHOT .. spring-cloud-starter-openfeign From 6c3b8c257e60c4cae69b0e0601675c19242945c2 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Fri, 25 Feb 2022 13:14:16 +0100 Subject: [PATCH 25/62] Fixes gh-680. --- .../openfeign/support/PageJacksonModule.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/PageJacksonModule.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/PageJacksonModule.java index a2c54baf0..d3c60849f 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/PageJacksonModule.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/PageJacksonModule.java @@ -194,6 +194,21 @@ public boolean isEmpty() { return delegate.isEmpty(); } + @Override + public int hashCode() { + return delegate.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return delegate.equals(obj); + } + + @Override + public String toString() { + return delegate.toString(); + } + } } From 9023cc2dc55e7871f16a1e3f1970a6a9b5b03fab Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Wed, 23 Feb 2022 17:22:13 +0100 Subject: [PATCH 26/62] Unwarap InvocationTargetException and NoFallbackAvailableException. --- .../FeignCircuitBreakerInvocationHandler.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java index da76d1752..9f0701aae 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java @@ -17,6 +17,7 @@ package org.springframework.cloud.openfeign; import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.LinkedHashMap; @@ -29,6 +30,7 @@ import org.springframework.cloud.client.circuitbreaker.CircuitBreaker; import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory; +import org.springframework.cloud.client.circuitbreaker.NoFallbackAvailableException; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; @@ -95,15 +97,26 @@ else if ("toString".equals(method.getName())) { try { return this.fallbackMethodMap.get(method).invoke(fallback, args); } - catch (Exception e) { - throw new IllegalStateException(e); + catch (Exception exception) { + unwrapAndRethrow(exception); } + return null; }; return circuitBreaker.run(supplier, fallbackFunction); } return circuitBreaker.run(supplier); } + private void unwrapAndRethrow(Exception exception) { + if (exception instanceof InvocationTargetException || exception instanceof NoFallbackAvailableException) { + Throwable underlyingException = exception.getCause(); + if (underlyingException instanceof RuntimeException) { + throw (RuntimeException) underlyingException; + } + throw new IllegalStateException(exception); + } + } + private Supplier asSupplier(final Method method, final Object[] args) { final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); return () -> { From ff96850ec0570ef2190ba388111c5e717bffe9a7 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Thu, 24 Feb 2022 12:02:11 +0100 Subject: [PATCH 27/62] Wrap underlying checked exception with IllegalStateException. Add tests. --- .../FeignCircuitBreakerInvocationHandler.java | 3 ++ .../circuitbreaker/CircuitBreakerTests.java | 54 ++++++++++++++++++- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java index 9f0701aae..a85cf4713 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java @@ -113,6 +113,9 @@ private void unwrapAndRethrow(Exception exception) { if (underlyingException instanceof RuntimeException) { throw (RuntimeException) underlyingException; } + if (underlyingException != null) { + throw new IllegalStateException(underlyingException); + } throw new IllegalStateException(exception); } } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerTests.java index 4194aaa6b..ecc598bb3 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerTests.java @@ -16,6 +16,7 @@ package org.springframework.cloud.openfeign.circuitbreaker; +import java.io.IOException; import java.util.function.Function; import org.apache.commons.logging.Log; @@ -49,6 +50,7 @@ import org.springframework.web.bind.annotation.RestController; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; /** * @author Spencer Gibb @@ -65,6 +67,9 @@ public class CircuitBreakerTests { @Autowired TestClient testClient; + @Autowired + ExceptionClient exceptionClient; + @Autowired TestClientWithFactory testClientWithFactory; @@ -111,6 +116,17 @@ public void test404WithFallbackFactory() { assertThat(testClientWithFactory.getException()).isEqualTo("Fixed response"); } + @Test + void testRuntimeExceptionUnwrapped() { + assertThatExceptionOfType(UnsupportedOperationException.class) + .isThrownBy(() -> exceptionClient.getRuntimeException()); + } + + @Test + void testCheckedExceptionWrapped() { + assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> exceptionClient.getCheckedException()); + } + @FeignClient(name = "test", url = "http://localhost:${server.port}/", fallback = Fallback.class) protected interface TestClient { @@ -122,6 +138,18 @@ protected interface TestClient { } + @FeignClient(name = "exceptionClient", url = "http://localhost:${server.port}/", + fallbackFactory = ExceptionThrowingFallbackFactory.class) + protected interface ExceptionClient { + + @GetMapping("/runtimeException") + Hello getRuntimeException(); + + @GetMapping("/runtimeException") + Hello getCheckedException() throws IOException; + + } + @Component static class Fallback implements TestClient { @@ -159,6 +187,25 @@ public FallbackWithFactory create(Throwable cause) { } + static class ExceptionThrowingFallbackFactory implements FallbackFactory { + + @Override + public ExceptionClient create(Throwable cause) { + return new ExceptionClient() { + @Override + public Hello getRuntimeException() { + throw new UnsupportedOperationException("Not implemented!"); + } + + @Override + public Hello getCheckedException() throws IOException { + throw new IOException(); + } + }; + } + + } + static class FallbackWithFactory implements TestClientWithFactory { @Override @@ -176,7 +223,7 @@ public String getException() { @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration @RestController - @EnableFeignClients(clients = { TestClient.class, TestClientWithFactory.class }) + @EnableFeignClients(clients = { TestClient.class, TestClientWithFactory.class, ExceptionClient.class }) @Import(NoSecurityConfiguration.class) protected static class Application implements TestClient { @@ -228,6 +275,11 @@ TestFallbackFactory testFallbackFactory() { return new TestFallbackFactory(); } + @Bean + ExceptionThrowingFallbackFactory exceptionThrowingFallbackFactory() { + return new ExceptionThrowingFallbackFactory(); + } + } } From d4832788f6f69c7f52fbef54d1795f44b9e64eb5 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Tue, 28 Jun 2022 15:02:16 +0200 Subject: [PATCH 28/62] Resolve conflicts for backport. --- .../cloud/openfeign/circuitbreaker/CircuitBreakerTests.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerTests.java index ecc598bb3..3dc1cad5e 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/CircuitBreakerTests.java @@ -117,13 +117,13 @@ public void test404WithFallbackFactory() { } @Test - void testRuntimeExceptionUnwrapped() { + public void testRuntimeExceptionUnwrapped() { assertThatExceptionOfType(UnsupportedOperationException.class) .isThrownBy(() -> exceptionClient.getRuntimeException()); } @Test - void testCheckedExceptionWrapped() { + public void testCheckedExceptionWrapped() { assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> exceptionClient.getCheckedException()); } From 24d9b08d5c3de3f45cd15c139330fefb113f150c Mon Sep 17 00:00:00 2001 From: Olga MaciaszekSharma Date: Mon, 17 Jan 2022 12:41:20 +0100 Subject: [PATCH 29/62] Backport bugfix and resolve conflicts. --- .../openfeign/FeignClientFactoryBean.java | 3 ++ .../openfeign/FeignClientsRegistrar.java | 6 ++- .../openfeign/FeignClientsRegistrarTests.java | 53 +++++++++++++++++++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java index ef9971ee5..004b9a0d7 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java @@ -443,6 +443,9 @@ T getTarget() { } private String cleanPath() { + if (path == null) { + return ""; + } String path = this.path.trim(); if (StringUtils.hasLength(path)) { if (!path.startsWith("/")) { diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsRegistrar.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsRegistrar.java index fa1495bef..4fe4b8d8a 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsRegistrar.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsRegistrar.java @@ -302,7 +302,11 @@ private String resolve(ConfigurableBeanFactory beanFactory, String value) { if (resolver == null) { return resolved; } - return String.valueOf(resolver.evaluate(resolved, new BeanExpressionContext(beanFactory, null))); + Object evaluateValue = resolver.evaluate(resolved, new BeanExpressionContext(beanFactory, null)); + if (evaluateValue != null) { + return String.valueOf(evaluateValue); + } + return null; } return value; } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientsRegistrarTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientsRegistrarTests.java index 483c5bb70..fc3109851 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientsRegistrarTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientsRegistrarTests.java @@ -18,6 +18,7 @@ import java.util.Collections; +import feign.Target; import org.junit.Test; import org.springframework.beans.factory.support.DefaultListableBeanFactory; @@ -25,15 +26,19 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.mock.env.MockEnvironment; +import org.springframework.test.util.ReflectionTestUtils; import org.springframework.web.bind.annotation.GetMapping; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; /** * @author Spencer Gibb * @author Gang Li * @author Michal Domagala + * @author Szymon Linowski + * @author Olga Maciaszek-Sharma */ public class FeignClientsRegistrarTests { @@ -101,6 +106,30 @@ public void shouldPassSubLevelFeignClient() { .doesNotThrowAnyException(); } + @Test + public void shouldResolveNullUrl() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(NullUrlFeignClientTestConfig.class); + context.refresh(); + + Object feignClientBean = context.getBean(NullUrlFeignClient.class); + + Object invocationHandlerLambda = ReflectionTestUtils.getField(feignClientBean, "h"); + Target.HardCodedTarget target = (Target.HardCodedTarget) ReflectionTestUtils + .getField(invocationHandlerLambda, "arg$4"); + assertThat(target.name()).isEqualTo("nullUrlFeignClient"); + assertThat(target.url()).isEqualTo("http://nullUrlFeignClient"); + } + + @Test + public void shouldResolveAndValidateNullName() { + assertThatIllegalStateException().isThrownBy(() -> { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.register(NullExpressionNameFeignClientTestConfig.class); + context.refresh(); + }); + } + @FeignClient(name = "fallbackTestClient", url = "http://localhost:8080/", fallback = FallbackClient.class) protected interface FallbackClient { @@ -118,6 +147,16 @@ protected interface FallbackFactoryClient { } + @FeignClient(name = "nullUrlFeignClient", url = "${test.url:#{null}}", path = "${test.path:#{null}}") + protected interface NullUrlFeignClient { + + } + + @FeignClient(name = "${test.name:#{null}}") + protected interface NullExpressionNameFeignClient { + + } + @Configuration(proxyBeanMethods = false) @EnableAutoConfiguration @EnableFeignClients(clients = { FeignClientsRegistrarTests.FallbackClient.class }) @@ -138,4 +177,18 @@ protected static class TopLevelSubLevelTestConfig { } + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration + @EnableFeignClients(clients = NullUrlFeignClient.class) + protected static class NullUrlFeignClientTestConfig { + + } + + @Configuration(proxyBeanMethods = false) + @EnableAutoConfiguration + @EnableFeignClients(clients = NullExpressionNameFeignClient.class) + protected static class NullExpressionNameFeignClientTestConfig { + + } + } From 10fefea29b9bf30ee091120309a59dd0ca427d72 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Tue, 28 Jun 2022 15:46:11 +0200 Subject: [PATCH 30/62] Remove incompatible test. --- .../openfeign/FeignClientsRegistrarTests.java | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientsRegistrarTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientsRegistrarTests.java index fc3109851..a1a7900f0 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientsRegistrarTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientsRegistrarTests.java @@ -18,7 +18,6 @@ import java.util.Collections; -import feign.Target; import org.junit.Test; import org.springframework.beans.factory.support.DefaultListableBeanFactory; @@ -26,7 +25,6 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.mock.env.MockEnvironment; -import org.springframework.test.util.ReflectionTestUtils; import org.springframework.web.bind.annotation.GetMapping; import static org.assertj.core.api.Assertions.assertThat; @@ -106,21 +104,6 @@ public void shouldPassSubLevelFeignClient() { .doesNotThrowAnyException(); } - @Test - public void shouldResolveNullUrl() { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - context.register(NullUrlFeignClientTestConfig.class); - context.refresh(); - - Object feignClientBean = context.getBean(NullUrlFeignClient.class); - - Object invocationHandlerLambda = ReflectionTestUtils.getField(feignClientBean, "h"); - Target.HardCodedTarget target = (Target.HardCodedTarget) ReflectionTestUtils - .getField(invocationHandlerLambda, "arg$4"); - assertThat(target.name()).isEqualTo("nullUrlFeignClient"); - assertThat(target.url()).isEqualTo("http://nullUrlFeignClient"); - } - @Test public void shouldResolveAndValidateNullName() { assertThatIllegalStateException().isThrownBy(() -> { From ae63aae6ebf562af45c3c2fa243e316658bcd5fc Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Wed, 29 Jun 2022 17:40:23 +0200 Subject: [PATCH 31/62] Add deprecations for OAuth2. --- .../cloud/openfeign/FeignAutoConfiguration.java | 1 + .../openfeign/security/OAuth2FeignRequestInterceptor.java | 1 + spring-cloud-openfeign-dependencies/pom.xml | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java index e8cfcd9ff..ef3957181 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java @@ -312,6 +312,7 @@ public Client feignClient(org.apache.hc.client5.http.impl.classic.CloseableHttpC @Configuration(proxyBeanMethods = false) @ConditionalOnClass(OAuth2ClientContext.class) @ConditionalOnProperty("feign.oauth2.enabled") + @Deprecated // spring-security-oauth2 reached EOL protected static class Oauth2FeignConfiguration { @Bean diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptor.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptor.java index 6b5302d5c..2d26df89e 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptor.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptor.java @@ -43,6 +43,7 @@ * @author Tim Ysewyn * @since 3.0.0 */ +@Deprecated // spring-security-oauth2 reached EOL public class OAuth2FeignRequestInterceptor implements RequestInterceptor { /** diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index 1be252fec..cb4644b92 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -17,7 +17,8 @@ 10.12 3.8.0 - 2.1.2.RELEASE + + 2.5.2 From e321e72e3d002753dea6758c78cec3f505ba1826 Mon Sep 17 00:00:00 2001 From: buildmaster Date: Wed, 29 Jun 2022 22:14:15 +0000 Subject: [PATCH 32/62] Update SNAPSHOT to 3.0.7 --- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 4 ++-- spring-cloud-starter-openfeign/pom.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 11a834bc3..0aa805c8c 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.0.7-SNAPSHOT + 3.0.7 spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index 873196860..ca5d333a4 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.0.7-SNAPSHOT + 3.0.7 pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.0.6-SNAPSHOT + 3.0.5 @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.0.6-SNAPSHOT + 3.0.6 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index c59bb4d51..8d8950779 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.0.7-SNAPSHOT + 3.0.7 .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index cb4644b92..40d473752 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 3.0.6-SNAPSHOT + 3.0.5 spring-cloud-openfeign-dependencies - 3.0.7-SNAPSHOT + 3.0.7 pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index 968e863af..75c24552a 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.0.7-SNAPSHOT + 3.0.7 .. spring-cloud-starter-openfeign From fc459044b5973ac493556e95b34e0fbab3088f1a Mon Sep 17 00:00:00 2001 From: buildmaster Date: Wed, 29 Jun 2022 22:15:46 +0000 Subject: [PATCH 33/62] Going back to snapshots --- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 4 ++-- spring-cloud-starter-openfeign/pom.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 0aa805c8c..11a834bc3 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.0.7 + 3.0.7-SNAPSHOT spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index ca5d333a4..873196860 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.0.7 + 3.0.7-SNAPSHOT pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.0.5 + 3.0.6-SNAPSHOT @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.0.6 + 3.0.6-SNAPSHOT 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 8d8950779..c59bb4d51 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.0.7 + 3.0.7-SNAPSHOT .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index 40d473752..cb4644b92 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 3.0.5 + 3.0.6-SNAPSHOT spring-cloud-openfeign-dependencies - 3.0.7 + 3.0.7-SNAPSHOT pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index 75c24552a..968e863af 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.0.7 + 3.0.7-SNAPSHOT .. spring-cloud-starter-openfeign From 7e013553175e0f489fd5f35db383f313ea0ad1c5 Mon Sep 17 00:00:00 2001 From: buildmaster Date: Wed, 29 Jun 2022 22:15:46 +0000 Subject: [PATCH 34/62] Bumping versions to 3.0.8-SNAPSHOT after release --- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 2 +- spring-cloud-starter-openfeign/pom.xml | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 11a834bc3..b5a25b37a 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.0.7-SNAPSHOT + 3.0.8-SNAPSHOT spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index 873196860..8df93e243 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.0.7-SNAPSHOT + 3.0.8-SNAPSHOT pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.0.6-SNAPSHOT + 3.0.5 @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.0.6-SNAPSHOT + 3.0.7-SNAPSHOT 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index c59bb4d51..b05d66324 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.0.7-SNAPSHOT + 3.0.8-SNAPSHOT .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index cb4644b92..5af8e20c2 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -10,7 +10,7 @@ spring-cloud-openfeign-dependencies - 3.0.7-SNAPSHOT + 3.0.8-SNAPSHOT pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index 968e863af..8e99ec865 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.0.7-SNAPSHOT + 3.0.8-SNAPSHOT .. spring-cloud-starter-openfeign From 1e70aec75cdeec3e7432e6e457d62742b889a03f Mon Sep 17 00:00:00 2001 From: buildmaster Date: Thu, 30 Jun 2022 10:59:53 +0000 Subject: [PATCH 35/62] Bumping versions --- docs/src/main/asciidoc/_configprops.adoc | 56 ++++++++++++------------ 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/docs/src/main/asciidoc/_configprops.adoc b/docs/src/main/asciidoc/_configprops.adoc index 14058c518..802070913 100644 --- a/docs/src/main/asciidoc/_configprops.adoc +++ b/docs/src/main/asciidoc/_configprops.adoc @@ -1,38 +1,38 @@ |=== |Name | Default | Description -|feign.autoconfiguration.jackson.enabled | `false` | If true, PageJacksonModule and SortJacksonModule bean will be provided for Jackson page decoding. -|feign.circuitbreaker.alphanumeric-ids.enabled | `false` | If true, Circuit Breaker ids will only contain alphanumeric characters to allow for configuration via configuration properties. -|feign.circuitbreaker.enabled | `false` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker. -|feign.circuitbreaker.group.enabled | `false` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker with with group. +|feign.autoconfiguration.jackson.enabled | `+++false+++` | If true, PageJacksonModule and SortJacksonModule bean will be provided for Jackson page decoding. +|feign.circuitbreaker.alphanumeric-ids.enabled | `+++false+++` | If true, Circuit Breaker ids will only contain alphanumeric characters to allow for configuration via configuration properties. +|feign.circuitbreaker.enabled | `+++false+++` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker. +|feign.circuitbreaker.group.enabled | `+++false+++` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker with with group. |feign.client.config | | -|feign.client.decode-slash | `true` | Feign clients do not encode slash `/` characters by default. To change this behavior, set the `decodeSlash` to `false`. -|feign.client.default-config | `default` | -|feign.client.default-to-properties | `true` | -|feign.client.refresh-enabled | `false` | Enables options value refresh capability for Feign. -|feign.compression.request.enabled | `false` | Enables the request sent by Feign to be compressed. -|feign.compression.request.mime-types | `[text/xml, application/xml, application/json]` | The list of supported mime types. -|feign.compression.request.min-request-size | `2048` | The minimum threshold content size. -|feign.compression.response.enabled | `false` | Enables the response from Feign to be compressed. -|feign.encoder.charset-from-content-type | `false` | Indicates whether the charset should be derived from the {@code Content-Type} header. -|feign.httpclient.connection-timeout | `2000` | -|feign.httpclient.connection-timer-repeat | `3000` | -|feign.httpclient.disable-ssl-validation | `false` | -|feign.httpclient.enabled | `true` | Enables the use of the Apache HTTP Client by Feign. -|feign.httpclient.follow-redirects | `true` | -|feign.httpclient.hc5.enabled | `false` | Enables the use of the Apache HTTP Client 5 by Feign. +|feign.client.decode-slash | `+++true+++` | Feign clients do not encode slash `/` characters by default. To change this behavior, set the `decodeSlash` to `false`. +|feign.client.default-config | `+++default+++` | +|feign.client.default-to-properties | `+++true+++` | +|feign.client.refresh-enabled | `+++false+++` | Enables options value refresh capability for Feign. +|feign.compression.request.enabled | `+++false+++` | Enables the request sent by Feign to be compressed. +|feign.compression.request.mime-types | `+++[text/xml, application/xml, application/json]+++` | The list of supported mime types. +|feign.compression.request.min-request-size | `+++2048+++` | The minimum threshold content size. +|feign.compression.response.enabled | `+++false+++` | Enables the response from Feign to be compressed. +|feign.encoder.charset-from-content-type | `+++false+++` | Indicates whether the charset should be derived from the {@code Content-Type} header. +|feign.httpclient.connection-timeout | `+++2000+++` | +|feign.httpclient.connection-timer-repeat | `+++3000+++` | +|feign.httpclient.disable-ssl-validation | `+++false+++` | +|feign.httpclient.enabled | `+++true+++` | Enables the use of the Apache HTTP Client by Feign. +|feign.httpclient.follow-redirects | `+++true+++` | +|feign.httpclient.hc5.enabled | `+++false+++` | Enables the use of the Apache HTTP Client 5 by Feign. |feign.httpclient.hc5.pool-concurrency-policy | | Pool concurrency policies. |feign.httpclient.hc5.pool-reuse-policy | | Pool connection re-use policies. -|feign.httpclient.hc5.socket-timeout | `5` | Default value for socket timeout. +|feign.httpclient.hc5.socket-timeout | `+++5+++` | Default value for socket timeout. |feign.httpclient.hc5.socket-timeout-unit | | Default value for socket timeout unit. -|feign.httpclient.max-connections | `200` | -|feign.httpclient.max-connections-per-route | `50` | -|feign.httpclient.ok-http.read-timeout | `60s` | {@link OkHttpClient} read timeout; defaults to 60 seconds. -|feign.httpclient.time-to-live | `900` | +|feign.httpclient.max-connections | `+++200+++` | +|feign.httpclient.max-connections-per-route | `+++50+++` | +|feign.httpclient.ok-http.read-timeout | `+++60s+++` | {@link OkHttpClient} read timeout; defaults to 60 seconds. +|feign.httpclient.time-to-live | `+++900+++` | |feign.httpclient.time-to-live-unit | | -|feign.metrics.enabled | `true` | Enables metrics capability for Feign. -|feign.oauth2.enabled | `false` | Enables feign interceptor for managing oauth2 access token. -|feign.oauth2.load-balanced | `false` | Enables load balancing for oauth2 access token provider. -|feign.okhttp.enabled | `false` | Enables the use of the OK HTTP Client by Feign. +|feign.metrics.enabled | `+++true+++` | Enables metrics capability for Feign. +|feign.oauth2.enabled | `+++false+++` | Enables feign interceptor for managing oauth2 access token. +|feign.oauth2.load-balanced | `+++false+++` | Enables load balancing for oauth2 access token provider. +|feign.okhttp.enabled | `+++false+++` | Enables the use of the OK HTTP Client by Feign. |=== \ No newline at end of file From a29f28506f98b5b87780e6c72f36e686e4143a35 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Fri, 1 Jul 2022 16:38:55 +0200 Subject: [PATCH 36/62] Remove vulnerable transitive dependency. --- spring-cloud-openfeign-core/pom.xml | 17 +++++++++++++++++ src/checkstyle/checkstyle-suppressions.xml | 1 + 2 files changed, 18 insertions(+) diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 1ce746f31..3353bf548 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -99,6 +99,13 @@ io.github.openfeign.form feign-form-spring + + + + commons-io + commons-io + + io.github.openfeign @@ -148,6 +155,10 @@ javax.activation javax.activation-api + + com.sun.activation + jakarta.activation + @@ -208,6 +219,12 @@ spring-cloud-loadbalancer true + + commons-io + commons-io + 2.11.0 + test + diff --git a/src/checkstyle/checkstyle-suppressions.xml b/src/checkstyle/checkstyle-suppressions.xml index 7fe5b1cac..81b1ab023 100644 --- a/src/checkstyle/checkstyle-suppressions.xml +++ b/src/checkstyle/checkstyle-suppressions.xml @@ -12,6 +12,7 @@ + From 8989036e93025b45a063a81b85548fe824593b4e Mon Sep 17 00:00:00 2001 From: spencergibb Date: Mon, 18 Jul 2022 12:59:55 -0400 Subject: [PATCH 37/62] Removes libs-*-local repos --- pom.xml | 10 +++++----- spring-cloud-openfeign-dependencies/pom.xml | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index 4e5102c5b..2fa323131 100644 --- a/pom.xml +++ b/pom.xml @@ -142,7 +142,7 @@ spring-snapshots Spring Snapshots - https://repo.spring.io/libs-snapshot-local + https://repo.spring.io/snapshot true @@ -153,7 +153,7 @@ spring-milestones Spring Milestones - https://repo.spring.io/libs-milestone-local + https://repo.spring.io/milestone false @@ -171,7 +171,7 @@ spring-snapshots Spring Snapshots - https://repo.spring.io/libs-snapshot-local + https://repo.spring.io/snapshot true @@ -182,7 +182,7 @@ spring-milestones Spring Milestones - https://repo.spring.io/libs-milestone-local + https://repo.spring.io/milestone false @@ -190,7 +190,7 @@ spring-releases Spring Releases - https://repo.spring.io/libs-release-local + https://repo.spring.io/release false diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index 02ca862db..f1089772b 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -58,7 +58,7 @@ spring-snapshots Spring Snapshots - https://repo.spring.io/libs-snapshot-local + https://repo.spring.io/snapshot true @@ -69,7 +69,7 @@ spring-milestones Spring Milestones - https://repo.spring.io/libs-milestone-local + https://repo.spring.io/milestone false @@ -87,7 +87,7 @@ spring-snapshots Spring Snapshots - https://repo.spring.io/libs-snapshot-local + https://repo.spring.io/snapshot true @@ -98,7 +98,7 @@ spring-milestones Spring Milestones - https://repo.spring.io/libs-milestone-local + https://repo.spring.io/milestone false From 8fdb488697652ca2aed1a5498083c0ce99fe3973 Mon Sep 17 00:00:00 2001 From: Nikita Konev <3160384+nkonev@users.noreply.github.com> Date: Fri, 14 Jan 2022 19:22:04 +0300 Subject: [PATCH 38/62] fix cascading deserialization Spring Data's Page by setting @JsonIgnoreProperties(ignoreUnknown = true), revert ignoring getTotalPages(), getNumberOfElements(), isFirst(), isLast() (#653) --- .../openfeign/support/PageJacksonModule.java | 10 +-- .../support/PageJacksonModuleTests.java | 61 +++++++++++++++++-- 2 files changed, 63 insertions(+), 8 deletions(-) diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/PageJacksonModule.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/PageJacksonModule.java index d3c60849f..23068bf65 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/PageJacksonModule.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/PageJacksonModule.java @@ -22,6 +22,7 @@ import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.core.Version; import com.fasterxml.jackson.databind.Module; @@ -59,6 +60,7 @@ public void setupModule(SetupContext context) { } @JsonDeserialize(as = SimplePageImpl.class) + @JsonIgnoreProperties(ignoreUnknown = true) private interface PageMixIn { } @@ -86,7 +88,7 @@ static class SimplePageImpl implements Page { } } - @JsonIgnore + @JsonProperty @Override public int getTotalPages() { return delegate.getTotalPages(); @@ -110,7 +112,7 @@ public int getSize() { return delegate.getSize(); } - @JsonIgnore + @JsonProperty @Override public int getNumberOfElements() { return delegate.getNumberOfElements(); @@ -134,13 +136,13 @@ public Sort getSort() { return delegate.getSort(); } - @JsonIgnore + @JsonProperty @Override public boolean isFirst() { return delegate.isFirst(); } - @JsonIgnore + @JsonProperty @Override public boolean isLast() { return delegate.isLast(); diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageJacksonModuleTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageJacksonModuleTests.java index c02e7dd62..f83db5a32 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageJacksonModuleTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageJacksonModuleTests.java @@ -82,10 +82,10 @@ public void serializeAndDeserializeEmpty() throws JsonProcessingException { @Test public void serializeAndDeserializeFilledMultiple() throws JsonProcessingException { // Given - ArrayList strings0 = new ArrayList<>(); - strings0.add("first element"); - strings0.add("second element"); - PageImpl objects = new PageImpl<>(strings0, PageRequest.of(6, 2), 100); + ArrayList pageElements = new ArrayList<>(); + pageElements.add("first element"); + pageElements.add("second element"); + PageImpl objects = new PageImpl<>(pageElements, PageRequest.of(6, 2), 100); assertThat(objects.getContent()).hasSize(2); assertThat(objects.getPageable().getPageSize()).isEqualTo(2); @@ -102,4 +102,57 @@ public void serializeAndDeserializeFilledMultiple() throws JsonProcessingExcepti assertThat(result.getPageable().getPageNumber()).isEqualTo(6); } + @Test + public void serializeAndDeserializeEmptyCascade() throws JsonProcessingException { + // Given + PageImpl objects = new PageImpl<>(new ArrayList<>()); + String pageJson = objectMapper.writeValueAsString(objects); + // When + Page result = objectMapper.readValue(pageJson, Page.class); + // Then + assertThat(result).isNotNull(); + assertThat(result.getTotalElements()).isEqualTo(0); + assertThat(result.getContent()).hasSize(0); + + String cascadedPageJson = objectMapper.writeValueAsString(result); + Page cascadedResult = objectMapper.readValue(cascadedPageJson, Page.class); + assertThat(cascadedResult).isNotNull(); + assertThat(cascadedResult.getTotalElements()).isEqualTo(0); + assertThat(cascadedResult.getContent()).hasSize(0); + } + + @Test + public void serializeAndDeserializeFilledMultipleCascade() throws JsonProcessingException { + // Given + ArrayList pageElements = new ArrayList<>(); + pageElements.add("first element in cascaded serialization"); + pageElements.add("second element in cascaded serialization"); + PageImpl objects = new PageImpl<>(pageElements, PageRequest.of(6, 2), 100); + assertThat(objects.getContent()).hasSize(2); + assertThat(objects.getPageable().getPageSize()).isEqualTo(2); + + String pageJson = objectMapper.writeValueAsString(objects); + // When + Page result = objectMapper.readValue(pageJson, Page.class); + // Then + assertThat(result).isNotNull(); + assertThat(result.getTotalElements()).isEqualTo(100); + assertThat(result.getContent()).hasSize(2); + assertThat(result.getContent().get(0)).isEqualTo("first element in cascaded serialization"); + assertThat(result.getContent().get(1)).isEqualTo("second element in cascaded serialization"); + assertThat(result.getPageable().getPageSize()).isEqualTo(2); + assertThat(result.getPageable().getPageNumber()).isEqualTo(6); + + String cascadedPageJson = objectMapper.writeValueAsString(result); + Page cascadedResult = objectMapper.readValue(cascadedPageJson, Page.class); + // Then + assertThat(cascadedResult).isNotNull(); + assertThat(cascadedResult.getTotalElements()).isEqualTo(100); + assertThat(cascadedResult.getContent()).hasSize(2); + assertThat(cascadedResult.getContent().get(0)).isEqualTo("first element in cascaded serialization"); + assertThat(cascadedResult.getContent().get(1)).isEqualTo("second element in cascaded serialization"); + assertThat(cascadedResult.getPageable().getPageSize()).isEqualTo(2); + assertThat(cascadedResult.getPageable().getPageNumber()).isEqualTo(6); + } + } From a26095bb6c66cddc67b74e9bc680ea89c357f9fc Mon Sep 17 00:00:00 2001 From: Kwangyong Kim Date: Fri, 29 Apr 2022 16:22:41 +0900 Subject: [PATCH 39/62] Fix a typo in sample code I found a typo in the latest release. It should be `@FeignClient(url = "http://localhost:8080")` --- docs/src/main/asciidoc/spring-cloud-openfeign.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc index 371190563..a3ddeba1e 100644 --- a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc +++ b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc @@ -385,7 +385,7 @@ For example, if you had this Feign client [source,java,indent=0] ---- -@FeignClienturl = "http://localhost:8080") +@FeignClient(url = "http://localhost:8080") public interface DemoClient { @GetMapping("demo") From 9529e43c018ce03e09a367441707955fe4755c3d Mon Sep 17 00:00:00 2001 From: fml2 <534392+fml2@users.noreply.github.com> Date: Wed, 20 Jul 2022 10:51:36 +0200 Subject: [PATCH 40/62] Update spring-cloud-openfeign.adoc --- docs/src/main/asciidoc/spring-cloud-openfeign.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc index a3ddeba1e..26cbe5202 100644 --- a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc +++ b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc @@ -202,7 +202,7 @@ feign: Default configurations can be specified in the `@EnableFeignClients` attribute `defaultConfiguration` in a similar manner as described above. The difference is that this configuration will apply to _all_ feign clients. -If you prefer using configuration properties to configured all `@FeignClient`, you can create configuration properties with `default` feign name. +If you prefer using configuration properties to configure all `@FeignClient`, you can create configuration properties with `default` feign name. You can use `feign.client.config.feignName.defaultQueryParameters` and `feign.client.config.feignName.defaultRequestHeaders` to specify query parameters and headers that will be sent with every request of the client named `feignName`. From b4c9567de51aa4047dd70a6a0280332fac6de962 Mon Sep 17 00:00:00 2001 From: fml2 <534392+fml2@users.noreply.github.com> Date: Wed, 20 Jul 2022 10:51:36 +0200 Subject: [PATCH 41/62] Update spring-cloud-openfeign.adoc --- docs/src/main/asciidoc/spring-cloud-openfeign.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc index 2d61f1af7..b4c5dbae4 100644 --- a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc +++ b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc @@ -197,7 +197,7 @@ feign: Default configurations can be specified in the `@EnableFeignClients` attribute `defaultConfiguration` in a similar manner as described above. The difference is that this configuration will apply to _all_ feign clients. -If you prefer using configuration properties to configured all `@FeignClient`, you can create configuration properties with `default` feign name. +If you prefer using configuration properties to configure all `@FeignClient`, you can create configuration properties with `default` feign name. You can use `feign.client.config.feignName.defaultQueryParameters` and `feign.client.config.feignName.defaultRequestHeaders` to specify query parameters and headers that will be sent with every request of the client named `feignName`. From 73cde28fcf6757ca85931f0082b5a08cbaa938fb Mon Sep 17 00:00:00 2001 From: buildmaster Date: Fri, 2 Sep 2022 23:44:34 +0000 Subject: [PATCH 42/62] Update SNAPSHOT to 3.1.4 --- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 4 ++-- spring-cloud-starter-openfeign/pom.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index c3e75e94b..4def74046 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.4-SNAPSHOT + 3.1.4 spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index 2fa323131..433be07c8 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.1.4-SNAPSHOT + 3.1.4 pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.1.4-SNAPSHOT + 3.1.4 @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.1.4-SNAPSHOT + 3.1.4 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 3353bf548..1a362f731 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.4-SNAPSHOT + 3.1.4 .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index f1089772b..245485d75 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 3.1.4-SNAPSHOT + 3.1.4 spring-cloud-openfeign-dependencies - 3.1.4-SNAPSHOT + 3.1.4 pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index 72253452c..94c7c1c93 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.4-SNAPSHOT + 3.1.4 .. spring-cloud-starter-openfeign From 0213d4be21f5b0e62e8d1c78c85e604105db477b Mon Sep 17 00:00:00 2001 From: buildmaster Date: Fri, 2 Sep 2022 23:46:05 +0000 Subject: [PATCH 43/62] Going back to snapshots --- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 4 ++-- spring-cloud-starter-openfeign/pom.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 4def74046..c3e75e94b 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.4 + 3.1.4-SNAPSHOT spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index 433be07c8..2fa323131 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.1.4 + 3.1.4-SNAPSHOT pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.1.4 + 3.1.4-SNAPSHOT @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.1.4 + 3.1.4-SNAPSHOT 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 1a362f731..3353bf548 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.4 + 3.1.4-SNAPSHOT .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index 245485d75..f1089772b 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 3.1.4 + 3.1.4-SNAPSHOT spring-cloud-openfeign-dependencies - 3.1.4 + 3.1.4-SNAPSHOT pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index 94c7c1c93..72253452c 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.4 + 3.1.4-SNAPSHOT .. spring-cloud-starter-openfeign From 375241803d417b970fffe237c18841b76f0084dd Mon Sep 17 00:00:00 2001 From: buildmaster Date: Fri, 2 Sep 2022 23:46:06 +0000 Subject: [PATCH 44/62] Bumping versions to 3.1.5-SNAPSHOT after release --- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 4 ++-- spring-cloud-starter-openfeign/pom.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index c3e75e94b..79cabddb7 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.4-SNAPSHOT + 3.1.5-SNAPSHOT spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index 2fa323131..ed1a3b8cb 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.1.4-SNAPSHOT + 3.1.5-SNAPSHOT pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.1.4-SNAPSHOT + 3.1.5-SNAPSHOT @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.1.4-SNAPSHOT + 3.1.5-SNAPSHOT 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 3353bf548..863e314c6 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.4-SNAPSHOT + 3.1.5-SNAPSHOT .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index f1089772b..16605ed70 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 3.1.4-SNAPSHOT + 3.1.5-SNAPSHOT spring-cloud-openfeign-dependencies - 3.1.4-SNAPSHOT + 3.1.5-SNAPSHOT pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index 72253452c..2a7bf0dbc 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.4-SNAPSHOT + 3.1.5-SNAPSHOT .. spring-cloud-starter-openfeign From bdcbaa7f3b1e65f69137c7ae246a30d96d1930d8 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Mon, 5 Sep 2022 16:37:12 +0200 Subject: [PATCH 45/62] Allow overriding binary content types. Fixes gh-734. --- .../cloud/openfeign/support/SpringEncoder.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java index ef2624ae8..5dea96f35 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java @@ -247,12 +247,12 @@ private boolean isFormUrlEncoded(MediaType requestContentType) { return Objects.equals(APPLICATION_FORM_URLENCODED, requestContentType); } - private boolean binaryContentType(FeignOutputMessage outputMessage) { + protected boolean binaryContentType(FeignOutputMessage outputMessage) { MediaType contentType = outputMessage.getHeaders().getContentType(); return contentType == null || Stream - .of(MediaType.APPLICATION_CBOR, MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_PDF, - MediaType.IMAGE_GIF, MediaType.IMAGE_JPEG, MediaType.IMAGE_PNG) - .anyMatch(mediaType -> mediaType.includes(contentType)); + .of(MediaType.APPLICATION_CBOR, MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_PDF, + MediaType.IMAGE_GIF, MediaType.IMAGE_JPEG, MediaType.IMAGE_PNG) + .anyMatch(mediaType -> mediaType.includes(contentType)); } private final class FeignOutputMessage implements HttpOutputMessage { From a95f2a23d26978bea4d8e4077cd832834107d5c7 Mon Sep 17 00:00:00 2001 From: buildmaster Date: Tue, 6 Sep 2022 10:57:04 +0000 Subject: [PATCH 46/62] Bumping versions --- .../cloud/openfeign/support/SpringEncoder.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java index 5dea96f35..f68933d59 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java @@ -250,9 +250,9 @@ private boolean isFormUrlEncoded(MediaType requestContentType) { protected boolean binaryContentType(FeignOutputMessage outputMessage) { MediaType contentType = outputMessage.getHeaders().getContentType(); return contentType == null || Stream - .of(MediaType.APPLICATION_CBOR, MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_PDF, - MediaType.IMAGE_GIF, MediaType.IMAGE_JPEG, MediaType.IMAGE_PNG) - .anyMatch(mediaType -> mediaType.includes(contentType)); + .of(MediaType.APPLICATION_CBOR, MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_PDF, + MediaType.IMAGE_GIF, MediaType.IMAGE_JPEG, MediaType.IMAGE_PNG) + .anyMatch(mediaType -> mediaType.includes(contentType)); } private final class FeignOutputMessage implements HttpOutputMessage { From 9a8dae2cba8e3efcd42fed087c72ffb4483927f9 Mon Sep 17 00:00:00 2001 From: buildmaster Date: Tue, 6 Sep 2022 20:20:22 +0000 Subject: [PATCH 47/62] Update SNAPSHOT to 3.1.4 --- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 4 ++-- spring-cloud-starter-openfeign/pom.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 79cabddb7..4def74046 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.5-SNAPSHOT + 3.1.4 spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index ed1a3b8cb..433be07c8 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.1.5-SNAPSHOT + 3.1.4 pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.1.5-SNAPSHOT + 3.1.4 @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.1.5-SNAPSHOT + 3.1.4 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 863e314c6..1a362f731 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.5-SNAPSHOT + 3.1.4 .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index 16605ed70..245485d75 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 3.1.5-SNAPSHOT + 3.1.4 spring-cloud-openfeign-dependencies - 3.1.5-SNAPSHOT + 3.1.4 pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index 2a7bf0dbc..94c7c1c93 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.5-SNAPSHOT + 3.1.4 .. spring-cloud-starter-openfeign From 11e9c5dab0b24cd325de285b7f07793dfb4e878b Mon Sep 17 00:00:00 2001 From: buildmaster Date: Tue, 6 Sep 2022 20:22:00 +0000 Subject: [PATCH 48/62] Going back to snapshots --- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 4 ++-- spring-cloud-starter-openfeign/pom.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 4def74046..79cabddb7 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.4 + 3.1.5-SNAPSHOT spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index 433be07c8..ed1a3b8cb 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.1.4 + 3.1.5-SNAPSHOT pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.1.4 + 3.1.5-SNAPSHOT @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.1.4 + 3.1.5-SNAPSHOT 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 1a362f731..863e314c6 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.4 + 3.1.5-SNAPSHOT .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index 245485d75..16605ed70 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 3.1.4 + 3.1.5-SNAPSHOT spring-cloud-openfeign-dependencies - 3.1.4 + 3.1.5-SNAPSHOT pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index 94c7c1c93..2a7bf0dbc 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.4 + 3.1.5-SNAPSHOT .. spring-cloud-starter-openfeign From d501cbbd07fed010f933f9e9c111bc3c140cc476 Mon Sep 17 00:00:00 2001 From: dzcr <1137729123@qq.com> Date: Fri, 23 Sep 2022 17:28:16 +0800 Subject: [PATCH 49/62] step 1 --- spring-cloud-openfeign-core/pom.xml | 4 + .../openfeign/FeignAutoConfiguration.java | 21 ++- .../OAuth2AccessTokenInterceptor.java | 177 ++++++++++++++++++ .../FeignAutoConfigurationTests.java | 67 +++++-- .../OAuth2AccessTokenInterceptorTests.java | 170 +++++++++++++++++ spring-cloud-openfeign-dependencies/pom.xml | 6 + 6 files changed, 429 insertions(+), 16 deletions(-) create mode 100644 spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptor.java create mode 100644 spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptorTests.java diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 863e314c6..b441393d0 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -225,6 +225,10 @@ 2.11.0 test + + org.springframework.security + spring-security-oauth2-client + diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java index 857f7f75c..ec97c25d8 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java @@ -51,6 +51,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cache.interceptor.CacheInterceptor; import org.springframework.cloud.client.actuator.HasFeatures; @@ -62,6 +63,7 @@ import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory; import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory; import org.springframework.cloud.commons.httpclient.OkHttpClientFactory; +import org.springframework.cloud.openfeign.security.OAuth2AccessTokenInterceptor; import org.springframework.cloud.openfeign.security.OAuth2FeignRequestInterceptor; import org.springframework.cloud.openfeign.security.OAuth2FeignRequestInterceptorConfigurer; import org.springframework.cloud.openfeign.support.FeignEncoderProperties; @@ -74,7 +76,9 @@ import org.springframework.context.annotation.Import; import org.springframework.data.domain.Page; import org.springframework.data.domain.Sort; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.OAuth2ClientContext; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; import static org.springframework.cloud.openfeign.security.OAuth2FeignRequestInterceptorBuilder.buildWithConfigurers; @@ -91,6 +95,7 @@ * @author Kwangyong Kim * @author Sam Kruglov * @author Wojciech Mąka + * @author Dangzhicairang(小水牛) */ @Configuration(proxyBeanMethods = false) @ConditionalOnClass(Feign.class) @@ -369,12 +374,24 @@ public OAuth2FeignRequestInterceptorConfigurer loadBalancerInterceptorInjectingC @Bean @ConditionalOnMissingBean(OAuth2FeignRequestInterceptor.class) - @ConditionalOnBean({ OAuth2ClientContext.class, OAuth2ProtectedResourceDetails.class }) + @ConditionalOnBean({OAuth2ClientContext.class, OAuth2ProtectedResourceDetails.class}) public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext, - OAuth2ProtectedResourceDetails resource, List configurers) { + OAuth2ProtectedResourceDetails resource, List configurers) { return buildWithConfigurers(oAuth2ClientContext, resource, configurers); } + @Bean + @ConditionalOnBean({OAuth2AuthorizedClientService.class, ClientRegistrationRepository.class}) + public OAuth2AccessTokenInterceptor defaultOAuth2AccessTokenInterceptor( + @Value("${spring.cloud.openfeign.oauth2.specifiedClientIds:}") List specifiedClientIds, + OAuth2ClientProperties oAuth2ClientProperties, + OAuth2AuthorizedClientService oAuth2AuthorizedClientService, + ClientRegistrationRepository clientRegistrationRepository) { + + return new OAuth2AccessTokenInterceptor(specifiedClientIds, oAuth2ClientProperties, + oAuth2AuthorizedClientService, clientRegistrationRepository); + } + } } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptor.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptor.java new file mode 100644 index 000000000..cc0170068 --- /dev/null +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptor.java @@ -0,0 +1,177 @@ +/* + * Copyright 2015-2022 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.openfeign.security; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import feign.RequestInterceptor; +import feign.RequestTemplate; + +import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.client.AuthorizedClientServiceOAuth2AuthorizedClientManager; +import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.core.OAuth2AccessToken; +import org.springframework.util.StringUtils; + +/** + * RequestInterceptor for OAuth2 Feign Requests. By default, It uses the + * {@link AuthorizedClientServiceOAuth2AuthorizedClientManager } to get + * {@link OAuth2AuthorizedClient } that hold an {@link OAuth2AccessToken }. Use the + * Client(s) from properties if not specific the field + * {@link OAuth2AccessTokenInterceptor#specifiedClientIds} + * + * @author Dangzhicairang(小水牛) + * @since 4.0.0 + */ +public class OAuth2AccessTokenInterceptor implements RequestInterceptor { + + /** + * The name of the token. + */ + public static final String BEARER = "Bearer"; + + /** + * The name of the header. + */ + public static final String AUTHORIZATION = "Authorization"; + + private final String tokenType; + + private final String header; + + private final List specifiedClientIds; + + private final OAuth2ClientProperties oAuth2ClientProperties; + + private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService; + + private OAuth2AuthorizedClientManager authorizedClientManager; + + public void setAuthorizedClientManager(OAuth2AuthorizedClientManager authorizedClientManager) { + this.authorizedClientManager = authorizedClientManager; + } + + private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken("anonymous", + "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); + + public OAuth2AccessTokenInterceptor(OAuth2ClientProperties oAuth2ClientProperties, + OAuth2AuthorizedClientService oAuth2AuthorizedClientService, + ClientRegistrationRepository clientRegistrationRepository) { + this(new ArrayList<>(), oAuth2ClientProperties, oAuth2AuthorizedClientService, clientRegistrationRepository); + } + + public OAuth2AccessTokenInterceptor(List specifiedClientIds, OAuth2ClientProperties oAuth2ClientProperties, + OAuth2AuthorizedClientService oAuth2AuthorizedClientService, + ClientRegistrationRepository clientRegistrationRepository) { + this(BEARER, AUTHORIZATION, specifiedClientIds, oAuth2ClientProperties, oAuth2AuthorizedClientService, + clientRegistrationRepository); + } + + public OAuth2AccessTokenInterceptor(String tokenType, String header, List specifiedClientIds, + OAuth2ClientProperties oAuth2ClientProperties, OAuth2AuthorizedClientService oAuth2AuthorizedClientService, + ClientRegistrationRepository clientRegistrationRepository) { + this.tokenType = tokenType; + this.header = header; + this.specifiedClientIds = specifiedClientIds; + this.oAuth2ClientProperties = oAuth2ClientProperties; + this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService; + this.authorizedClientManager = new AuthorizedClientServiceOAuth2AuthorizedClientManager( + clientRegistrationRepository, this.oAuth2AuthorizedClientService); + } + + @Override + public void apply(RequestTemplate template) { + template.header(header); + template.header(header, extract(tokenType)); + } + + protected String extract(String tokenType) { + OAuth2AccessToken accessToken = getToken(); + return String.format("%s %s", tokenType, accessToken.getTokenValue()); + } + + public OAuth2AccessToken getToken() { + + // if specific, try to use them to get token. + for (String clientId : this.specifiedClientIds) { + OAuth2AccessToken token = this.getToken(clientId); + if (token != null) { + return token; + } + } + + // use clients from properties by default + for (String clientId : Optional.ofNullable(this.oAuth2ClientProperties) + .map(OAuth2ClientProperties::getRegistration).map(Map::keySet) + .orElse(new HashSet<>())) { + OAuth2AccessToken token = this.getToken(clientId); + if (token != null) { + return token; + } + } + + throw new IllegalStateException("No token acquired, which is illegal according to the contract."); + } + + protected OAuth2AccessToken getToken(String clientId) { + + if (!StringUtils.hasText(clientId)) { + return null; + } + + Authentication principal = SecurityContextHolder.getContext().getAuthentication(); + if (principal == null) { + principal = ANONYMOUS_AUTHENTICATION; + } + + // already exist + OAuth2AuthorizedClient oAuth2AuthorizedClient = oAuth2AuthorizedClientService.loadAuthorizedClient(clientId, + principal.getName()); + if (oAuth2AuthorizedClient != null) { + OAuth2AccessToken accessToken = oAuth2AuthorizedClient.getAccessToken(); + if (accessToken != null && this.noExpire(accessToken)) { + return accessToken; + } + } + + OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(clientId) + .principal(principal).build(); + OAuth2AuthorizedClient authorize = this.authorizedClientManager.authorize(authorizeRequest); + return Optional.ofNullable(authorize).map(OAuth2AuthorizedClient::getAccessToken) + .filter(this::noExpire) + .orElse(null); + } + + protected boolean noExpire(OAuth2AccessToken token) { + return Optional.ofNullable(token).map(OAuth2AccessToken::getExpiresAt) + .map(expire -> expire.isAfter(Instant.now())).orElse(false); + } + +} diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignAutoConfigurationTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignAutoConfigurationTests.java index b906ff552..4997f29e0 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignAutoConfigurationTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignAutoConfigurationTests.java @@ -17,6 +17,8 @@ package org.springframework.cloud.openfeign; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; import feign.Target; import org.assertj.core.api.Condition; @@ -29,12 +31,15 @@ import org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor; import org.springframework.cloud.openfeign.FeignAutoConfiguration.CircuitBreakerPresentFeignTargeterConfiguration.DefaultCircuitBreakerNameResolver; import org.springframework.cloud.openfeign.security.MockOAuth2ClientContext; +import org.springframework.cloud.openfeign.security.OAuth2AccessTokenInterceptor; import org.springframework.cloud.openfeign.security.OAuth2FeignRequestInterceptor; import org.springframework.cloud.openfeign.security.OAuth2FeignRequestInterceptorBuilder; import org.springframework.cloud.openfeign.security.OAuth2FeignRequestInterceptorConfigurer; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.support.BasicAuthenticationInterceptor; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.client.resource.BaseOAuth2ProtectedResourceDetails; import static org.assertj.core.api.Assertions.assertThat; @@ -46,6 +51,7 @@ * @author Andrii Bohutskyi * @author Kwangyong Kim * @author Wojciech Mąka + * @author Dangzhicairang(小水牛) */ class FeignAutoConfigurationTests { @@ -123,12 +129,30 @@ void shouldInstantiateFeignOAuth2FeignRequestInterceptorWithoutLoadBalancedInter @Test void shouldInstantiateFeignOAuth2FeignRequestInterceptorWithCustomAccessTokenProviderInterceptor() { - runner.withPropertyValues("feign.oauth2.enabled=true").withBean(MockOAuth2ClientContext.class, "token") - .withBean(BaseOAuth2ProtectedResourceDetails.class) - .withBean(CustomOAuth2FeignRequestInterceptorConfigurer.class).run(ctx -> { - assertOauth2FeignRequestInterceptorExists(ctx); - assertAccessTokenProviderInterceptorExists(ctx, BasicAuthenticationInterceptor.class); - }); + runner.withPropertyValues("feign.oauth2.enabled=true") + .withBean(MockOAuth2ClientContext.class, "token") + .withBean(BaseOAuth2ProtectedResourceDetails.class) + .withBean(CustomOAuth2FeignRequestInterceptorConfigurer.class).run(ctx -> { + assertOauth2FeignRequestInterceptorExists(ctx); + assertAccessTokenProviderInterceptorExists(ctx, BasicAuthenticationInterceptor.class); + }); + } + + @Test + void shouldInstantiateFeignOAuth2FeignRequestInterceptor() { + runner.withPropertyValues("spring.cloud.openfeign.oauth2.enabled=true", + "spring.cloud.openfeign.oauth2.specifiedClientIds=feign-client") + .withBean(OAuth2AuthorizedClientService.class, () -> mock(OAuth2AuthorizedClientService.class)) + .withBean(ClientRegistrationRepository.class, () -> mock(ClientRegistrationRepository.class)) + .run(ctx -> { + assertOauth2AccessTokenInterceptorExists(ctx); + assertThatOauth2AccessTokenInterceptorHasSpecifiedIdsPropertyWithValue(ctx, + new ArrayList() { + { + add("feign-client"); + } + }); + }); } private void assertOauth2FeignRequestInterceptorExists(ConfigurableApplicationContext ctx) { @@ -137,28 +161,43 @@ private void assertOauth2FeignRequestInterceptorExists(ConfigurableApplicationCo } private void assertAccessTokenProviderInterceptorExists(ConfigurableApplicationContext ctx, - Class clazz) { + Class clazz) { AssertableApplicationContext context = AssertableApplicationContext.get(() -> ctx); - assertThat(context).getBean(OAuth2FeignRequestInterceptor.class).extracting("accessTokenProvider") + assertThat(context).getBean(OAuth2FeignRequestInterceptor.class) + .extracting("accessTokenProvider") .extracting("interceptors").asList().first().isInstanceOf(clazz); } private void assertAccessTokenProviderInterceptorNotExists(ConfigurableApplicationContext ctx, - Class clazz) { + Class clazz) { + AssertableApplicationContext context = AssertableApplicationContext.get(() -> ctx); + assertThat(context).getBean(OAuth2FeignRequestInterceptor.class) + .extracting("accessTokenProvider") + .extracting("interceptors").asList() + .filteredOn(obj -> clazz.isAssignableFrom(obj.getClass())) + .isEmpty(); + } + + private void assertOauth2AccessTokenInterceptorExists(ConfigurableApplicationContext ctx) { AssertableApplicationContext context = AssertableApplicationContext.get(() -> ctx); - assertThat(context).getBean(OAuth2FeignRequestInterceptor.class).extracting("accessTokenProvider") - .extracting("interceptors").asList().filteredOn(obj -> clazz.isAssignableFrom(obj.getClass())) - .isEmpty(); + assertThat(context).hasSingleBean(OAuth2AccessTokenInterceptor.class); + } + + private void assertThatOauth2AccessTokenInterceptorHasSpecifiedIdsPropertyWithValue( + ConfigurableApplicationContext ctx, List expectedValue) { + final OAuth2AccessTokenInterceptor bean = ctx.getBean(OAuth2AccessTokenInterceptor.class); + assertThat(bean).hasFieldOrPropertyWithValue("specifiedClientIds", expectedValue); } private void assertOnlyOneTargeterPresent(ConfigurableApplicationContext ctx, Class beanClass) { - assertThat(ctx.getBeansOfType(Targeter.class)).hasSize(1).hasValueSatisfying(new Condition<>( + assertThat(ctx.getBeansOfType(Targeter.class)).hasSize(1) + .hasValueSatisfying(new Condition<>( beanClass::isInstance, String.format("Targeter should be an instance of %s", beanClass))); } private void assertThatFeignCircuitBreakerTargeterHasGroupEnabledPropertyWithValue( - ConfigurableApplicationContext ctx, boolean expectedValue) { + ConfigurableApplicationContext ctx, boolean expectedValue) { final FeignCircuitBreakerTargeter bean = ctx.getBean(FeignCircuitBreakerTargeter.class); assertThat(bean).hasFieldOrPropertyWithValue("circuitBreakerGroupEnabled", expectedValue); } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptorTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptorTests.java new file mode 100644 index 000000000..4242609ba --- /dev/null +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptorTests.java @@ -0,0 +1,170 @@ +/* + * Copyright 2015-2022 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.openfeign.security; + +import java.time.Instant; +import java.util.HashMap; + +import feign.Request.HttpMethod; +import feign.RequestTemplate; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.core.AuthorizationGrantType; +import org.springframework.security.oauth2.core.OAuth2AccessToken; +import org.springframework.util.AlternativeJdkIdGenerator; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +/** + * @author Dangzhicairang(小水牛) + */ +class OAuth2AccessTokenInterceptorTests { + + private OAuth2AccessTokenInterceptor oAuth2AccessTokenInterceptor; + + private RequestTemplate requestTemplate; + + private OAuth2ClientProperties mockOAuth2ClientProperties; + + private static final String DEFAULT_CLIENT_ID = "feign-client"; + + @BeforeEach + void setUp() { + + requestTemplate = new RequestTemplate().method(HttpMethod.GET); + + mockOAuth2ClientProperties = mock(OAuth2ClientProperties.class); + given(mockOAuth2ClientProperties.getRegistration()) + .willReturn(new HashMap() { + { + put(DEFAULT_CLIENT_ID, mock(OAuth2ClientProperties.Registration.class)); + } + }); + + } + + @Test + void noTokenAcquired() { + + OAuth2AuthorizedClientService mockOAuth2AuthorizedClientService = mock(OAuth2AuthorizedClientService.class); + given(mockOAuth2AuthorizedClientService.loadAuthorizedClient(anyString(), anyString())).willReturn(null); + + oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2ClientProperties, + mockOAuth2AuthorizedClientService, mock(ClientRegistrationRepository.class)); + + OAuth2AuthorizedClientManager mockOAuth2AuthorizedClientManager = mock(OAuth2AuthorizedClientManager.class); + given(mockOAuth2AuthorizedClientManager.authorize(any())).willReturn(null); + + oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager); + + Assertions.assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(() -> oAuth2AccessTokenInterceptor.apply(requestTemplate)) + .withMessage("No token acquired, which is illegal according to the contract."); + + } + + @Test + void validTokenAcquired() { + + OAuth2AuthorizedClientService mockOAuth2AuthorizedClientService = mock(OAuth2AuthorizedClientService.class); + given(mockOAuth2AuthorizedClientService.loadAuthorizedClient(anyString(), anyString())).willReturn(null); + + oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2ClientProperties, + mockOAuth2AuthorizedClientService, mock(ClientRegistrationRepository.class)); + + OAuth2AuthorizedClientManager mockOAuth2AuthorizedClientManager = mock(OAuth2AuthorizedClientManager.class); + given(mockOAuth2AuthorizedClientManager.authorize(any())).willReturn(validTokenOAuth2AuthorizedClient()); + + oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager); + + oAuth2AccessTokenInterceptor.apply(requestTemplate); + + Assertions.assertThat(requestTemplate.headers().get("Authorization")) + .contains("Bearer Valid Token"); + } + + @Test + void expireTokenAcquired() { + + OAuth2AuthorizedClientService mockOAuth2AuthorizedClientService = mock(OAuth2AuthorizedClientService.class); + given(mockOAuth2AuthorizedClientService.loadAuthorizedClient(anyString(), anyString())).willReturn(null); + + oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2ClientProperties, + mockOAuth2AuthorizedClientService, mock(ClientRegistrationRepository.class)); + + OAuth2AuthorizedClientManager mockOAuth2AuthorizedClientManager = mock(OAuth2AuthorizedClientManager.class); + given(mockOAuth2AuthorizedClientManager.authorize(any())).willReturn(expiredTokenOAuth2AuthorizedClient()); + + oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager); + + Assertions.assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(() -> oAuth2AccessTokenInterceptor.apply(requestTemplate)) + .withMessage("No token acquired, which is illegal according to the contract."); + } + + @Test + void acquireTokenFromAuthorizedClient() { + OAuth2AuthorizedClientService mockOAuth2AuthorizedClientService = mock(OAuth2AuthorizedClientService.class); + given(mockOAuth2AuthorizedClientService.loadAuthorizedClient(anyString(), anyString())) + .willReturn(validTokenOAuth2AuthorizedClient()); + + oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2ClientProperties, + mockOAuth2AuthorizedClientService, mock(ClientRegistrationRepository.class)); + + oAuth2AccessTokenInterceptor.apply(requestTemplate); + + Assertions.assertThat(requestTemplate.headers().get("Authorization")) + .contains("Bearer Valid Token"); + } + + private OAuth2AccessToken validToken() { + return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "Valid Token", Instant.now(), + Instant.now().plusSeconds(60L)); + } + + private OAuth2AccessToken expiredToken() { + return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "Expired Token", + Instant.now().minusSeconds(61L), Instant.now().minusSeconds(60L)); + } + + private OAuth2AuthorizedClient validTokenOAuth2AuthorizedClient() { + return new OAuth2AuthorizedClient(defaultClientRegistration(), "anonymousUser", validToken()); + } + + private OAuth2AuthorizedClient expiredTokenOAuth2AuthorizedClient() { + return new OAuth2AuthorizedClient(defaultClientRegistration(), "anonymousUser", expiredToken()); + } + + private ClientRegistration defaultClientRegistration() { + return ClientRegistration.withRegistrationId(new AlternativeJdkIdGenerator().generateId() + .toString()) + .clientId(DEFAULT_CLIENT_ID).tokenUri("mock token uri") + .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS).build(); + } + +} diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index 16605ed70..a46020b66 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -19,6 +19,7 @@ 3.8.0 2.5.2 + 6.0.0-SNAPSHOT @@ -27,6 +28,11 @@ spring-security-oauth2-autoconfigure ${spring-security-oauth2-autoconfigure.version} + + org.springframework.security + spring-security-oauth2-client + ${spring-security-oauth2-client.version} + org.springframework.cloud spring-cloud-openfeign-core From 60fc4a53e7ea1f2846afeb27e37723e4adf8eee1 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Tue, 27 Sep 2022 15:14:54 +0200 Subject: [PATCH 50/62] Refactor. Add docs. Fix dependencies. --- .../main/asciidoc/spring-cloud-openfeign.adoc | 13 ++ spring-cloud-openfeign-core/pom.xml | 1 + .../openfeign/FeignAutoConfiguration.java | 9 +- .../OAuth2AccessTokenInterceptor.java | 116 ++++++++------- ...itional-spring-configuration-metadata.json | 6 + .../FeignAutoConfigurationTests.java | 15 +- .../OAuth2AccessTokenInterceptorTests.java | 136 +++++++++--------- spring-cloud-openfeign-dependencies/pom.xml | 2 - 8 files changed, 148 insertions(+), 150 deletions(-) diff --git a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc index 26cbe5202..3dd8e88e0 100644 --- a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc +++ b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc @@ -811,6 +811,19 @@ When the flag is set to true, and the oauth2 client context resource details are Sometimes, when load balancing is enabled for Feign clients, you may want to use load balancing for fetching access tokens, too. To do so, you should ensure that the load balancer is on the classpath (spring-cloud-starter-loadbalancer) and explicitly enable load balancing for OAuth2FeignRequestInterceptor by setting the following flag: ---- feign.oauth2.load-balanced=true + +When the flag is set to true, and the oauth2 client context resource details are present, a bean of class `OAuth2AccessTokenInterceptor` is created. Before each request, the interceptor resolves the required access token and includes it as a header. +`OAuth2AccessTokenInterceptor` uses the `AuthorizedClientServiceOAuth2AuthorizedClientManager` to get `OAuth2AuthorizedClient` that holds an `OAuth2AccessToken`. If the user has specified an OAuth2 `clientId` using the `spring.cloud.openfeign.oauth2.clientId` property, it will be used to retrieve the token. If the token is not retrieved or the `clientId` has not been specified, the `serviceId` retrieved from the `url` host segment will be used. + +TIP:: Using the `serviceId` as OAuth2 client id is convenient for load-balanced Feign clients. For non-load-balanced ones, the property-based `clientId` is a suitable approach. + +=== Transform the load-balanced HTTP request + +You can use the selected `ServiceInstance` to transform the load-balanced HTTP Request. + +For `Request`, you need to implement and define `LoadBalancerFeignRequestTransformer`, as follows: + +[source,java,indent=0] ---- == Configuration properties diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index b441393d0..306c44747 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -228,6 +228,7 @@ org.springframework.security spring-security-oauth2-client + true diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java index ec97c25d8..a182c0d97 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java @@ -51,7 +51,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cache.interceptor.CacheInterceptor; import org.springframework.cloud.client.actuator.HasFeatures; @@ -383,13 +382,11 @@ public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oAut @Bean @ConditionalOnBean({OAuth2AuthorizedClientService.class, ClientRegistrationRepository.class}) public OAuth2AccessTokenInterceptor defaultOAuth2AccessTokenInterceptor( - @Value("${spring.cloud.openfeign.oauth2.specifiedClientIds:}") List specifiedClientIds, - OAuth2ClientProperties oAuth2ClientProperties, + @Value("${spring.cloud.openfeign.oauth2.clientId:}") String clientId, OAuth2AuthorizedClientService oAuth2AuthorizedClientService, ClientRegistrationRepository clientRegistrationRepository) { - - return new OAuth2AccessTokenInterceptor(specifiedClientIds, oAuth2ClientProperties, - oAuth2AuthorizedClientService, clientRegistrationRepository); + return new OAuth2AccessTokenInterceptor(clientId, oAuth2AuthorizedClientService, + clientRegistrationRepository); } } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptor.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptor.java index cc0170068..b421d0ae5 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptor.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptor.java @@ -16,17 +16,14 @@ package org.springframework.cloud.openfeign.security; +import java.net.URI; import java.time.Instant; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Map; import java.util.Optional; import feign.RequestInterceptor; import feign.RequestTemplate; +import feign.Target; -import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.AuthorityUtils; @@ -38,16 +35,22 @@ import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.core.OAuth2AccessToken; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** - * RequestInterceptor for OAuth2 Feign Requests. By default, It uses the + * A {@link RequestInterceptor} for OAuth2 Feign Requests. By default, it uses the * {@link AuthorizedClientServiceOAuth2AuthorizedClientManager } to get - * {@link OAuth2AuthorizedClient } that hold an {@link OAuth2AccessToken }. Use the - * Client(s) from properties if not specific the field - * {@link OAuth2AccessTokenInterceptor#specifiedClientIds} + * {@link OAuth2AuthorizedClient } that holds an {@link OAuth2AccessToken }. If the user + * has specified an OAuth2 {@code clientId} using the + * {@code spring.cloud.openfeign.oauth2.clientId} property, it will be used to retrieve + * the token. If the token is not retrieved or the {@code clientId} has not been + * specified, the {@code serviceId} retrieved from the {@code url} host segment will be + * used. This approach is convenient for load-balanced Feign clients. For + * non-load-balanced ones, the property-based {@code clientId} is a suitable approach. * * @author Dangzhicairang(小水牛) + * @author Olga Maciaszek-Sharma * @since 4.0.0 */ public class OAuth2AccessTokenInterceptor implements RequestInterceptor { @@ -66,9 +69,7 @@ public class OAuth2AccessTokenInterceptor implements RequestInterceptor { private final String header; - private final List specifiedClientIds; - - private final OAuth2ClientProperties oAuth2ClientProperties; + private final String clientId; private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService; @@ -79,69 +80,56 @@ public void setAuthorizedClientManager(OAuth2AuthorizedClientManager authorizedC } private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken("anonymous", - "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); + "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); - public OAuth2AccessTokenInterceptor(OAuth2ClientProperties oAuth2ClientProperties, - OAuth2AuthorizedClientService oAuth2AuthorizedClientService, - ClientRegistrationRepository clientRegistrationRepository) { - this(new ArrayList<>(), oAuth2ClientProperties, oAuth2AuthorizedClientService, clientRegistrationRepository); + public OAuth2AccessTokenInterceptor(OAuth2AuthorizedClientService oAuth2AuthorizedClientService, + ClientRegistrationRepository clientRegistrationRepository) { + this(null, oAuth2AuthorizedClientService, clientRegistrationRepository); } - public OAuth2AccessTokenInterceptor(List specifiedClientIds, OAuth2ClientProperties oAuth2ClientProperties, - OAuth2AuthorizedClientService oAuth2AuthorizedClientService, - ClientRegistrationRepository clientRegistrationRepository) { - this(BEARER, AUTHORIZATION, specifiedClientIds, oAuth2ClientProperties, oAuth2AuthorizedClientService, - clientRegistrationRepository); + public OAuth2AccessTokenInterceptor(String clientId, OAuth2AuthorizedClientService oAuth2AuthorizedClientService, + ClientRegistrationRepository clientRegistrationRepository) { + this(BEARER, AUTHORIZATION, clientId, oAuth2AuthorizedClientService, clientRegistrationRepository); } - public OAuth2AccessTokenInterceptor(String tokenType, String header, List specifiedClientIds, - OAuth2ClientProperties oAuth2ClientProperties, OAuth2AuthorizedClientService oAuth2AuthorizedClientService, - ClientRegistrationRepository clientRegistrationRepository) { + public OAuth2AccessTokenInterceptor(String tokenType, String header, String clientId, + OAuth2AuthorizedClientService oAuth2AuthorizedClientService, + ClientRegistrationRepository clientRegistrationRepository) { this.tokenType = tokenType; this.header = header; - this.specifiedClientIds = specifiedClientIds; - this.oAuth2ClientProperties = oAuth2ClientProperties; + this.clientId = clientId; this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService; this.authorizedClientManager = new AuthorizedClientServiceOAuth2AuthorizedClientManager( - clientRegistrationRepository, this.oAuth2AuthorizedClientService); + clientRegistrationRepository, this.oAuth2AuthorizedClientService); } @Override public void apply(RequestTemplate template) { + OAuth2AccessToken token = getToken(template); + String extractedToken = String.format("%s %s", tokenType, token.getTokenValue()); template.header(header); - template.header(header, extract(tokenType)); - } - - protected String extract(String tokenType) { - OAuth2AccessToken accessToken = getToken(); - return String.format("%s %s", tokenType, accessToken.getTokenValue()); + template.header(header, extractedToken); } - public OAuth2AccessToken getToken() { - - // if specific, try to use them to get token. - for (String clientId : this.specifiedClientIds) { - OAuth2AccessToken token = this.getToken(clientId); + public OAuth2AccessToken getToken(RequestTemplate template) { + // If specified, try to use them to get token. + if (StringUtils.hasText(clientId)) { + OAuth2AccessToken token = getToken(clientId); if (token != null) { return token; } } - // use clients from properties by default - for (String clientId : Optional.ofNullable(this.oAuth2ClientProperties) - .map(OAuth2ClientProperties::getRegistration).map(Map::keySet) - .orElse(new HashSet<>())) { - OAuth2AccessToken token = this.getToken(clientId); - if (token != null) { - return token; - } + // If not specified use host (synonymous with serviceId for load-balanced + // requests; non-load-balanced requests should use the method above). + OAuth2AccessToken token = getToken(getServiceId(template)); + if (token != null) { + return token; } - - throw new IllegalStateException("No token acquired, which is illegal according to the contract."); + throw new IllegalStateException("OAuth2 token has not been successfully acquired."); } protected OAuth2AccessToken getToken(String clientId) { - if (!StringUtils.hasText(clientId)) { return null; } @@ -151,27 +139,35 @@ protected OAuth2AccessToken getToken(String clientId) { principal = ANONYMOUS_AUTHENTICATION; } - // already exist + // Already exist OAuth2AuthorizedClient oAuth2AuthorizedClient = oAuth2AuthorizedClientService.loadAuthorizedClient(clientId, - principal.getName()); + principal.getName()); if (oAuth2AuthorizedClient != null) { OAuth2AccessToken accessToken = oAuth2AuthorizedClient.getAccessToken(); - if (accessToken != null && this.noExpire(accessToken)) { + if (accessToken != null && notExpired(accessToken)) { return accessToken; } } OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(clientId) - .principal(principal).build(); - OAuth2AuthorizedClient authorize = this.authorizedClientManager.authorize(authorizeRequest); - return Optional.ofNullable(authorize).map(OAuth2AuthorizedClient::getAccessToken) - .filter(this::noExpire) - .orElse(null); + .principal(principal).build(); + OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest); + return Optional.ofNullable(authorizedClient).map(OAuth2AuthorizedClient::getAccessToken) + .filter(this::notExpired).orElse(null); } - protected boolean noExpire(OAuth2AccessToken token) { + protected boolean notExpired(OAuth2AccessToken token) { return Optional.ofNullable(token).map(OAuth2AccessToken::getExpiresAt) - .map(expire -> expire.isAfter(Instant.now())).orElse(false); + .map(expire -> expire.isAfter(Instant.now())).orElse(false); + } + + private static String getServiceId(RequestTemplate template) { + Target feignTarget = template.feignTarget(); + Assert.notNull(feignTarget, "feignTarget may not be null"); + String url = feignTarget.url(); + Assert.hasLength(url, "url may not be empty"); + final URI originalUri = URI.create(url); + return originalUri.getHost(); } } diff --git a/spring-cloud-openfeign-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-openfeign-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 73ff218b2..88c42c56c 100644 --- a/spring-cloud-openfeign-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-openfeign-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -79,6 +79,12 @@ "type": "java.lang.Boolean", "description": "Enables load balancing for oauth2 access token provider.", "defaultValue": "false" + }, + { + "name": "feign.oauth2.registrationClientId", + "type": "java.lang.String", + "description": "Provides a clientId to be used with OAuth2.", + "defaultValue": "" } ] } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignAutoConfigurationTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignAutoConfigurationTests.java index 4997f29e0..a8f1c9197 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignAutoConfigurationTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignAutoConfigurationTests.java @@ -17,8 +17,6 @@ package org.springframework.cloud.openfeign; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; import feign.Target; import org.assertj.core.api.Condition; @@ -141,17 +139,12 @@ void shouldInstantiateFeignOAuth2FeignRequestInterceptorWithCustomAccessTokenPro @Test void shouldInstantiateFeignOAuth2FeignRequestInterceptor() { runner.withPropertyValues("spring.cloud.openfeign.oauth2.enabled=true", - "spring.cloud.openfeign.oauth2.specifiedClientIds=feign-client") + "spring.cloud.openfeign.oauth2.clientId=feign-client") .withBean(OAuth2AuthorizedClientService.class, () -> mock(OAuth2AuthorizedClientService.class)) .withBean(ClientRegistrationRepository.class, () -> mock(ClientRegistrationRepository.class)) .run(ctx -> { assertOauth2AccessTokenInterceptorExists(ctx); - assertThatOauth2AccessTokenInterceptorHasSpecifiedIdsPropertyWithValue(ctx, - new ArrayList() { - { - add("feign-client"); - } - }); + assertThatOauth2AccessTokenInterceptorHasSpecifiedIdsPropertyWithValue(ctx, "feign-client"); }); } @@ -184,9 +177,9 @@ private void assertOauth2AccessTokenInterceptorExists(ConfigurableApplicationCon } private void assertThatOauth2AccessTokenInterceptorHasSpecifiedIdsPropertyWithValue( - ConfigurableApplicationContext ctx, List expectedValue) { + ConfigurableApplicationContext ctx, String expectedValue) { final OAuth2AccessTokenInterceptor bean = ctx.getBean(OAuth2AccessTokenInterceptor.class); - assertThat(bean).hasFieldOrPropertyWithValue("specifiedClientIds", expectedValue); + assertThat(bean).hasFieldOrPropertyWithValue("clientId", expectedValue); } private void assertOnlyOneTargeterPresent(ConfigurableApplicationContext ctx, Class beanClass) { diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptorTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptorTests.java index 4242609ba..9a825e57f 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptorTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptorTests.java @@ -17,15 +17,14 @@ package org.springframework.cloud.openfeign.security; import java.time.Instant; -import java.util.HashMap; import feign.Request.HttpMethod; import feign.RequestTemplate; -import org.assertj.core.api.Assertions; +import feign.Target; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties; +import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; @@ -35,121 +34,117 @@ import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.util.AlternativeJdkIdGenerator; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.BDDMockito.given; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** + * Tests for {@link OAuth2AccessTokenInterceptor}. + * * @author Dangzhicairang(小水牛) + * @author Olga Maciaszek-Sharma + * */ class OAuth2AccessTokenInterceptorTests { + private final OAuth2AuthorizedClientService mockOAuth2AuthorizedClientService = mock( + OAuth2AuthorizedClientService.class); + + private final OAuth2AuthorizedClientManager mockOAuth2AuthorizedClientManager = mock( + OAuth2AuthorizedClientManager.class); + private OAuth2AccessTokenInterceptor oAuth2AccessTokenInterceptor; private RequestTemplate requestTemplate; - private OAuth2ClientProperties mockOAuth2ClientProperties; - private static final String DEFAULT_CLIENT_ID = "feign-client"; @BeforeEach void setUp() { - requestTemplate = new RequestTemplate().method(HttpMethod.GET); - - mockOAuth2ClientProperties = mock(OAuth2ClientProperties.class); - given(mockOAuth2ClientProperties.getRegistration()) - .willReturn(new HashMap() { - { - put(DEFAULT_CLIENT_ID, mock(OAuth2ClientProperties.Registration.class)); - } - }); - + Target feignTarget = mock(Target.class); + when(feignTarget.url()).thenReturn("http://test"); + requestTemplate.feignTarget(feignTarget); } @Test - void noTokenAcquired() { - - OAuth2AuthorizedClientService mockOAuth2AuthorizedClientService = mock(OAuth2AuthorizedClientService.class); - given(mockOAuth2AuthorizedClientService.loadAuthorizedClient(anyString(), anyString())).willReturn(null); - - oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2ClientProperties, - mockOAuth2AuthorizedClientService, mock(ClientRegistrationRepository.class)); - - OAuth2AuthorizedClientManager mockOAuth2AuthorizedClientManager = mock(OAuth2AuthorizedClientManager.class); - given(mockOAuth2AuthorizedClientManager.authorize(any())).willReturn(null); - + void shouldThrowExceptionWhenNoTokenAcquired() { + when(mockOAuth2AuthorizedClientService.loadAuthorizedClient(anyString(), anyString())).thenReturn(null); + oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientService, + mock(ClientRegistrationRepository.class)); + when(mockOAuth2AuthorizedClientManager.authorize(any())).thenReturn(null); oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager); - Assertions.assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> oAuth2AccessTokenInterceptor.apply(requestTemplate)) - .withMessage("No token acquired, which is illegal according to the contract."); - + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(() -> oAuth2AccessTokenInterceptor.apply(requestTemplate)) + .withMessage("OAuth2 token has not been successfully acquired."); } @Test - void validTokenAcquired() { - - OAuth2AuthorizedClientService mockOAuth2AuthorizedClientService = mock(OAuth2AuthorizedClientService.class); - given(mockOAuth2AuthorizedClientService.loadAuthorizedClient(anyString(), anyString())).willReturn(null); - - oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2ClientProperties, - mockOAuth2AuthorizedClientService, mock(ClientRegistrationRepository.class)); - - OAuth2AuthorizedClientManager mockOAuth2AuthorizedClientManager = mock(OAuth2AuthorizedClientManager.class); - given(mockOAuth2AuthorizedClientManager.authorize(any())).willReturn(validTokenOAuth2AuthorizedClient()); - + void shouldAcquireValidToken() { + when(mockOAuth2AuthorizedClientService.loadAuthorizedClient(anyString(), anyString())).thenReturn(null); + oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientService, + mock(ClientRegistrationRepository.class)); + when(mockOAuth2AuthorizedClientManager.authorize( + argThat((OAuth2AuthorizeRequest request) -> ("test").equals(request.getClientRegistrationId())))) + .thenReturn(validTokenOAuth2AuthorizedClient()); oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager); oAuth2AccessTokenInterceptor.apply(requestTemplate); - Assertions.assertThat(requestTemplate.headers().get("Authorization")) - .contains("Bearer Valid Token"); + assertThat(requestTemplate.headers().get("Authorization")).contains("Bearer Valid Token"); } @Test - void expireTokenAcquired() { - - OAuth2AuthorizedClientService mockOAuth2AuthorizedClientService = mock(OAuth2AuthorizedClientService.class); - given(mockOAuth2AuthorizedClientService.loadAuthorizedClient(anyString(), anyString())).willReturn(null); + void shouldThrowExceptionWhenExpiredTokenAcquired() { + when(mockOAuth2AuthorizedClientService.loadAuthorizedClient(anyString(), anyString())).thenReturn(null); + oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientService, + mock(ClientRegistrationRepository.class)); + when(mockOAuth2AuthorizedClientManager.authorize(any())).thenReturn(expiredTokenOAuth2AuthorizedClient()); + oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager); - oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2ClientProperties, - mockOAuth2AuthorizedClientService, mock(ClientRegistrationRepository.class)); + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(() -> oAuth2AccessTokenInterceptor.apply(requestTemplate)) + .withMessage("OAuth2 token has not been successfully acquired."); + } - OAuth2AuthorizedClientManager mockOAuth2AuthorizedClientManager = mock(OAuth2AuthorizedClientManager.class); - given(mockOAuth2AuthorizedClientManager.authorize(any())).willReturn(expiredTokenOAuth2AuthorizedClient()); + @Test + void shouldAcquireTokenFromAuthorizedClient() { + when(mockOAuth2AuthorizedClientService.loadAuthorizedClient(eq("test"), anyString())) + .thenReturn(validTokenOAuth2AuthorizedClient()); + oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientService, + mock(ClientRegistrationRepository.class)); - oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager); + oAuth2AccessTokenInterceptor.apply(requestTemplate); - Assertions.assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> oAuth2AccessTokenInterceptor.apply(requestTemplate)) - .withMessage("No token acquired, which is illegal according to the contract."); + assertThat(requestTemplate.headers().get("Authorization")).contains("Bearer Valid Token"); } @Test - void acquireTokenFromAuthorizedClient() { - OAuth2AuthorizedClientService mockOAuth2AuthorizedClientService = mock(OAuth2AuthorizedClientService.class); - given(mockOAuth2AuthorizedClientService.loadAuthorizedClient(anyString(), anyString())) - .willReturn(validTokenOAuth2AuthorizedClient()); - - oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2ClientProperties, - mockOAuth2AuthorizedClientService, mock(ClientRegistrationRepository.class)); + void shouldAcquireValidTokenFromSpecifiedClientId() { + when(mockOAuth2AuthorizedClientService.loadAuthorizedClient(eq("testId"), anyString())) + .thenReturn(validTokenOAuth2AuthorizedClient()); + oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor("testId", mockOAuth2AuthorizedClientService, + mock(ClientRegistrationRepository.class)); oAuth2AccessTokenInterceptor.apply(requestTemplate); - Assertions.assertThat(requestTemplate.headers().get("Authorization")) - .contains("Bearer Valid Token"); + assertThat(requestTemplate.headers().get("Authorization")).contains("Bearer Valid Token"); } private OAuth2AccessToken validToken() { return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "Valid Token", Instant.now(), - Instant.now().plusSeconds(60L)); + Instant.now().plusSeconds(60L)); } private OAuth2AccessToken expiredToken() { return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "Expired Token", - Instant.now().minusSeconds(61L), Instant.now().minusSeconds(60L)); + Instant.now().minusSeconds(61L), Instant.now().minusSeconds(60L)); } private OAuth2AuthorizedClient validTokenOAuth2AuthorizedClient() { @@ -161,10 +156,9 @@ private OAuth2AuthorizedClient expiredTokenOAuth2AuthorizedClient() { } private ClientRegistration defaultClientRegistration() { - return ClientRegistration.withRegistrationId(new AlternativeJdkIdGenerator().generateId() - .toString()) - .clientId(DEFAULT_CLIENT_ID).tokenUri("mock token uri") - .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS).build(); + return ClientRegistration.withRegistrationId(new AlternativeJdkIdGenerator().generateId().toString()) + .clientId(DEFAULT_CLIENT_ID).tokenUri("mock token uri") + .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS).build(); } } diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index a46020b66..70929ac70 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -19,7 +19,6 @@ 3.8.0 2.5.2 - 6.0.0-SNAPSHOT @@ -31,7 +30,6 @@ org.springframework.security spring-security-oauth2-client - ${spring-security-oauth2-client.version} org.springframework.cloud From afbe0b232b834f99b79fb9a00fdb9b313d79ec86 Mon Sep 17 00:00:00 2001 From: dzcr <1137729123@qq.com> Date: Fri, 30 Sep 2022 11:13:43 +0800 Subject: [PATCH 51/62] Adjust based on review. --- .../main/asciidoc/spring-cloud-openfeign.adoc | 4 +- .../openfeign/FeignAutoConfiguration.java | 13 ++-- .../OAuth2AccessTokenInterceptor.java | 45 ++++--------- .../FeignAutoConfigurationTests.java | 23 ++++--- .../OAuth2AccessTokenInterceptorTests.java | 63 ++++++------------- 5 files changed, 53 insertions(+), 95 deletions(-) diff --git a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc index 3dd8e88e0..2c893da44 100644 --- a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc +++ b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc @@ -813,9 +813,9 @@ Sometimes, when load balancing is enabled for Feign clients, you may want to use feign.oauth2.load-balanced=true When the flag is set to true, and the oauth2 client context resource details are present, a bean of class `OAuth2AccessTokenInterceptor` is created. Before each request, the interceptor resolves the required access token and includes it as a header. -`OAuth2AccessTokenInterceptor` uses the `AuthorizedClientServiceOAuth2AuthorizedClientManager` to get `OAuth2AuthorizedClient` that holds an `OAuth2AccessToken`. If the user has specified an OAuth2 `clientId` using the `spring.cloud.openfeign.oauth2.clientId` property, it will be used to retrieve the token. If the token is not retrieved or the `clientId` has not been specified, the `serviceId` retrieved from the `url` host segment will be used. +`OAuth2AccessTokenInterceptor` uses the `AuthorizedClientServiceOAuth2AuthorizedClientManager` to get `OAuth2AuthorizedClient` that holds an `OAuth2AccessToken`. If the user has specified an OAuth2 `clientRegistrationId` using the `spring.cloud.openfeign.oauth2.clientRegistrationId` property, it will be used to retrieve the token. If the token is not retrieved or the `clientRegistrationId` has not been specified, the `serviceId` retrieved from the `url` host segment will be used. -TIP:: Using the `serviceId` as OAuth2 client id is convenient for load-balanced Feign clients. For non-load-balanced ones, the property-based `clientId` is a suitable approach. +TIP:: Using the `serviceId` as OAuth2 client registrationId is convenient for load-balanced Feign clients. For non-load-balanced ones, the property-based `clientRegistrationId` is a suitable approach. === Transform the load-balanced HTTP request diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java index a182c0d97..2bec63154 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java @@ -75,6 +75,7 @@ import org.springframework.context.annotation.Import; import org.springframework.data.domain.Page; import org.springframework.data.domain.Sort; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.OAuth2ClientContext; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; @@ -380,13 +381,13 @@ public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oAut } @Bean - @ConditionalOnBean({OAuth2AuthorizedClientService.class, ClientRegistrationRepository.class}) + @ConditionalOnBean({ OAuth2AuthorizedClientService.class, ClientRegistrationRepository.class }) public OAuth2AccessTokenInterceptor defaultOAuth2AccessTokenInterceptor( - @Value("${spring.cloud.openfeign.oauth2.clientId:}") String clientId, - OAuth2AuthorizedClientService oAuth2AuthorizedClientService, - ClientRegistrationRepository clientRegistrationRepository) { - return new OAuth2AccessTokenInterceptor(clientId, oAuth2AuthorizedClientService, - clientRegistrationRepository); + @Value("${spring.cloud.openfeign.oauth2.clientRegistrationId:}") String clientRegistrationId, + OAuth2AuthorizedClientService oAuth2AuthorizedClientService, + ClientRegistrationRepository clientRegistrationRepository) { + return new OAuth2AccessTokenInterceptor(clientRegistrationId, oAuth2AuthorizedClientService, + clientRegistrationRepository); } } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptor.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptor.java index b421d0ae5..dacd8b30c 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptor.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptor.java @@ -17,7 +17,6 @@ package org.springframework.cloud.openfeign.security; import java.net.URI; -import java.time.Instant; import java.util.Optional; import feign.RequestInterceptor; @@ -69,9 +68,7 @@ public class OAuth2AccessTokenInterceptor implements RequestInterceptor { private final String header; - private final String clientId; - - private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService; + private final String clientRegistrationId; private OAuth2AuthorizedClientManager authorizedClientManager; @@ -87,20 +84,20 @@ public OAuth2AccessTokenInterceptor(OAuth2AuthorizedClientService oAuth2Authoriz this(null, oAuth2AuthorizedClientService, clientRegistrationRepository); } - public OAuth2AccessTokenInterceptor(String clientId, OAuth2AuthorizedClientService oAuth2AuthorizedClientService, + public OAuth2AccessTokenInterceptor(String clientRegistrationId, + OAuth2AuthorizedClientService oAuth2AuthorizedClientService, ClientRegistrationRepository clientRegistrationRepository) { - this(BEARER, AUTHORIZATION, clientId, oAuth2AuthorizedClientService, clientRegistrationRepository); + this(BEARER, AUTHORIZATION, clientRegistrationId, oAuth2AuthorizedClientService, clientRegistrationRepository); } - public OAuth2AccessTokenInterceptor(String tokenType, String header, String clientId, + public OAuth2AccessTokenInterceptor(String tokenType, String header, String clientRegistrationId, OAuth2AuthorizedClientService oAuth2AuthorizedClientService, ClientRegistrationRepository clientRegistrationRepository) { this.tokenType = tokenType; this.header = header; - this.clientId = clientId; - this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService; + this.clientRegistrationId = clientRegistrationId; this.authorizedClientManager = new AuthorizedClientServiceOAuth2AuthorizedClientManager( - clientRegistrationRepository, this.oAuth2AuthorizedClientService); + clientRegistrationRepository, oAuth2AuthorizedClientService); } @Override @@ -113,8 +110,8 @@ public void apply(RequestTemplate template) { public OAuth2AccessToken getToken(RequestTemplate template) { // If specified, try to use them to get token. - if (StringUtils.hasText(clientId)) { - OAuth2AccessToken token = getToken(clientId); + if (StringUtils.hasText(clientRegistrationId)) { + OAuth2AccessToken token = getToken(clientRegistrationId); if (token != null) { return token; } @@ -129,8 +126,8 @@ public OAuth2AccessToken getToken(RequestTemplate template) { throw new IllegalStateException("OAuth2 token has not been successfully acquired."); } - protected OAuth2AccessToken getToken(String clientId) { - if (!StringUtils.hasText(clientId)) { + protected OAuth2AccessToken getToken(String clientRegistrationId) { + if (!StringUtils.hasText(clientRegistrationId)) { return null; } @@ -139,26 +136,10 @@ protected OAuth2AccessToken getToken(String clientId) { principal = ANONYMOUS_AUTHENTICATION; } - // Already exist - OAuth2AuthorizedClient oAuth2AuthorizedClient = oAuth2AuthorizedClientService.loadAuthorizedClient(clientId, - principal.getName()); - if (oAuth2AuthorizedClient != null) { - OAuth2AccessToken accessToken = oAuth2AuthorizedClient.getAccessToken(); - if (accessToken != null && notExpired(accessToken)) { - return accessToken; - } - } - - OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(clientId) + OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId(clientRegistrationId) .principal(principal).build(); OAuth2AuthorizedClient authorizedClient = authorizedClientManager.authorize(authorizeRequest); - return Optional.ofNullable(authorizedClient).map(OAuth2AuthorizedClient::getAccessToken) - .filter(this::notExpired).orElse(null); - } - - protected boolean notExpired(OAuth2AccessToken token) { - return Optional.ofNullable(token).map(OAuth2AccessToken::getExpiresAt) - .map(expire -> expire.isAfter(Instant.now())).orElse(false); + return Optional.ofNullable(authorizedClient).map(OAuth2AuthorizedClient::getAccessToken).orElse(null); } private static String getServiceId(RequestTemplate template) { diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignAutoConfigurationTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignAutoConfigurationTests.java index a8f1c9197..b43fba31c 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignAutoConfigurationTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignAutoConfigurationTests.java @@ -139,13 +139,13 @@ void shouldInstantiateFeignOAuth2FeignRequestInterceptorWithCustomAccessTokenPro @Test void shouldInstantiateFeignOAuth2FeignRequestInterceptor() { runner.withPropertyValues("spring.cloud.openfeign.oauth2.enabled=true", - "spring.cloud.openfeign.oauth2.clientId=feign-client") - .withBean(OAuth2AuthorizedClientService.class, () -> mock(OAuth2AuthorizedClientService.class)) - .withBean(ClientRegistrationRepository.class, () -> mock(ClientRegistrationRepository.class)) - .run(ctx -> { - assertOauth2AccessTokenInterceptorExists(ctx); - assertThatOauth2AccessTokenInterceptorHasSpecifiedIdsPropertyWithValue(ctx, "feign-client"); - }); + "spring.cloud.openfeign.oauth2.clientRegistrationId=feign-client") + .withBean(OAuth2AuthorizedClientService.class, () -> mock(OAuth2AuthorizedClientService.class)) + .withBean(ClientRegistrationRepository.class, () -> mock(ClientRegistrationRepository.class)) + .run(ctx -> { + assertOauth2AccessTokenInterceptorExists(ctx); + assertThatOauth2AccessTokenInterceptorHasSpecifiedIdsPropertyWithValue(ctx, "feign-client"); + }); } private void assertOauth2FeignRequestInterceptorExists(ConfigurableApplicationContext ctx) { @@ -177,20 +177,19 @@ private void assertOauth2AccessTokenInterceptorExists(ConfigurableApplicationCon } private void assertThatOauth2AccessTokenInterceptorHasSpecifiedIdsPropertyWithValue( - ConfigurableApplicationContext ctx, String expectedValue) { + ConfigurableApplicationContext ctx, String expectedValue) { final OAuth2AccessTokenInterceptor bean = ctx.getBean(OAuth2AccessTokenInterceptor.class); - assertThat(bean).hasFieldOrPropertyWithValue("clientId", expectedValue); + assertThat(bean).hasFieldOrPropertyWithValue("clientRegistrationId", expectedValue); } private void assertOnlyOneTargeterPresent(ConfigurableApplicationContext ctx, Class beanClass) { - assertThat(ctx.getBeansOfType(Targeter.class)).hasSize(1) - .hasValueSatisfying(new Condition<>( + assertThat(ctx.getBeansOfType(Targeter.class)).hasSize(1).hasValueSatisfying(new Condition<>( beanClass::isInstance, String.format("Targeter should be an instance of %s", beanClass))); } private void assertThatFeignCircuitBreakerTargeterHasGroupEnabledPropertyWithValue( - ConfigurableApplicationContext ctx, boolean expectedValue) { + ConfigurableApplicationContext ctx, boolean expectedValue) { final FeignCircuitBreakerTargeter bean = ctx.getBean(FeignCircuitBreakerTargeter.class); assertThat(bean).hasFieldOrPropertyWithValue("circuitBreakerGroupEnabled", expectedValue); } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptorTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptorTests.java index 9a825e57f..acedec4e0 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptorTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptorTests.java @@ -32,14 +32,11 @@ import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.OAuth2AccessToken; -import org.springframework.util.AlternativeJdkIdGenerator; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -52,6 +49,9 @@ */ class OAuth2AccessTokenInterceptorTests { + private final ClientRegistrationRepository mockClientRegistrationRepository = mock( + ClientRegistrationRepository.class); + private final OAuth2AuthorizedClientService mockOAuth2AuthorizedClientService = mock( OAuth2AuthorizedClientService.class); @@ -62,7 +62,7 @@ class OAuth2AccessTokenInterceptorTests { private RequestTemplate requestTemplate; - private static final String DEFAULT_CLIENT_ID = "feign-client"; + private static final String DEFAULT_CLIENT_REGISTRATION_ID = "feign-client"; @BeforeEach void setUp() { @@ -74,9 +74,8 @@ void setUp() { @Test void shouldThrowExceptionWhenNoTokenAcquired() { - when(mockOAuth2AuthorizedClientService.loadAuthorizedClient(anyString(), anyString())).thenReturn(null); oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientService, - mock(ClientRegistrationRepository.class)); + mockClientRegistrationRepository); when(mockOAuth2AuthorizedClientManager.authorize(any())).thenReturn(null); oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager); @@ -87,12 +86,9 @@ void shouldThrowExceptionWhenNoTokenAcquired() { @Test void shouldAcquireValidToken() { - when(mockOAuth2AuthorizedClientService.loadAuthorizedClient(anyString(), anyString())).thenReturn(null); oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientService, - mock(ClientRegistrationRepository.class)); - when(mockOAuth2AuthorizedClientManager.authorize( - argThat((OAuth2AuthorizeRequest request) -> ("test").equals(request.getClientRegistrationId())))) - .thenReturn(validTokenOAuth2AuthorizedClient()); + mockClientRegistrationRepository); + when(mockOAuth2AuthorizedClientManager.authorize(any())).thenReturn(validTokenOAuth2AuthorizedClient()); oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager); oAuth2AccessTokenInterceptor.apply(requestTemplate); @@ -101,25 +97,14 @@ void shouldAcquireValidToken() { } @Test - void shouldThrowExceptionWhenExpiredTokenAcquired() { - when(mockOAuth2AuthorizedClientService.loadAuthorizedClient(anyString(), anyString())).thenReturn(null); + void shouldAcquireValidTokenFromServiceId() { oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientService, - mock(ClientRegistrationRepository.class)); - when(mockOAuth2AuthorizedClientManager.authorize(any())).thenReturn(expiredTokenOAuth2AuthorizedClient()); + mockClientRegistrationRepository); + when(mockOAuth2AuthorizedClientManager.authorize( + argThat((OAuth2AuthorizeRequest request) -> ("test").equals(request.getClientRegistrationId())))) + .thenReturn(validTokenOAuth2AuthorizedClient()); oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager); - assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> oAuth2AccessTokenInterceptor.apply(requestTemplate)) - .withMessage("OAuth2 token has not been successfully acquired."); - } - - @Test - void shouldAcquireTokenFromAuthorizedClient() { - when(mockOAuth2AuthorizedClientService.loadAuthorizedClient(eq("test"), anyString())) - .thenReturn(validTokenOAuth2AuthorizedClient()); - oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientService, - mock(ClientRegistrationRepository.class)); - oAuth2AccessTokenInterceptor.apply(requestTemplate); assertThat(requestTemplate.headers().get("Authorization")).contains("Bearer Valid Token"); @@ -127,10 +112,12 @@ void shouldAcquireTokenFromAuthorizedClient() { @Test void shouldAcquireValidTokenFromSpecifiedClientId() { - when(mockOAuth2AuthorizedClientService.loadAuthorizedClient(eq("testId"), anyString())) - .thenReturn(validTokenOAuth2AuthorizedClient()); - oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor("testId", mockOAuth2AuthorizedClientService, - mock(ClientRegistrationRepository.class)); + oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(DEFAULT_CLIENT_REGISTRATION_ID, + mockOAuth2AuthorizedClientService, mockClientRegistrationRepository); + when(mockOAuth2AuthorizedClientManager + .authorize(argThat((OAuth2AuthorizeRequest request) -> (DEFAULT_CLIENT_REGISTRATION_ID) + .equals(request.getClientRegistrationId())))).thenReturn(validTokenOAuth2AuthorizedClient()); + oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager); oAuth2AccessTokenInterceptor.apply(requestTemplate); @@ -142,23 +129,13 @@ private OAuth2AccessToken validToken() { Instant.now().plusSeconds(60L)); } - private OAuth2AccessToken expiredToken() { - return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "Expired Token", - Instant.now().minusSeconds(61L), Instant.now().minusSeconds(60L)); - } - private OAuth2AuthorizedClient validTokenOAuth2AuthorizedClient() { return new OAuth2AuthorizedClient(defaultClientRegistration(), "anonymousUser", validToken()); } - private OAuth2AuthorizedClient expiredTokenOAuth2AuthorizedClient() { - return new OAuth2AuthorizedClient(defaultClientRegistration(), "anonymousUser", expiredToken()); - } - private ClientRegistration defaultClientRegistration() { - return ClientRegistration.withRegistrationId(new AlternativeJdkIdGenerator().generateId().toString()) - .clientId(DEFAULT_CLIENT_ID).tokenUri("mock token uri") - .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS).build(); + return ClientRegistration.withRegistrationId(DEFAULT_CLIENT_REGISTRATION_ID).clientId("clientId") + .tokenUri("mock token uri").authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS).build(); } } From 77072d96e47b2e7cfcba85eb537601a1bad34b30 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Fri, 30 Sep 2022 14:30:30 +0200 Subject: [PATCH 52/62] Inject OAuth2AuthorizedClientManager via constructor. --- docs/src/main/asciidoc/_configprops.adoc | 5 ++- .../main/asciidoc/spring-cloud-openfeign.adoc | 16 +++++-- .../openfeign/FeignAutoConfiguration.java | 33 ++++++++++---- .../OAuth2AccessTokenInterceptor.java | 45 +++++++------------ .../OAuth2FeignRequestInterceptor.java | 1 + .../OAuth2FeignRequestInterceptorBuilder.java | 2 + ...uth2FeignRequestInterceptorConfigurer.java | 2 + ...itional-spring-configuration-metadata.json | 6 +-- .../FeignAutoConfigurationTests.java | 31 ++++++------- .../OAuth2AccessTokenInterceptorTests.java | 29 ++++-------- spring-cloud-openfeign-dependencies/pom.xml | 2 + 11 files changed, 89 insertions(+), 83 deletions(-) diff --git a/docs/src/main/asciidoc/_configprops.adoc b/docs/src/main/asciidoc/_configprops.adoc index 802070913..31a6377d0 100644 --- a/docs/src/main/asciidoc/_configprops.adoc +++ b/docs/src/main/asciidoc/_configprops.adoc @@ -4,7 +4,7 @@ |feign.autoconfiguration.jackson.enabled | `+++false+++` | If true, PageJacksonModule and SortJacksonModule bean will be provided for Jackson page decoding. |feign.circuitbreaker.alphanumeric-ids.enabled | `+++false+++` | If true, Circuit Breaker ids will only contain alphanumeric characters to allow for configuration via configuration properties. |feign.circuitbreaker.enabled | `+++false+++` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker. -|feign.circuitbreaker.group.enabled | `+++false+++` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker with with group. +|feign.circuitbreaker.group.enabled | `+++false+++` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker with group. |feign.client.config | | |feign.client.decode-slash | `+++true+++` | Feign clients do not encode slash `/` characters by default. To change this behavior, set the `decodeSlash` to `false`. |feign.client.default-config | `+++default+++` | @@ -31,8 +31,9 @@ |feign.httpclient.time-to-live | `+++900+++` | |feign.httpclient.time-to-live-unit | | |feign.metrics.enabled | `+++true+++` | Enables metrics capability for Feign. +|feign.oauth2.clientRegistrationId | | Provides a clientRegistrationId to be used with OAuth2. |feign.oauth2.enabled | `+++false+++` | Enables feign interceptor for managing oauth2 access token. |feign.oauth2.load-balanced | `+++false+++` | Enables load balancing for oauth2 access token provider. |feign.okhttp.enabled | `+++false+++` | Enables the use of the OK HTTP Client by Feign. -|=== \ No newline at end of file +|=== diff --git a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc index 2c893da44..271f42c0a 100644 --- a/docs/src/main/asciidoc/spring-cloud-openfeign.adoc +++ b/docs/src/main/asciidoc/spring-cloud-openfeign.adoc @@ -807,16 +807,26 @@ OAuth2 support can be enabled by setting following flag: ---- feign.oauth2.enabled=true ---- -When the flag is set to true, and the oauth2 client context resource details are present, a bean of class `OAuth2FeignRequestInterceptor` is created. Before each request, the interceptor resolves the required access token and includes it as a header. + +==== Deprecated OAuth2 Support + +When the flag is set to `true`, and `spring-security-oauth2-autoconfigure` is present in the classpath and the oauth2 client context resource details are present and `OAuth2ClientContext` and `OAuth2ProtectedResourceDetails` beans are present, a bean of class `OAuth2FeignRequestInterceptor` is created. Before each request, the interceptor resolves the required access token and includes it as a header. Sometimes, when load balancing is enabled for Feign clients, you may want to use load balancing for fetching access tokens, too. To do so, you should ensure that the load balancer is on the classpath (spring-cloud-starter-loadbalancer) and explicitly enable load balancing for OAuth2FeignRequestInterceptor by setting the following flag: ---- feign.oauth2.load-balanced=true +---- -When the flag is set to true, and the oauth2 client context resource details are present, a bean of class `OAuth2AccessTokenInterceptor` is created. Before each request, the interceptor resolves the required access token and includes it as a header. -`OAuth2AccessTokenInterceptor` uses the `AuthorizedClientServiceOAuth2AuthorizedClientManager` to get `OAuth2AuthorizedClient` that holds an `OAuth2AccessToken`. If the user has specified an OAuth2 `clientRegistrationId` using the `spring.cloud.openfeign.oauth2.clientRegistrationId` property, it will be used to retrieve the token. If the token is not retrieved or the `clientRegistrationId` has not been specified, the `serviceId` retrieved from the `url` host segment will be used. +WARNING:: The OAuth2 support as described above is now deprecated since `spring-security-oauth2-autoconfigure` has reached end of life. Please use the mode described below instead. + +==== Current OAuth2 Support + +When the `feign.client.refresh-enabled` flag is set to true, and `spring-security-oauth2-client` is present in the classpath, a bean of class `OAuth2AccessTokenInterceptor` is created. Before each request, the interceptor resolves the required access token and includes it as a header. +`OAuth2AccessTokenInterceptor` uses the `OAuth2AuthorizedClientManager` to get `OAuth2AuthorizedClient` that holds an `OAuth2AccessToken`. If the user has specified an OAuth2 `clientRegistrationId` using the `feign.oauth2.clientRegistrationId` property, it will be used to retrieve the token. If the token is not retrieved or the `clientRegistrationId` has not been specified, the `serviceId` retrieved from the `url` host segment will be used. TIP:: Using the `serviceId` as OAuth2 client registrationId is convenient for load-balanced Feign clients. For non-load-balanced ones, the property-based `clientRegistrationId` is a suitable approach. +TIP:: If you do not want to use the default setup for the `OAuth2AuthorizedClientManager`, you can just instantiate a bean of this type in your configuration. + === Transform the load-balanced HTTP request You can use the selected `ServiceInstance` to transform the load-balanced HTTP Request. diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java index 2bec63154..153331686 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java @@ -75,6 +75,7 @@ import org.springframework.context.annotation.Import; import org.springframework.data.domain.Page; import org.springframework.data.domain.Sort; +import org.springframework.security.oauth2.client.AuthorizedClientServiceOAuth2AuthorizedClientManager; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.OAuth2ClientContext; @@ -352,7 +353,7 @@ public Client feignClient(org.apache.hc.client5.http.impl.classic.CloseableHttpC @ConditionalOnClass(OAuth2ClientContext.class) @ConditionalOnProperty("feign.oauth2.enabled") @Deprecated // spring-security-oauth2 reached EOL - protected static class Oauth2FeignConfiguration { + protected static class DeprecatedOauth2FeignConfiguration { @ConditionalOnBean({ RetryLoadBalancerInterceptor.class, OAuth2ClientContext.class, OAuth2ProtectedResourceDetails.class }) @@ -374,20 +375,36 @@ public OAuth2FeignRequestInterceptorConfigurer loadBalancerInterceptorInjectingC @Bean @ConditionalOnMissingBean(OAuth2FeignRequestInterceptor.class) - @ConditionalOnBean({OAuth2ClientContext.class, OAuth2ProtectedResourceDetails.class}) + @ConditionalOnBean({ OAuth2ClientContext.class, OAuth2ProtectedResourceDetails.class }) public RequestInterceptor oauth2FeignRequestInterceptor(OAuth2ClientContext oAuth2ClientContext, - OAuth2ProtectedResourceDetails resource, List configurers) { + OAuth2ProtectedResourceDetails resource, List configurers) { return buildWithConfigurers(oAuth2ClientContext, resource, configurers); } + } + + @Configuration(proxyBeanMethods = false) + @ConditionalOnClass(OAuth2AuthorizedClientManager.class) + @ConditionalOnProperty("feign.oauth2.enabled") + protected static class Oauth2FeignConfiguration { + @Bean @ConditionalOnBean({ OAuth2AuthorizedClientService.class, ClientRegistrationRepository.class }) + @ConditionalOnMissingBean + OAuth2AuthorizedClientManager feignOAuth2AuthorizedClientManager( + ClientRegistrationRepository clientRegistrationRepository, + OAuth2AuthorizedClientService oAuth2AuthorizedClientService) { + return new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository, + oAuth2AuthorizedClientService); + + } + + @Bean + @ConditionalOnBean(OAuth2AuthorizedClientManager.class) public OAuth2AccessTokenInterceptor defaultOAuth2AccessTokenInterceptor( - @Value("${spring.cloud.openfeign.oauth2.clientRegistrationId:}") String clientRegistrationId, - OAuth2AuthorizedClientService oAuth2AuthorizedClientService, - ClientRegistrationRepository clientRegistrationRepository) { - return new OAuth2AccessTokenInterceptor(clientRegistrationId, oAuth2AuthorizedClientService, - clientRegistrationRepository); + @Value("${feign.oauth2.clientRegistrationId:}") String clientRegistrationId, + OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) { + return new OAuth2AccessTokenInterceptor(clientRegistrationId, oAuth2AuthorizedClientManager); } } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptor.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptor.java index dacd8b30c..631edbc14 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptor.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptor.java @@ -27,26 +27,23 @@ import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.oauth2.client.AuthorizedClientServiceOAuth2AuthorizedClientManager; import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.core.OAuth2AccessToken; import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** * A {@link RequestInterceptor} for OAuth2 Feign Requests. By default, it uses the - * {@link AuthorizedClientServiceOAuth2AuthorizedClientManager } to get - * {@link OAuth2AuthorizedClient } that holds an {@link OAuth2AccessToken }. If the user - * has specified an OAuth2 {@code clientId} using the - * {@code spring.cloud.openfeign.oauth2.clientId} property, it will be used to retrieve - * the token. If the token is not retrieved or the {@code clientId} has not been - * specified, the {@code serviceId} retrieved from the {@code url} host segment will be - * used. This approach is convenient for load-balanced Feign clients. For - * non-load-balanced ones, the property-based {@code clientId} is a suitable approach. + * {@link OAuth2AuthorizedClientManager } to get {@link OAuth2AuthorizedClient } that + * holds an {@link OAuth2AccessToken }. If the user has specified an OAuth2 + * {@code clientRegistrationId} using the {@code feign.oauth2.clientRegistrationId} + * property, it will be used to retrieve the token. If the token is not retrieved or the + * {@code clientRegistrationId} has not been specified, the {@code serviceId} retrieved + * from the {@code url} host segment will be used. This approach is convenient for + * load-balanced Feign clients. For non-load-balanced ones, the property-based + * {@code clientRegistrationId} is a suitable approach. * * @author Dangzhicairang(小水牛) * @author Olga Maciaszek-Sharma @@ -70,34 +67,26 @@ public class OAuth2AccessTokenInterceptor implements RequestInterceptor { private final String clientRegistrationId; - private OAuth2AuthorizedClientManager authorizedClientManager; - - public void setAuthorizedClientManager(OAuth2AuthorizedClientManager authorizedClientManager) { - this.authorizedClientManager = authorizedClientManager; - } + private final OAuth2AuthorizedClientManager authorizedClientManager; private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken("anonymous", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); - public OAuth2AccessTokenInterceptor(OAuth2AuthorizedClientService oAuth2AuthorizedClientService, - ClientRegistrationRepository clientRegistrationRepository) { - this(null, oAuth2AuthorizedClientService, clientRegistrationRepository); + public OAuth2AccessTokenInterceptor(OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) { + this(null, oAuth2AuthorizedClientManager); } public OAuth2AccessTokenInterceptor(String clientRegistrationId, - OAuth2AuthorizedClientService oAuth2AuthorizedClientService, - ClientRegistrationRepository clientRegistrationRepository) { - this(BEARER, AUTHORIZATION, clientRegistrationId, oAuth2AuthorizedClientService, clientRegistrationRepository); + OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) { + this(BEARER, AUTHORIZATION, clientRegistrationId, oAuth2AuthorizedClientManager); } public OAuth2AccessTokenInterceptor(String tokenType, String header, String clientRegistrationId, - OAuth2AuthorizedClientService oAuth2AuthorizedClientService, - ClientRegistrationRepository clientRegistrationRepository) { + OAuth2AuthorizedClientManager oAuth2AuthorizedClientManager) { this.tokenType = tokenType; this.header = header; this.clientRegistrationId = clientRegistrationId; - this.authorizedClientManager = new AuthorizedClientServiceOAuth2AuthorizedClientManager( - clientRegistrationRepository, oAuth2AuthorizedClientService); + this.authorizedClientManager = oAuth2AuthorizedClientManager; } @Override @@ -144,9 +133,9 @@ protected OAuth2AccessToken getToken(String clientRegistrationId) { private static String getServiceId(RequestTemplate template) { Target feignTarget = template.feignTarget(); - Assert.notNull(feignTarget, "feignTarget may not be null"); + Assert.notNull(feignTarget, "FeignTarget may not be null."); String url = feignTarget.url(); - Assert.hasLength(url, "url may not be empty"); + Assert.hasLength(url, "Url may not be empty."); final URI originalUri = URI.create(url); return originalUri.getHost(); } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptor.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptor.java index 2d26df89e..78d0fa7ed 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptor.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptor.java @@ -42,6 +42,7 @@ * @author Joao Pedro Evangelista * @author Tim Ysewyn * @since 3.0.0 + * @deprecated in favour of {@link OAuth2AccessTokenInterceptor} */ @Deprecated // spring-security-oauth2 reached EOL public class OAuth2FeignRequestInterceptor implements RequestInterceptor { diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptorBuilder.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptorBuilder.java index 62beba0b9..a80da25bd 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptorBuilder.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptorBuilder.java @@ -39,7 +39,9 @@ * * @author Wojciech Mąka * @since 3.1.1 + * @deprecated since spring-security-oauth2 reached EOL */ +@Deprecated // spring-security-oauth2 reached EOL public class OAuth2FeignRequestInterceptorBuilder { private AccessTokenProvider accessTokenProvider; diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptorConfigurer.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptorConfigurer.java index 74ebbcdcf..ed83952fc 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptorConfigurer.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptorConfigurer.java @@ -26,7 +26,9 @@ * * @author Wojciech Mąka * @since 3.1.1 + * @deprecated since spring-security-oauth2 reached EOL */ +@Deprecated // spring-security-oauth2 reached EOL @FunctionalInterface public interface OAuth2FeignRequestInterceptorConfigurer { diff --git a/spring-cloud-openfeign-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-openfeign-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 88c42c56c..960855b54 100644 --- a/spring-cloud-openfeign-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-openfeign-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -23,7 +23,7 @@ { "name": "feign.circuitbreaker.group.enabled", "type": "java.lang.Boolean", - "description": "If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker with with group.", + "description": "If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker with group.", "defaultValue": "false" }, { @@ -81,9 +81,9 @@ "defaultValue": "false" }, { - "name": "feign.oauth2.registrationClientId", + "name": "feign.oauth2.clientRegistrationId", "type": "java.lang.String", - "description": "Provides a clientId to be used with OAuth2.", + "description": "Provides a clientRegistrationId to be used with OAuth2.", "defaultValue": "" } ] diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignAutoConfigurationTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignAutoConfigurationTests.java index b43fba31c..e91c4c503 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignAutoConfigurationTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignAutoConfigurationTests.java @@ -127,19 +127,17 @@ void shouldInstantiateFeignOAuth2FeignRequestInterceptorWithoutLoadBalancedInter @Test void shouldInstantiateFeignOAuth2FeignRequestInterceptorWithCustomAccessTokenProviderInterceptor() { - runner.withPropertyValues("feign.oauth2.enabled=true") - .withBean(MockOAuth2ClientContext.class, "token") - .withBean(BaseOAuth2ProtectedResourceDetails.class) - .withBean(CustomOAuth2FeignRequestInterceptorConfigurer.class).run(ctx -> { - assertOauth2FeignRequestInterceptorExists(ctx); - assertAccessTokenProviderInterceptorExists(ctx, BasicAuthenticationInterceptor.class); - }); + runner.withPropertyValues("feign.oauth2.enabled=true").withBean(MockOAuth2ClientContext.class, "token") + .withBean(BaseOAuth2ProtectedResourceDetails.class) + .withBean(CustomOAuth2FeignRequestInterceptorConfigurer.class).run(ctx -> { + assertOauth2FeignRequestInterceptorExists(ctx); + assertAccessTokenProviderInterceptorExists(ctx, BasicAuthenticationInterceptor.class); + }); } @Test void shouldInstantiateFeignOAuth2FeignRequestInterceptor() { - runner.withPropertyValues("spring.cloud.openfeign.oauth2.enabled=true", - "spring.cloud.openfeign.oauth2.clientRegistrationId=feign-client") + runner.withPropertyValues("feign.oauth2.enabled=true", "feign.oauth2.clientRegistrationId=feign-client") .withBean(OAuth2AuthorizedClientService.class, () -> mock(OAuth2AuthorizedClientService.class)) .withBean(ClientRegistrationRepository.class, () -> mock(ClientRegistrationRepository.class)) .run(ctx -> { @@ -154,21 +152,18 @@ private void assertOauth2FeignRequestInterceptorExists(ConfigurableApplicationCo } private void assertAccessTokenProviderInterceptorExists(ConfigurableApplicationContext ctx, - Class clazz) { + Class clazz) { AssertableApplicationContext context = AssertableApplicationContext.get(() -> ctx); - assertThat(context).getBean(OAuth2FeignRequestInterceptor.class) - .extracting("accessTokenProvider") + assertThat(context).getBean(OAuth2FeignRequestInterceptor.class).extracting("accessTokenProvider") .extracting("interceptors").asList().first().isInstanceOf(clazz); } private void assertAccessTokenProviderInterceptorNotExists(ConfigurableApplicationContext ctx, - Class clazz) { + Class clazz) { AssertableApplicationContext context = AssertableApplicationContext.get(() -> ctx); - assertThat(context).getBean(OAuth2FeignRequestInterceptor.class) - .extracting("accessTokenProvider") - .extracting("interceptors").asList() - .filteredOn(obj -> clazz.isAssignableFrom(obj.getClass())) - .isEmpty(); + assertThat(context).getBean(OAuth2FeignRequestInterceptor.class).extracting("accessTokenProvider") + .extracting("interceptors").asList().filteredOn(obj -> clazz.isAssignableFrom(obj.getClass())) + .isEmpty(); } private void assertOauth2AccessTokenInterceptorExists(ConfigurableApplicationContext ctx) { diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptorTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptorTests.java index acedec4e0..686ebab55 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptorTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/OAuth2AccessTokenInterceptorTests.java @@ -27,9 +27,7 @@ import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest; import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager; -import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; import org.springframework.security.oauth2.client.registration.ClientRegistration; -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.OAuth2AccessToken; @@ -49,12 +47,6 @@ */ class OAuth2AccessTokenInterceptorTests { - private final ClientRegistrationRepository mockClientRegistrationRepository = mock( - ClientRegistrationRepository.class); - - private final OAuth2AuthorizedClientService mockOAuth2AuthorizedClientService = mock( - OAuth2AuthorizedClientService.class); - private final OAuth2AuthorizedClientManager mockOAuth2AuthorizedClientManager = mock( OAuth2AuthorizedClientManager.class); @@ -74,10 +66,8 @@ void setUp() { @Test void shouldThrowExceptionWhenNoTokenAcquired() { - oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientService, - mockClientRegistrationRepository); + oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientManager); when(mockOAuth2AuthorizedClientManager.authorize(any())).thenReturn(null); - oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager); assertThatExceptionOfType(IllegalStateException.class) .isThrownBy(() -> oAuth2AccessTokenInterceptor.apply(requestTemplate)) @@ -86,10 +76,10 @@ void shouldThrowExceptionWhenNoTokenAcquired() { @Test void shouldAcquireValidToken() { - oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientService, - mockClientRegistrationRepository); - when(mockOAuth2AuthorizedClientManager.authorize(any())).thenReturn(validTokenOAuth2AuthorizedClient()); - oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager); + oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientManager); + when(mockOAuth2AuthorizedClientManager.authorize( + argThat((OAuth2AuthorizeRequest request) -> ("test").equals(request.getClientRegistrationId())))) + .thenReturn(validTokenOAuth2AuthorizedClient()); oAuth2AccessTokenInterceptor.apply(requestTemplate); @@ -98,12 +88,10 @@ void shouldAcquireValidToken() { @Test void shouldAcquireValidTokenFromServiceId() { - oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientService, - mockClientRegistrationRepository); when(mockOAuth2AuthorizedClientManager.authorize( argThat((OAuth2AuthorizeRequest request) -> ("test").equals(request.getClientRegistrationId())))) .thenReturn(validTokenOAuth2AuthorizedClient()); - oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager); + oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(mockOAuth2AuthorizedClientManager); oAuth2AccessTokenInterceptor.apply(requestTemplate); @@ -111,13 +99,12 @@ void shouldAcquireValidTokenFromServiceId() { } @Test - void shouldAcquireValidTokenFromSpecifiedClientId() { + void shouldAcquireValidTokenFromSpecifiedClientRegistrationId() { oAuth2AccessTokenInterceptor = new OAuth2AccessTokenInterceptor(DEFAULT_CLIENT_REGISTRATION_ID, - mockOAuth2AuthorizedClientService, mockClientRegistrationRepository); + mockOAuth2AuthorizedClientManager); when(mockOAuth2AuthorizedClientManager .authorize(argThat((OAuth2AuthorizeRequest request) -> (DEFAULT_CLIENT_REGISTRATION_ID) .equals(request.getClientRegistrationId())))).thenReturn(validTokenOAuth2AuthorizedClient()); - oAuth2AccessTokenInterceptor.setAuthorizedClientManager(mockOAuth2AuthorizedClientManager); oAuth2AccessTokenInterceptor.apply(requestTemplate); diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index 70929ac70..f2064a01e 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -19,6 +19,7 @@ 3.8.0 2.5.2 + 5.7.3 @@ -30,6 +31,7 @@ org.springframework.security spring-security-oauth2-client + ${spring-security-oauth2-client.version} org.springframework.cloud From a44d6e1a3bc0cc5cce851cac057d32236bfc6edf Mon Sep 17 00:00:00 2001 From: buildmaster Date: Sat, 1 Oct 2022 10:56:52 +0000 Subject: [PATCH 53/62] Bumping versions --- docs/src/main/asciidoc/_configprops.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/main/asciidoc/_configprops.adoc b/docs/src/main/asciidoc/_configprops.adoc index 31a6377d0..1dcf85633 100644 --- a/docs/src/main/asciidoc/_configprops.adoc +++ b/docs/src/main/asciidoc/_configprops.adoc @@ -36,4 +36,4 @@ |feign.oauth2.load-balanced | `+++false+++` | Enables load balancing for oauth2 access token provider. |feign.okhttp.enabled | `+++false+++` | Enables the use of the OK HTTP Client by Feign. -|=== +|=== \ No newline at end of file From b3578fc454c0e3d6b1af904ca1d1000a47648b74 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Oct 2022 17:56:08 +0200 Subject: [PATCH 54/62] Bump protobuf-java from 3.19.3 to 3.19.6 in /spring-cloud-openfeign-core (#764) --- spring-cloud-openfeign-core/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 306c44747..81eb35f2d 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -205,7 +205,7 @@ com.google.protobuf protobuf-java - 3.19.3 + 3.19.6 test From 5b0f9f74aff289ee620ffedb3740f1a497fd4243 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Mon, 10 Oct 2022 14:16:40 +0200 Subject: [PATCH 55/62] Upgrade Feign to 10.11. Remove some deprecations. Refactor. (#767) --- .github/workflows/maven.yml | 6 +++--- .../cloud/openfeign/DefaultFeignLoggerFactory.java | 3 ++- .../cloud/openfeign/FeignClientBuilder.java | 3 ++- .../cloud/openfeign/FeignClientFactoryBean.java | 6 +++--- .../annotation/CookieValueParameterProcessor.java | 10 ++++++---- .../annotation/QueryMapParameterProcessor.java | 2 +- .../OAuth2FeignRequestInterceptorBuilder.java | 2 +- .../cloud/openfeign/support/FeignUtils.java | 12 ------------ .../openfeign/support/ResponseEntityDecoder.java | 2 +- .../cloud/openfeign/support/SpringEncoder.java | 4 ++-- .../cloud/openfeign/support/SpringMvcContract.java | 2 +- .../cloud/openfeign/FeignBuilderCustomizerTests.java | 10 +++++----- .../openfeign/FeignClientConfigurationTests.java | 6 ++++-- .../openfeign/FeignClientOverrideDefaultsTests.java | 12 ++++++++---- .../openfeign/FeignClientUsingPropertiesTests.java | 5 ++++- .../cloud/openfeign/OptionsTestClient.java | 10 +++++----- .../encoding/proto/ProtobufSpringEncoderTest.java | 6 +++--- .../FeignBlockingLoadBalancerClientTests.java | 6 +++--- .../openfeign/security/MockAccessTokenProvider.java | 2 +- .../openfeign/security/MockOAuth2AccessToken.java | 2 +- .../support/FeignHttpClientPropertiesTests.java | 2 +- .../cloud/openfeign/support/SpringEncoderTests.java | 4 ++-- spring-cloud-openfeign-dependencies/pom.xml | 2 +- 23 files changed, 60 insertions(+), 59 deletions(-) diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index d8affd442..0282149bd 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -5,9 +5,9 @@ name: Build on: push: - branches: [ main ] + branches: [ 3.1.x ] pull_request: - branches: [ main ] + branches: [ 3.1.x ] jobs: build: @@ -16,7 +16,7 @@ jobs: strategy: matrix: - java: ["8", "11", "16"] + java: ["8"] steps: - uses: actions/checkout@v2 diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/DefaultFeignLoggerFactory.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/DefaultFeignLoggerFactory.java index 4ea8e5c1f..41e9635c7 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/DefaultFeignLoggerFactory.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/DefaultFeignLoggerFactory.java @@ -21,10 +21,11 @@ /** * @author Venil Noronha + * @author Olga Maciaszek-Sharma */ public class DefaultFeignLoggerFactory implements FeignLoggerFactory { - private Logger logger; + private final Logger logger; public DefaultFeignLoggerFactory(Logger logger) { this.logger = logger; diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientBuilder.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientBuilder.java index 241d38878..81c53e0f4 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientBuilder.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientBuilder.java @@ -29,6 +29,7 @@ * @author Sven Döring * @author Matt King * @author Sam Kruglov + * @author Olga Maciaszek-Sharma */ public class FeignClientBuilder { @@ -54,7 +55,7 @@ public Builder forType(final Class type, final FeignClientFactoryBean */ public static final class Builder { - private FeignClientFactoryBean feignClientFactoryBean; + private final FeignClientFactoryBean feignClientFactoryBean; private Builder(final ApplicationContext applicationContext, final Class type, final String name) { this(applicationContext, new FeignClientFactoryBean(), type, name); diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java index f8b000540..b27d450bc 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java @@ -79,7 +79,7 @@ public class FeignClientFactoryBean * lifecycle race condition. ***********************************/ - private static Log LOG = LogFactory.getLog(FeignClientFactoryBean.class); + private static final Log LOG = LogFactory.getLog(FeignClientFactoryBean.class); private Class type; @@ -215,7 +215,7 @@ protected void configureUsingConfiguration(FeignContext context, Feign.Builder b builder.queryMapEncoder(queryMapEncoder); } if (decode404) { - builder.decode404(); + builder.dismiss404(); } ExceptionPropagationPolicy exceptionPropagationPolicy = getInheritedAwareOptional(context, ExceptionPropagationPolicy.class); @@ -270,7 +270,7 @@ protected void configureUsingProperties(FeignClientProperties.FeignClientConfigu if (config.getDecode404() != null) { if (config.getDecode404()) { - builder.decode404(); + builder.dismiss404(); } } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/CookieValueParameterProcessor.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/CookieValueParameterProcessor.java index ca16d2c18..31eceab7b 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/CookieValueParameterProcessor.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/CookieValueParameterProcessor.java @@ -18,7 +18,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import java.util.Arrays; +import java.util.Collections; import feign.MethodMetadata; @@ -30,8 +30,10 @@ import static feign.Util.emptyToNull; /** - * @{link CookieValue} annotation processor. + * {@link CookieValue} annotation processor. + * * @author Gong Yi + * @author Olga Maciaszek-Sharma * */ public class CookieValueParameterProcessor implements AnnotatedParameterProcessor { @@ -51,8 +53,8 @@ public boolean processArgument(AnnotatedParameterContext context, Annotation ann String name = cookie.value().trim(); checkState(emptyToNull(name) != null, "Cookie.name() was empty on parameter %s", parameterIndex); context.setParameterName(name); - String cookieExpression = data.template().headers().getOrDefault(HttpHeaders.COOKIE, Arrays.asList("")).stream() - .findFirst().orElse(""); + String cookieExpression = data.template().headers() + .getOrDefault(HttpHeaders.COOKIE, Collections.singletonList("")).stream().findFirst().orElse(""); if (cookieExpression.length() == 0) { cookieExpression = String.format("%s={%s}", name, name); } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/QueryMapParameterProcessor.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/QueryMapParameterProcessor.java index d08ea3496..eb7b747f1 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/QueryMapParameterProcessor.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/QueryMapParameterProcessor.java @@ -28,6 +28,7 @@ * {@link SpringQueryMap} parameter processor. * * @author Aram Peres + * @author Olga Maciaszek-Sharma * @see AnnotatedParameterProcessor */ public class QueryMapParameterProcessor implements AnnotatedParameterProcessor { @@ -45,7 +46,6 @@ public boolean processArgument(AnnotatedParameterContext context, Annotation ann MethodMetadata metadata = context.getMethodMetadata(); if (metadata.queryMapIndex() == null) { metadata.queryMapIndex(paramIndex); - metadata.queryMapEncoded(SpringQueryMap.class.cast(annotation).encoded()); } return true; } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptorBuilder.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptorBuilder.java index a80da25bd..6937258f1 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptorBuilder.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/security/OAuth2FeignRequestInterceptorBuilder.java @@ -44,7 +44,7 @@ @Deprecated // spring-security-oauth2 reached EOL public class OAuth2FeignRequestInterceptorBuilder { - private AccessTokenProvider accessTokenProvider; + private final AccessTokenProvider accessTokenProvider; private final List accessTokenProviderInterceptors = new ArrayList<>(); diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FeignUtils.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FeignUtils.java index c9ad4590e..e0862e7a7 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FeignUtils.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FeignUtils.java @@ -18,8 +18,6 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import org.springframework.http.HttpHeaders; @@ -44,16 +42,6 @@ static HttpHeaders getHttpHeaders(Map> headers) { return httpHeaders; } - static Map> getHeaders(HttpHeaders httpHeaders) { - LinkedHashMap> headers = new LinkedHashMap<>(); - - for (Map.Entry> entry : httpHeaders.entrySet()) { - headers.put(entry.getKey(), entry.getValue()); - } - - return headers; - } - static Collection addTemplateParameter(Collection possiblyNull, String paramName) { Collection params = ofNullable(possiblyNull).map(ArrayList::new).orElse(new ArrayList<>()); params.add(String.format("{%s}", paramName)); diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/ResponseEntityDecoder.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/ResponseEntityDecoder.java index c69d2ac7a..d5e1f8eb6 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/ResponseEntityDecoder.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/ResponseEntityDecoder.java @@ -39,7 +39,7 @@ */ public class ResponseEntityDecoder implements Decoder { - private Decoder decoder; + private final Decoder decoder; public ResponseEntityDecoder(Decoder decoder) { this.decoder = decoder; diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java index f68933d59..fb840845d 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java @@ -24,6 +24,7 @@ import java.nio.charset.StandardCharsets; import java.util.Arrays; import java.util.Collection; +import java.util.LinkedHashMap; import java.util.List; import java.util.Objects; import java.util.stream.Stream; @@ -49,7 +50,6 @@ import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter; import org.springframework.web.multipart.MultipartFile; -import static org.springframework.cloud.openfeign.support.FeignUtils.getHeaders; import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHeaders; import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED; import static org.springframework.http.MediaType.MULTIPART_FORM_DATA; @@ -158,7 +158,7 @@ private void encodeWithMessageConverter(Object requestBody, Type bodyType, Reque request.headers(null); // converters can modify headers, so update the request // with the modified headers - request.headers(getHeaders(outputMessage.getHeaders())); + request.headers(new LinkedHashMap<>(outputMessage.getHeaders())); // do not use charset for binary data and protobuf Charset charset; diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java index 48d99edd2..98e52e56f 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java @@ -108,7 +108,7 @@ public class SpringMvcContract extends Contract.BaseContract implements Resource private ResourceLoader resourceLoader = new DefaultResourceLoader(); - private boolean decodeSlash; + private final boolean decodeSlash; public SpringMvcContract() { this(Collections.emptyList()); diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignBuilderCustomizerTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignBuilderCustomizerTests.java index 06d2f2599..d6ed73e6f 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignBuilderCustomizerTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignBuilderCustomizerTests.java @@ -68,7 +68,7 @@ void testBuilderCustomizer() { Assertions.assertNotNull(feignBuilderCaptor.getValue()); Feign.Builder builder = feignBuilderCaptor.getValue(); assertFeignBuilderField(builder, "logLevel", Logger.Level.HEADERS); - assertFeignBuilderField(builder, "decode404", true); + assertFeignBuilderField(builder, "dismiss404", true); context.close(); } @@ -95,7 +95,7 @@ void testBuildCustomizerOrdered() { Assertions.assertNotNull(feignBuilderCaptor.getValue()); Feign.Builder builder = feignBuilderCaptor.getValue(); assertFeignBuilderField(builder, "logLevel", Logger.Level.FULL); - assertFeignBuilderField(builder, "decode404", true); + assertFeignBuilderField(builder, "dismiss404", true); context.close(); } @@ -116,7 +116,7 @@ void testBuildCustomizerOrderedWithAdditional() { Assertions.assertNotNull(feignBuilderCaptor.getValue()); Feign.Builder builder = feignBuilderCaptor.getValue(); assertFeignBuilderField(builder, "logLevel", Logger.Level.BASIC); - assertFeignBuilderField(builder, "decode404", true); + assertFeignBuilderField(builder, "dismiss404", true); assertFeignBuilderField(builder, "closeAfterDecode", false); context.close(); @@ -178,7 +178,7 @@ FeignBuilderCustomizer feignBuilderCustomizer() { @Bean FeignBuilderCustomizer feignBuilderCustomizer2() { - return Feign.Builder::decode404; + return Feign.Builder::dismiss404; } @Bean @@ -221,7 +221,7 @@ FeignBuilderCustomizer feignBuilderCustomizer1() { @Bean FeignBuilderCustomizer feignBuilderCustomizer2() { - return Feign.Builder::decode404; + return Feign.Builder::dismiss404; } @Bean diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientConfigurationTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientConfigurationTests.java index 5d9255ce8..a250ec7e5 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientConfigurationTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientConfigurationTests.java @@ -17,6 +17,7 @@ package org.springframework.cloud.openfeign; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; @@ -45,6 +46,7 @@ /** * @author Jonatan Ivanov * @author Hyeonmin Park + * @author Olga Maciaszek-Sharma */ class FeignClientConfigurationTests { @@ -80,9 +82,9 @@ void shouldReturnValuesWhenSet() { config.setErrorDecoder(ErrorDecoder.class); List> requestInterceptors = Lists.list(RequestInterceptor.class); config.setRequestInterceptors(requestInterceptors); - Map> defaultRequestHeaders = Maps.newHashMap("default", Lists.emptyList()); + Map> defaultRequestHeaders = Maps.newHashMap("default", Collections.emptyList()); config.setDefaultRequestHeaders(defaultRequestHeaders); - Map> defaultQueryParameters = Maps.newHashMap("default", Lists.emptyList()); + Map> defaultQueryParameters = Maps.newHashMap("default", Collections.emptyList()); config.setDefaultQueryParameters(defaultQueryParameters); config.setDecode404(true); config.setDecoder(Decoder.class); diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientOverrideDefaultsTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientOverrideDefaultsTests.java index 0679c2114..319f39811 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientOverrideDefaultsTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientOverrideDefaultsTests.java @@ -35,6 +35,7 @@ import feign.micrometer.MicrometerCapability; import feign.optionals.OptionalDecoder; import feign.querymap.BeanQueryMapEncoder; +import feign.querymap.FieldQueryMapEncoder; import feign.slf4j.Slf4jLogger; import org.junit.jupiter.api.Test; @@ -49,6 +50,7 @@ import org.springframework.web.bind.annotation.GetMapping; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; /** * @author Spencer Gibb @@ -127,8 +129,10 @@ void overrideRequestOptions() { @Test void overrideQueryMapEncoder() { - QueryMapEncoder.Default.class.cast(context.getInstance("foo", QueryMapEncoder.class)); - BeanQueryMapEncoder.class.cast(context.getInstance("bar", QueryMapEncoder.class)); + assertThatCode(() -> { + FieldQueryMapEncoder.class.cast(context.getInstance("foo", QueryMapEncoder.class)); + BeanQueryMapEncoder.class.cast(context.getInstance("bar", QueryMapEncoder.class)); + }).doesNotThrowAnyException(); } @Test @@ -208,7 +212,7 @@ public Encoder feignEncoder() { @Bean public Logger feignLogger() { - return new Logger.JavaLogger(); + return new Logger.JavaLogger(FooConfiguration.class); } @Bean @@ -218,7 +222,7 @@ public Contract feignContract() { @Bean public QueryMapEncoder queryMapEncoder() { - return new feign.QueryMapEncoder.Default(); + return new FieldQueryMapEncoder(); } } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientUsingPropertiesTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientUsingPropertiesTests.java index 54c5baddc..dea7f7e05 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientUsingPropertiesTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientUsingPropertiesTests.java @@ -420,7 +420,10 @@ public void encode(Object o, Type type, RequestTemplate requestTemplate) throws Map form = (Map) o; StringBuilder builder = new StringBuilder(); form.forEach((key, value) -> { - builder.append(key + "=" + value + "&"); + builder.append(key); + builder.append("="); + builder.append(value); + builder.append("&"); }); requestTemplate.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE); diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/OptionsTestClient.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/OptionsTestClient.java index 075f85012..77ccc085e 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/OptionsTestClient.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/OptionsTestClient.java @@ -36,7 +36,7 @@ */ public class OptionsTestClient implements Client { - private static ObjectMapper mapper; + private final static ObjectMapper mapper; static { mapper = new ObjectMapper(); @@ -69,13 +69,13 @@ private byte[] prepareResponse(Request.Options options) { static class OptionsResponseForTests { - private long connectTimeout; + private final long connectTimeout; - private TimeUnit connectTimeoutUnit; + private final TimeUnit connectTimeoutUnit; - private long readTimeout; + private final long readTimeout; - private TimeUnit readTimeoutUnit; + private final TimeUnit readTimeoutUnit; OptionsResponseForTests(long connectTimeout, TimeUnit connectTimeoutUnit, long readTimeout, TimeUnit readTimeoutUnit) { diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/ProtobufSpringEncoderTest.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/ProtobufSpringEncoderTest.java index 1355ae6f1..053fd0299 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/ProtobufSpringEncoderTest.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/ProtobufSpringEncoderTest.java @@ -47,7 +47,6 @@ import org.springframework.cloud.openfeign.support.SpringEncoder; import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter; -import static feign.Request.Body.encoded; import static feign.Request.HttpMethod.POST; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; @@ -56,6 +55,7 @@ * Test {@link SpringEncoder} with {@link ProtobufHttpMessageConverter} * * @author ScienJus + * @author Olga Maciaszek-Sharma */ @ExtendWith(MockitoExtension.class) class ProtobufSpringEncoderTest { @@ -64,7 +64,7 @@ class ProtobufSpringEncoderTest { private HttpClient httpClient; // a protobuf object with some content - private org.springframework.cloud.openfeign.encoding.proto.Request request = org.springframework.cloud.openfeign.encoding.proto.Request + private final org.springframework.cloud.openfeign.encoding.proto.Request request = org.springframework.cloud.openfeign.encoding.proto.Request .newBuilder().setId(1000000) .setMsg("Erlang/OTP 最初是爱立信为开发电信设备系统设计的编程语言平台," + "电信设备(路由器、接入网关、…)典型设计是通过背板连接主控板卡与多块业务板卡的分布式系统。").build(); @@ -88,7 +88,7 @@ void testProtobufWithCharsetWillFail() throws IOException { RequestTemplate requestTemplate = newRequestTemplate(); newEncoder().encode(this.request, Request.class, requestTemplate); // set a charset - requestTemplate.body(encoded(requestTemplate.requestBody().asBytes(), StandardCharsets.UTF_8)); + requestTemplate.body(requestTemplate.body(), StandardCharsets.UTF_8); HttpEntity entity = toApacheHttpEntity(requestTemplate); byte[] bytes = read(entity.getContent(), (int) entity.getContentLength()); diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/loadbalancer/FeignBlockingLoadBalancerClientTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/loadbalancer/FeignBlockingLoadBalancerClientTests.java index 755d3b395..937c54cb2 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/loadbalancer/FeignBlockingLoadBalancerClientTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/loadbalancer/FeignBlockingLoadBalancerClientTests.java @@ -177,12 +177,12 @@ void shouldExecuteLoadBalancerLifecycleCallbacks() throws IOException { private String read(Response response) throws IOException { BufferedReader reader = new BufferedReader( new InputStreamReader(response.body().asInputStream(), StandardCharsets.UTF_8)); - String outputString = ""; + StringBuilder outputString = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { - outputString += line; + outputString.append(line); } - return outputString; + return outputString.toString(); } private Request testRequest() { diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/MockAccessTokenProvider.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/MockAccessTokenProvider.java index d70eb6ba7..0f7ca9a04 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/MockAccessTokenProvider.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/MockAccessTokenProvider.java @@ -32,7 +32,7 @@ */ public class MockAccessTokenProvider implements AccessTokenProvider { - private OAuth2AccessToken token; + private final OAuth2AccessToken token; public MockAccessTokenProvider(OAuth2AccessToken token) { this.token = token; diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/MockOAuth2AccessToken.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/MockOAuth2AccessToken.java index 2031441d0..937c8432a 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/MockOAuth2AccessToken.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/security/MockOAuth2AccessToken.java @@ -30,7 +30,7 @@ */ public class MockOAuth2AccessToken implements OAuth2AccessToken { - private String value; + private final String value; public MockOAuth2AccessToken(String value) { this.value = value; diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/FeignHttpClientPropertiesTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/FeignHttpClientPropertiesTests.java index 09996864f..cbff0103b 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/FeignHttpClientPropertiesTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/FeignHttpClientPropertiesTests.java @@ -42,7 +42,7 @@ @DirtiesContext class FeignHttpClientPropertiesTests { - private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); @AfterEach void clear() { diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringEncoderTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringEncoderTests.java index e24b97444..c19f5e9b8 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringEncoderTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringEncoderTests.java @@ -179,7 +179,7 @@ void testMultipartFile2() { .isEqualTo(MULTIPART_FORM_DATA_VALUE); assertThat(((List) request.headers().get(CONTENT_LENGTH)).get(0)) .as("Request Content-Length is not equal to 186").isEqualTo("186"); - assertThat(new String(request.requestBody().asBytes())).as("Body content cannot be decoded").contains("hi"); + assertThat(new String(request.body())).as("Body content cannot be decoded").contains("hi"); } @Test @@ -190,7 +190,7 @@ void testFromURLEncodedValue() { request.header(CONTENT_TYPE, APPLICATION_FORM_URLENCODED_VALUE); String body = "test"; encoder.encode(body, String.class, request); - assertThat(new String(request.requestBody().asBytes())).as("Body content cannot be decoded").contains(body); + assertThat(new String(request.body())).as("Body content cannot be decoded").contains(body); } @Test diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index f2064a01e..fdf6e847f 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -15,7 +15,7 @@ spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies - 11.8 + 11.10 3.8.0 2.5.2 From 561acf6276c86f0d8d3b38a82048b855d00858a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=9B=90=EC=8B=9D?= <30713548+WonSik36@users.noreply.github.com> Date: Wed, 19 Oct 2022 00:11:03 +0900 Subject: [PATCH 56/62] Disable loadbalancer clients retry when clients retry is disabled (fixed) (#773) --- ...RetryableFeignBlockingLoadBalancerClient.java | 7 +++++-- ...ableFeignBlockingLoadBalancerClientTests.java | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClient.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClient.java index c38a75324..2747b29ab 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClient.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClient.java @@ -65,6 +65,7 @@ * load-balanced with Spring Cloud LoadBalancer. * * @author Olga Maciaszek-Sharma + * @author Wonsik Cheung * @since 2.2.6 */ @SuppressWarnings({ "rawtypes", "unchecked" }) @@ -201,8 +202,10 @@ private RetryTemplate buildRetryTemplate(String serviceId, Request request, Load retryTemplate.setListeners(retryListeners); } - retryTemplate.setRetryPolicy(retryPolicy == null ? new NeverRetryPolicy() - : new InterceptorRetryPolicy(toHttpRequest(request), retryPolicy, loadBalancerClient, serviceId)); + retryTemplate.setRetryPolicy( + !loadBalancerClientFactory.getProperties(serviceId).getRetry().isEnabled() || retryPolicy == null + ? new NeverRetryPolicy() : new InterceptorRetryPolicy(toHttpRequest(request), retryPolicy, + loadBalancerClient, serviceId)); return retryTemplate; } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClientTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClientTests.java index f7bd3ab57..843107454 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClientTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClientTests.java @@ -55,6 +55,7 @@ import org.springframework.http.MediaType; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; @@ -72,6 +73,7 @@ * @see BlockingLoadBalancerClientTests * @author Olga Maciaszek-Sharma + * @author Wonsik Cheung */ @ExtendWith(MockitoExtension.class) class RetryableFeignBlockingLoadBalancerClientTests { @@ -160,6 +162,20 @@ void shouldRetryOnRepeatableStatusCode() throws IOException { verify(delegate, times(2)).execute(any(), any()); } + @Test + void shouldNotRetryOnDisabled() throws IOException { + properties.getRetry().setEnabled(false); + Request request = testRequest(); + when(delegate.execute(any(), any())).thenThrow(new IOException()); + when(retryFactory.createRetryPolicy(any(), eq(loadBalancerClient))) + .thenReturn(new BlockingLoadBalancedRetryPolicy(properties)); + + assertThatThrownBy(() -> feignBlockingLoadBalancerClient.execute(request, new Request.Options())) + .isInstanceOf(IOException.class); + + verify(delegate, times(1)).execute(any(), any()); + } + @Test void shouldExposeResponseBodyOnRetry() throws IOException { properties.getRetry().getRetryableStatusCodes().add(503); From c862311811c750bd5375506c7187af3501400be9 Mon Sep 17 00:00:00 2001 From: vicasong Date: Wed, 19 Oct 2022 18:22:30 +0800 Subject: [PATCH 57/62] Release RequestAttributes when called asynchronously. Fixes gh-716 (#775) --- .../FeignCircuitBreakerInvocationHandler.java | 19 +++++++++++- .../AsyncCircuitBreakerTest.java | 30 +++++++++++++++++-- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java index a85cf4713..77ba428e7 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java @@ -36,6 +36,14 @@ import static feign.Util.checkNotNull; +/** + * @author Grzejszczak + * @author Sharma + * @author Niang + * @author Bohutskyi + * @author kim + * @author Vicasong + */ class FeignCircuitBreakerInvocationHandler implements InvocationHandler { private final CircuitBreakerFactory factory; @@ -122,9 +130,13 @@ private void unwrapAndRethrow(Exception exception) { private Supplier asSupplier(final Method method, final Object[] args) { final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); + final Thread caller = Thread.currentThread(); return () -> { + boolean isAsync = caller != Thread.currentThread(); try { - RequestContextHolder.setRequestAttributes(requestAttributes); + if (isAsync) { + RequestContextHolder.setRequestAttributes(requestAttributes); + } return dispatch.get(method).invoke(args); } catch (RuntimeException throwable) { @@ -133,6 +145,11 @@ private Supplier asSupplier(final Method method, final Object[] args) { catch (Throwable throwable) { throw new RuntimeException(throwable); } + finally { + if (isAsync) { + RequestContextHolder.resetRequestAttributes(); + } + } }; } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/AsyncCircuitBreakerTest.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/AsyncCircuitBreakerTest.java index 863381a9e..369802177 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/AsyncCircuitBreakerTest.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/AsyncCircuitBreakerTest.java @@ -19,6 +19,9 @@ import java.time.Duration; import java.util.Objects; import java.util.UUID; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.function.Function; import javax.servlet.http.HttpServletRequest; @@ -28,6 +31,7 @@ import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; @@ -41,6 +45,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.http.HttpHeaders; +import org.springframework.scheduling.concurrent.CustomizableThreadFactory; import org.springframework.test.web.servlet.MockMvc; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -48,6 +53,7 @@ import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; +import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; @@ -67,6 +73,10 @@ class AsyncCircuitBreakerTest { @Autowired MockMvc mvc; + @Autowired + @Qualifier("asyncWorker") + ExecutorService asyncCircuitBreakerExecutor; + @Test void shouldWorkNormally() throws Exception { mvc.perform(get("/hello/proxy")).andDo(print()).andExpect(status().isOk()) @@ -86,14 +96,30 @@ void shouldProxyHeaderWhenHeaderSet() throws Exception { authorization)).andDo(print()).andExpect(status().isOk()).andExpect(content().string(authorization)); } + @Test + void shouldProxyHeaderWhenHeaderSetAndCleanRequestAttributesAfterReturn() throws Exception { + shouldNotProxyAnyHeadersWithoutHeaderSet(); + Future future = asyncCircuitBreakerExecutor.submit(() -> (ServletRequestAttributes) + RequestContextHolder.getRequestAttributes()); + assertThat(future.get()) + .as("the RequestAttributes has been cleared") + .isNull(); + } + @EnableAutoConfiguration @Configuration(proxyBeanMethods = false) @EnableFeignClients(clients = { TestClient.class }) @Import({ NoSecurityConfiguration.class, TestController.class }) static class Application { + @Bean(name = "asyncWorker", destroyMethod = "shutdown") + ExecutorService asyncCircuitBreakerExecutor() { + return Executors.newSingleThreadExecutor(new CustomizableThreadFactory("async")); + } + @Bean - CircuitBreakerFactory> circuitBreakerFactory() { + CircuitBreakerFactory> circuitBreakerFactory( + @Qualifier("asyncWorker") ExecutorService asyncCircuitBreakerExecutor) { return new CircuitBreakerFactory>() { Function defaultConfiguration = id -> Duration.ofMillis(1000); @@ -101,7 +127,7 @@ CircuitBreakerFactory> circuitBreakerFactory() @Override public CircuitBreaker create(String id) { Duration timeout = super.getConfigurations().computeIfAbsent(id, defaultConfiguration); - return new AsyncCircuitBreaker(timeout); + return new AsyncCircuitBreaker(timeout, asyncCircuitBreakerExecutor); } @Override From 172fcbbedf671252ddeb80707f57ebfcecd6b827 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Wed, 19 Oct 2022 12:26:07 +0200 Subject: [PATCH 58/62] Fix test class name. --- ...akerTest.java => AsyncCircuitBreakerTests.java} | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) rename spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/{AsyncCircuitBreakerTest.java => AsyncCircuitBreakerTests.java} (94%) diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/AsyncCircuitBreakerTest.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/AsyncCircuitBreakerTests.java similarity index 94% rename from spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/AsyncCircuitBreakerTest.java rename to spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/AsyncCircuitBreakerTests.java index 369802177..3c7a7cb6d 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/AsyncCircuitBreakerTest.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/AsyncCircuitBreakerTests.java @@ -65,10 +65,10 @@ * * @author John Niang */ -@SpringBootTest(classes = AsyncCircuitBreakerTest.Application.class, webEnvironment = RANDOM_PORT, +@SpringBootTest(classes = AsyncCircuitBreakerTests.Application.class, webEnvironment = RANDOM_PORT, properties = "feign.circuitbreaker.enabled=true") @AutoConfigureMockMvc -class AsyncCircuitBreakerTest { +class AsyncCircuitBreakerTests { @Autowired MockMvc mvc; @@ -99,11 +99,9 @@ void shouldProxyHeaderWhenHeaderSet() throws Exception { @Test void shouldProxyHeaderWhenHeaderSetAndCleanRequestAttributesAfterReturn() throws Exception { shouldNotProxyAnyHeadersWithoutHeaderSet(); - Future future = asyncCircuitBreakerExecutor.submit(() -> (ServletRequestAttributes) - RequestContextHolder.getRequestAttributes()); - assertThat(future.get()) - .as("the RequestAttributes has been cleared") - .isNull(); + Future future = asyncCircuitBreakerExecutor + .submit(() -> (ServletRequestAttributes) RequestContextHolder.getRequestAttributes()); + assertThat(future.get()).as("the RequestAttributes has been cleared").isNull(); } @EnableAutoConfiguration @@ -119,7 +117,7 @@ ExecutorService asyncCircuitBreakerExecutor() { @Bean CircuitBreakerFactory> circuitBreakerFactory( - @Qualifier("asyncWorker") ExecutorService asyncCircuitBreakerExecutor) { + @Qualifier("asyncWorker") ExecutorService asyncCircuitBreakerExecutor) { return new CircuitBreakerFactory>() { Function defaultConfiguration = id -> Duration.ofMillis(1000); From ed40511b19367e2891c7c405ee537cbd4699d30e Mon Sep 17 00:00:00 2001 From: pandaapo <35672972+pandaapo@users.noreply.github.com> Date: Wed, 19 Oct 2022 18:38:02 +0800 Subject: [PATCH 59/62] Handle byte[] arrays correctly.(Fixes gh-741) (#774) --- .../openfeign/support/AbstractFormWriter.java | 14 ++++- .../support/AbstractFormWriterTests.java | 62 +++++++++++++++++++ 2 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/AbstractFormWriterTests.java diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/AbstractFormWriter.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/AbstractFormWriter.java index 2fc3395f3..de8a994ae 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/AbstractFormWriter.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/AbstractFormWriter.java @@ -17,6 +17,7 @@ package org.springframework.cloud.openfeign.support; import java.io.IOException; +import java.lang.reflect.Array; import java.util.Iterator; import java.util.function.Predicate; @@ -32,6 +33,7 @@ /** * @author Darren Foong + * @author Wu Daifu */ public abstract class AbstractFormWriter extends AbstractWriter { @@ -61,10 +63,16 @@ public void write(Output output, String key, Object object) throws EncodeExcepti protected abstract String writeAsString(Object object) throws IOException; private boolean isTypeOrCollection(Object object, Predicate isType) { + if (object == null) { + return false; + } if (object.getClass().isArray()) { - Object[] array = (Object[]) object; - - return array.length > 1 && isType.test(array[0]); + int len = Array.getLength(object); + if (len > 0) { + Object one = Array.get(object, 0); + return len > 1 && one != null && isType.test(one); + } + return false; } else if (object instanceof Iterable) { Iterable iterable = (Iterable) object; diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/AbstractFormWriterTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/AbstractFormWriterTests.java new file mode 100644 index 000000000..600dd6bd7 --- /dev/null +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/AbstractFormWriterTests.java @@ -0,0 +1,62 @@ +/* + * Copyright 2013-2022 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.openfeign.support; + +import java.io.IOException; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import org.springframework.http.MediaType; + +/** + * @author Wu Daifu + */ +class AbstractFormWriterTests { + + @Test + void shouldCorrectlyResolveIfApplicableForCollection() { + MockFormWriter formWriter = new MockFormWriter(); + Object object = new Object(); + Assertions.assertFalse(formWriter.isApplicable(object)); + object = new Object[]{new Object(), new Object()}; + Assertions.assertFalse(formWriter.isApplicable(object)); + object = new UserPojo(); + Assertions.assertTrue(formWriter.isApplicable(object)); + object = new UserPojo[] {new UserPojo(), new UserPojo()}; + Assertions.assertTrue(formWriter.isApplicable(object)); + object = new byte[] {'1', '2'}; + Assertions.assertFalse(formWriter.isApplicable(object)); + } + + class MockFormWriter extends AbstractFormWriter { + + @Override + protected MediaType getContentType() { + return null; + } + + @Override + protected String writeAsString(Object object) throws IOException { + return null; + } + } + + class UserPojo { + } +} + From 9b97262f1fa489aae3b2e60475cb6eee0b604111 Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Wed, 19 Oct 2022 12:40:31 +0200 Subject: [PATCH 60/62] Update javadoc. Refactor. --- .../openfeign/FeignCircuitBreakerInvocationHandler.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java index 77ba428e7..5c27e6c1e 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java @@ -37,8 +37,8 @@ import static feign.Util.checkNotNull; /** - * @author Grzejszczak - * @author Sharma + * @author Marcin Grzejszczak + * @author Olga Maciaszek-Sharma * @author Niang * @author Bohutskyi * @author kim @@ -163,7 +163,7 @@ private Supplier asSupplier(final Method method, final Object[] args) { * @return cached methods map for fallback invoking */ static Map toFallbackMethod(Map dispatch) { - Map result = new LinkedHashMap(); + Map result = new LinkedHashMap<>(); for (Method method : dispatch.keySet()) { method.setAccessible(true); result.put(method, method); From 9328a21d6433ff505e744cf6bb64c664ec55bbcf Mon Sep 17 00:00:00 2001 From: Olga Maciaszek-Sharma Date: Wed, 19 Oct 2022 12:42:00 +0200 Subject: [PATCH 61/62] Refactor. --- .../openfeign/support/AbstractFormWriterTests.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/AbstractFormWriterTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/AbstractFormWriterTests.java index 600dd6bd7..74937bf37 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/AbstractFormWriterTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/AbstractFormWriterTests.java @@ -33,13 +33,13 @@ void shouldCorrectlyResolveIfApplicableForCollection() { MockFormWriter formWriter = new MockFormWriter(); Object object = new Object(); Assertions.assertFalse(formWriter.isApplicable(object)); - object = new Object[]{new Object(), new Object()}; + object = new Object[] { new Object(), new Object() }; Assertions.assertFalse(formWriter.isApplicable(object)); object = new UserPojo(); Assertions.assertTrue(formWriter.isApplicable(object)); - object = new UserPojo[] {new UserPojo(), new UserPojo()}; + object = new UserPojo[] { new UserPojo(), new UserPojo() }; Assertions.assertTrue(formWriter.isApplicable(object)); - object = new byte[] {'1', '2'}; + object = new byte[] { '1', '2' }; Assertions.assertFalse(formWriter.isApplicable(object)); } @@ -54,9 +54,11 @@ protected MediaType getContentType() { protected String writeAsString(Object object) throws IOException { return null; } + } class UserPojo { + } -} +} From cce0de9e898318b7edabab902afda5a45f2ecb41 Mon Sep 17 00:00:00 2001 From: buildmaster Date: Thu, 3 Nov 2022 15:26:47 +0000 Subject: [PATCH 62/62] Update SNAPSHOT to 3.1.5 --- docs/pom.xml | 2 +- pom.xml | 6 +++--- spring-cloud-openfeign-core/pom.xml | 2 +- spring-cloud-openfeign-dependencies/pom.xml | 4 ++-- spring-cloud-starter-openfeign/pom.xml | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/pom.xml b/docs/pom.xml index 79cabddb7..0da8bb821 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.5-SNAPSHOT + 3.1.5 spring-cloud-openfeign-docs jar diff --git a/pom.xml b/pom.xml index ed1a3b8cb..6fb9ed1f9 100644 --- a/pom.xml +++ b/pom.xml @@ -4,14 +4,14 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 spring-cloud-openfeign - 3.1.5-SNAPSHOT + 3.1.5 pom Spring Cloud OpenFeign Spring Cloud OpenFeign org.springframework.cloud spring-cloud-build - 3.1.5-SNAPSHOT + 3.1.5 @@ -26,7 +26,7 @@ ${basedir} 2.11.3 - 3.1.5-SNAPSHOT + 3.1.5 2.10 diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 81eb35f2d..fa1add9c6 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -6,7 +6,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.5-SNAPSHOT + 3.1.5 .. spring-cloud-openfeign-core diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index fdf6e847f..a2ea2cd1f 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -6,11 +6,11 @@ spring-cloud-dependencies-parent org.springframework.cloud - 3.1.5-SNAPSHOT + 3.1.5 spring-cloud-openfeign-dependencies - 3.1.5-SNAPSHOT + 3.1.5 pom spring-cloud-openfeign-dependencies Spring Cloud OpenFeign Dependencies diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index 2a7bf0dbc..ee0a84e04 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -5,7 +5,7 @@ org.springframework.cloud spring-cloud-openfeign - 3.1.5-SNAPSHOT + 3.1.5 .. spring-cloud-starter-openfeign