Compare commits
56
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
11e9c5dab0 | ||
|
|
9a8dae2cba | ||
|
|
a95f2a23d2 | ||
|
|
bdcbaa7f3b | ||
|
|
375241803d | ||
|
|
0213d4be21 | ||
|
|
73cde28fcf | ||
|
|
efbf71294d | ||
|
|
b4c9567de5 | ||
|
|
9529e43c01 | ||
|
|
a26095bb6c | ||
|
|
8fdb488697 | ||
|
|
8989036e93 | ||
|
|
a29f28506f | ||
|
|
926f1151c1 | ||
|
|
1e70aec75c | ||
|
|
7e01355317 | ||
|
|
fc459044b5 | ||
|
|
e321e72e3d | ||
|
|
ae63aae6eb | ||
|
|
10fefea29b | ||
|
|
24d9b08d5c | ||
|
|
d4832788f6 | ||
|
|
ff96850ec0 | ||
|
|
9023cc2dc5 | ||
|
|
6c3b8c257e | ||
|
|
f8b3ed971e | ||
|
|
bc7d39fa04 | ||
|
|
4cba1de153 | ||
|
|
1bed516e03 | ||
|
|
a142f6f2c0 | ||
|
|
434ce22328 | ||
|
|
9f160e7688 | ||
|
|
a3f270d3b4 | ||
|
|
1ff643f4c1 | ||
|
|
6704982bcd | ||
|
|
909da0afdf | ||
|
|
4cd0c363be | ||
|
|
86a2f28a9e | ||
|
|
573d933b3d | ||
|
|
d76869f214 | ||
|
|
12efc629a3 | ||
|
|
965c168c2c | ||
|
|
f2386e365d | ||
|
|
765431dce1 | ||
|
|
75fc0f6b2e | ||
|
|
f662c0547d | ||
|
|
fe43b6c52b | ||
|
|
aa4b8a280d | ||
|
|
973c048be5 | ||
|
|
5a19a0a128 | ||
|
|
e99dac2af1 | ||
|
|
83b6f043dd | ||
|
|
813ff7c8b0 | ||
|
|
b8519c01a0 | ||
|
|
d626518dc5 |
+2
-1
@@ -40,7 +40,8 @@ and binding to the Spring Environment and other Spring programming model idioms.
|
|||||||
|
|
||||||
== Building
|
== Building
|
||||||
|
|
||||||
:jdkversion: 1.8
|
|
||||||
|
:jdkversion: 17
|
||||||
|
|
||||||
=== Basic Compile and Test
|
=== Basic Compile and Test
|
||||||
|
|
||||||
|
|||||||
+3
-1
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-openfeign</artifactId>
|
<artifactId>spring-cloud-openfeign</artifactId>
|
||||||
<version>3.1.1-SNAPSHOT</version>
|
<version>3.1.5-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>spring-cloud-openfeign-docs</artifactId>
|
<artifactId>spring-cloud-openfeign-docs</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
@@ -17,6 +17,8 @@
|
|||||||
<main.basedir>${basedir}/..</main.basedir>
|
<main.basedir>${basedir}/..</main.basedir>
|
||||||
<configprops.inclusionPattern>feign.*</configprops.inclusionPattern>
|
<configprops.inclusionPattern>feign.*</configprops.inclusionPattern>
|
||||||
<upload-docs-zip.phase>deploy</upload-docs-zip.phase>
|
<upload-docs-zip.phase>deploy</upload-docs-zip.phase>
|
||||||
|
<!-- Don't upload docs jar to central / repo.spring.io -->
|
||||||
|
<maven-deploy-plugin-default.phase>none</maven-deploy-plugin-default.phase>
|
||||||
</properties>
|
</properties>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|||||||
@@ -1,36 +1,38 @@
|
|||||||
|===
|
|===
|
||||||
|Name | Default | Description
|
|Name | Default | Description
|
||||||
|
|
||||||
|feign.autoconfiguration.jackson.enabled | `false` | If true, PageJacksonModule and SortJacksonModule bean will be provided for Jackson page decoding.
|
|feign.autoconfiguration.jackson.enabled | `+++false+++` | If true, PageJacksonModule and SortJacksonModule bean will be provided for Jackson page decoding.
|
||||||
|feign.circuitbreaker.enabled | `false` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker.
|
|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.group.enabled | `false` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker with with group.
|
|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.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.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-config | `+++default+++` |
|
||||||
|feign.client.default-to-properties | `true` |
|
|feign.client.default-to-properties | `+++true+++` |
|
||||||
|feign.client.refresh-enabled | `false` | Enables options value refresh capability for Feign.
|
|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.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.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.request.min-request-size | `+++2048+++` | The minimum threshold content size.
|
||||||
|feign.compression.response.enabled | `false` | Enables the response from Feign to be compressed.
|
|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.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-timeout | `+++2000+++` |
|
||||||
|feign.httpclient.connection-timer-repeat | `3000` |
|
|feign.httpclient.connection-timer-repeat | `+++3000+++` |
|
||||||
|feign.httpclient.disable-ssl-validation | `false` |
|
|feign.httpclient.disable-ssl-validation | `+++false+++` |
|
||||||
|feign.httpclient.enabled | `true` | Enables the use of the Apache HTTP Client by Feign.
|
|feign.httpclient.enabled | `+++true+++` | Enables the use of the Apache HTTP Client by Feign.
|
||||||
|feign.httpclient.follow-redirects | `true` |
|
|feign.httpclient.follow-redirects | `+++true+++` |
|
||||||
|feign.httpclient.hc5.enabled | `false` | Enables the use of the Apache HTTP Client 5 by Feign.
|
|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-concurrency-policy | | Pool concurrency policies.
|
||||||
|feign.httpclient.hc5.pool-reuse-policy | | Pool connection re-use 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.hc5.socket-timeout-unit | | Default value for socket timeout unit.
|
||||||
|feign.httpclient.max-connections | `200` |
|
|feign.httpclient.max-connections | `+++200+++` |
|
||||||
|feign.httpclient.max-connections-per-route | `50` |
|
|feign.httpclient.max-connections-per-route | `+++50+++` |
|
||||||
|feign.httpclient.time-to-live | `900` |
|
|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.httpclient.time-to-live-unit | |
|
||||||
|feign.metrics.enabled | `true` | Enables metrics capability for Feign.
|
|feign.metrics.enabled | `+++true+++` | Enables metrics capability for Feign.
|
||||||
|feign.oauth2.enabled | `false` | Enables feign interceptor for managing oauth2 access token.
|
|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.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.okhttp.enabled | `+++false+++` | Enables the use of the OK HTTP Client by Feign.
|
||||||
|
|
||||||
|===
|
|===
|
||||||
@@ -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.
|
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`.
|
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`.
|
||||||
|
|
||||||
@@ -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`).
|
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]
|
||||||
|
----
|
||||||
|
@FeignClient(url = "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]]
|
[[spring-cloud-feign-circuitbreaker-fallback]]
|
||||||
=== Feign Spring Cloud CircuitBreaker Fallbacks
|
=== Feign Spring Cloud CircuitBreaker Fallbacks
|
||||||
@@ -739,7 +780,7 @@ To work around this problem you can use an `ObjectProvider` when autowiring your
|
|||||||
[source,java,indent=0]
|
[source,java,indent=0]
|
||||||
----
|
----
|
||||||
@Autowired
|
@Autowired
|
||||||
ObjectProvider<TestFeginClient> testFeginClient;
|
ObjectProvider<TestFeignClient> testFeignClient;
|
||||||
----
|
----
|
||||||
|
|
||||||
=== Spring Data Support
|
=== Spring Data Support
|
||||||
|
|||||||
@@ -4,14 +4,14 @@
|
|||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>spring-cloud-openfeign</artifactId>
|
<artifactId>spring-cloud-openfeign</artifactId>
|
||||||
<version>3.1.1-SNAPSHOT</version>
|
<version>3.1.5-SNAPSHOT</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<name>Spring Cloud OpenFeign</name>
|
<name>Spring Cloud OpenFeign</name>
|
||||||
<description>Spring Cloud OpenFeign</description>
|
<description>Spring Cloud OpenFeign</description>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-build</artifactId>
|
<artifactId>spring-cloud-build</artifactId>
|
||||||
<version>3.1.1-SNAPSHOT</version>
|
<version>3.1.5-SNAPSHOT</version>
|
||||||
<relativePath/>
|
<relativePath/>
|
||||||
</parent>
|
</parent>
|
||||||
<scm>
|
<scm>
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<main.basedir>${basedir}</main.basedir>
|
<main.basedir>${basedir}</main.basedir>
|
||||||
<jackson.version>2.11.3</jackson.version>
|
<jackson.version>2.11.3</jackson.version>
|
||||||
<spring-cloud-commons.version>3.1.1-SNAPSHOT</spring-cloud-commons.version>
|
<spring-cloud-commons.version>3.1.5-SNAPSHOT</spring-cloud-commons.version>
|
||||||
|
|
||||||
<!-- Plugin versions -->
|
<!-- Plugin versions -->
|
||||||
<maven-eclipse-plugin.version>2.10</maven-eclipse-plugin.version>
|
<maven-eclipse-plugin.version>2.10</maven-eclipse-plugin.version>
|
||||||
@@ -142,7 +142,7 @@
|
|||||||
<repository>
|
<repository>
|
||||||
<id>spring-snapshots</id>
|
<id>spring-snapshots</id>
|
||||||
<name>Spring Snapshots</name>
|
<name>Spring Snapshots</name>
|
||||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
<url>https://repo.spring.io/snapshot</url>
|
||||||
<snapshots>
|
<snapshots>
|
||||||
<enabled>true</enabled>
|
<enabled>true</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
@@ -153,7 +153,7 @@
|
|||||||
<repository>
|
<repository>
|
||||||
<id>spring-milestones</id>
|
<id>spring-milestones</id>
|
||||||
<name>Spring Milestones</name>
|
<name>Spring Milestones</name>
|
||||||
<url>https://repo.spring.io/libs-milestone-local</url>
|
<url>https://repo.spring.io/milestone</url>
|
||||||
<snapshots>
|
<snapshots>
|
||||||
<enabled>false</enabled>
|
<enabled>false</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
@@ -171,7 +171,7 @@
|
|||||||
<pluginRepository>
|
<pluginRepository>
|
||||||
<id>spring-snapshots</id>
|
<id>spring-snapshots</id>
|
||||||
<name>Spring Snapshots</name>
|
<name>Spring Snapshots</name>
|
||||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
<url>https://repo.spring.io/snapshot</url>
|
||||||
<snapshots>
|
<snapshots>
|
||||||
<enabled>true</enabled>
|
<enabled>true</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
@@ -182,7 +182,7 @@
|
|||||||
<pluginRepository>
|
<pluginRepository>
|
||||||
<id>spring-milestones</id>
|
<id>spring-milestones</id>
|
||||||
<name>Spring Milestones</name>
|
<name>Spring Milestones</name>
|
||||||
<url>https://repo.spring.io/libs-milestone-local</url>
|
<url>https://repo.spring.io/milestone</url>
|
||||||
<snapshots>
|
<snapshots>
|
||||||
<enabled>false</enabled>
|
<enabled>false</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
@@ -190,7 +190,7 @@
|
|||||||
<pluginRepository>
|
<pluginRepository>
|
||||||
<id>spring-releases</id>
|
<id>spring-releases</id>
|
||||||
<name>Spring Releases</name>
|
<name>Spring Releases</name>
|
||||||
<url>https://repo.spring.io/libs-release-local</url>
|
<url>https://repo.spring.io/release</url>
|
||||||
<snapshots>
|
<snapshots>
|
||||||
<enabled>false</enabled>
|
<enabled>false</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-openfeign</artifactId>
|
<artifactId>spring-cloud-openfeign</artifactId>
|
||||||
<version>3.1.1-SNAPSHOT</version>
|
<version>3.1.5-SNAPSHOT</version>
|
||||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>spring-cloud-openfeign-core</artifactId>
|
<artifactId>spring-cloud-openfeign-core</artifactId>
|
||||||
@@ -99,6 +99,13 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.github.openfeign.form</groupId>
|
<groupId>io.github.openfeign.form</groupId>
|
||||||
<artifactId>feign-form-spring</artifactId>
|
<artifactId>feign-form-spring</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<!-- Vulnerable in 3.8.0-->
|
||||||
|
<exclusion>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.github.openfeign</groupId>
|
<groupId>io.github.openfeign</groupId>
|
||||||
@@ -148,6 +155,10 @@
|
|||||||
<groupId>javax.activation</groupId>
|
<groupId>javax.activation</groupId>
|
||||||
<artifactId>javax.activation-api</artifactId>
|
<artifactId>javax.activation-api</artifactId>
|
||||||
</exclusion>
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>com.sun.activation</groupId>
|
||||||
|
<artifactId>jakarta.activation</artifactId>
|
||||||
|
</exclusion>
|
||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -208,6 +219,12 @@
|
|||||||
<artifactId>spring-cloud-loadbalancer</artifactId>
|
<artifactId>spring-cloud-loadbalancer</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-io</groupId>
|
||||||
|
<artifactId>commons-io</artifactId>
|
||||||
|
<version>2.11.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<profiles>
|
<profiles>
|
||||||
<profile>
|
<profile>
|
||||||
|
|||||||
+27
-6
@@ -18,6 +18,7 @@ package org.springframework.cloud.openfeign;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
@@ -165,10 +166,19 @@ public class FeignAutoConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean(CircuitBreakerNameResolver.class)
|
@ConditionalOnMissingBean(CircuitBreakerNameResolver.class)
|
||||||
|
@ConditionalOnProperty(value = "feign.circuitbreaker.alphanumeric-ids.enabled", havingValue = "false",
|
||||||
|
matchIfMissing = true)
|
||||||
public CircuitBreakerNameResolver circuitBreakerNameResolver() {
|
public CircuitBreakerNameResolver circuitBreakerNameResolver() {
|
||||||
return new DefaultCircuitBreakerNameResolver();
|
return new DefaultCircuitBreakerNameResolver();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(CircuitBreakerNameResolver.class)
|
||||||
|
@ConditionalOnProperty(value = "feign.circuitbreaker.alphanumeric-ids.enabled", havingValue = "true")
|
||||||
|
public CircuitBreakerNameResolver alphanumericCircuitBreakerNameResolver() {
|
||||||
|
return new AlphanumericCircuitBreakerNameResolver();
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
@ConditionalOnBean(CircuitBreakerFactory.class)
|
@ConditionalOnBean(CircuitBreakerFactory.class)
|
||||||
@@ -188,6 +198,15 @@ public class FeignAutoConfiguration {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
// the following configuration is for alternate feign clients if
|
||||||
@@ -274,8 +293,8 @@ public class FeignAutoConfiguration {
|
|||||||
@ConditionalOnMissingBean(ConnectionPool.class)
|
@ConditionalOnMissingBean(ConnectionPool.class)
|
||||||
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
|
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
|
||||||
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
|
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
|
||||||
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
|
int maxTotalConnections = httpClientProperties.getMaxConnections();
|
||||||
Long timeToLive = httpClientProperties.getTimeToLive();
|
long timeToLive = httpClientProperties.getTimeToLive();
|
||||||
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
|
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
|
||||||
return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
|
return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
|
||||||
}
|
}
|
||||||
@@ -283,12 +302,13 @@ public class FeignAutoConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool,
|
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool,
|
||||||
FeignHttpClientProperties httpClientProperties) {
|
FeignHttpClientProperties httpClientProperties) {
|
||||||
Boolean followRedirects = httpClientProperties.isFollowRedirects();
|
boolean followRedirects = httpClientProperties.isFollowRedirects();
|
||||||
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
|
int connectTimeout = httpClientProperties.getConnectionTimeout();
|
||||||
Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
|
boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
|
||||||
|
Duration readTimeout = httpClientProperties.getOkHttp().getReadTimeout();
|
||||||
this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation)
|
this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation)
|
||||||
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).followRedirects(followRedirects)
|
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).followRedirects(followRedirects)
|
||||||
.connectionPool(connectionPool).build();
|
.readTimeout(readTimeout).connectionPool(connectionPool).build();
|
||||||
return this.okHttpClient;
|
return this.okHttpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,6 +346,7 @@ public class FeignAutoConfiguration {
|
|||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@ConditionalOnClass(OAuth2ClientContext.class)
|
@ConditionalOnClass(OAuth2ClientContext.class)
|
||||||
@ConditionalOnProperty("feign.oauth2.enabled")
|
@ConditionalOnProperty("feign.oauth2.enabled")
|
||||||
|
@Deprecated // spring-security-oauth2 reached EOL
|
||||||
protected static class Oauth2FeignConfiguration {
|
protected static class Oauth2FeignConfiguration {
|
||||||
|
|
||||||
@ConditionalOnBean({ RetryLoadBalancerInterceptor.class, OAuth2ClientContext.class,
|
@ConditionalOnBean({ RetryLoadBalancerInterceptor.class, OAuth2ClientContext.class,
|
||||||
|
|||||||
+18
-2
@@ -17,6 +17,7 @@
|
|||||||
package org.springframework.cloud.openfeign;
|
package org.springframework.cloud.openfeign;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationHandler;
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Proxy;
|
import java.lang.reflect.Proxy;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@@ -29,6 +30,7 @@ import feign.Target;
|
|||||||
|
|
||||||
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
|
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
|
||||||
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
|
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.RequestAttributes;
|
||||||
import org.springframework.web.context.request.RequestContextHolder;
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
|
||||||
@@ -95,15 +97,29 @@ class FeignCircuitBreakerInvocationHandler implements InvocationHandler {
|
|||||||
try {
|
try {
|
||||||
return this.fallbackMethodMap.get(method).invoke(fallback, args);
|
return this.fallbackMethodMap.get(method).invoke(fallback, args);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception exception) {
|
||||||
throw new IllegalStateException(e);
|
unwrapAndRethrow(exception);
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
};
|
};
|
||||||
return circuitBreaker.run(supplier, fallbackFunction);
|
return circuitBreaker.run(supplier, fallbackFunction);
|
||||||
}
|
}
|
||||||
return circuitBreaker.run(supplier);
|
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<Object> asSupplier(final Method method, final Object[] args) {
|
private Supplier<Object> asSupplier(final Method method, final Object[] args) {
|
||||||
final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||||
return () -> {
|
return () -> {
|
||||||
|
|||||||
+8
-1
@@ -69,6 +69,7 @@ import org.springframework.util.StringUtils;
|
|||||||
* @author Sam Kruglov
|
* @author Sam Kruglov
|
||||||
* @author Jasbir Singh
|
* @author Jasbir Singh
|
||||||
* @author Hyeonmin Park
|
* @author Hyeonmin Park
|
||||||
|
* @author Felix Dittrich
|
||||||
*/
|
*/
|
||||||
public class FeignClientFactoryBean
|
public class FeignClientFactoryBean
|
||||||
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware, BeanFactoryAware {
|
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware, BeanFactoryAware {
|
||||||
@@ -132,7 +133,6 @@ public class FeignClientFactoryBean
|
|||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
|
||||||
configureFeign(context, builder);
|
configureFeign(context, builder);
|
||||||
applyBuildCustomizers(context, builder);
|
|
||||||
|
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
@@ -374,6 +374,7 @@ public class FeignClientFactoryBean
|
|||||||
Client client = getOptional(context, Client.class);
|
Client client = getOptional(context, Client.class);
|
||||||
if (client != null) {
|
if (client != null) {
|
||||||
builder.client(client);
|
builder.client(client);
|
||||||
|
applyBuildCustomizers(context, builder);
|
||||||
Targeter targeter = get(context, Targeter.class);
|
Targeter targeter = get(context, Targeter.class);
|
||||||
return targeter.target(this, builder, context, target);
|
return targeter.target(this, builder, context, target);
|
||||||
}
|
}
|
||||||
@@ -443,11 +444,17 @@ public class FeignClientFactoryBean
|
|||||||
}
|
}
|
||||||
builder.client(client);
|
builder.client(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
applyBuildCustomizers(context, builder);
|
||||||
|
|
||||||
Targeter targeter = get(context, Targeter.class);
|
Targeter targeter = get(context, Targeter.class);
|
||||||
return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
|
return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String cleanPath() {
|
private String cleanPath() {
|
||||||
|
if (path == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
String path = this.path.trim();
|
String path = this.path.trim();
|
||||||
if (StringUtils.hasLength(path)) {
|
if (StringUtils.hasLength(path)) {
|
||||||
if (!path.startsWith("/")) {
|
if (!path.startsWith("/")) {
|
||||||
|
|||||||
+5
-1
@@ -302,7 +302,11 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLo
|
|||||||
if (resolver == null) {
|
if (resolver == null) {
|
||||||
return resolved;
|
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;
|
return value;
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-5
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.openfeign.clientconfig;
|
package org.springframework.cloud.openfeign.clientconfig;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.annotation.PreDestroy;
|
import javax.annotation.PreDestroy;
|
||||||
@@ -48,8 +49,8 @@ public class OkHttpFeignConfiguration {
|
|||||||
@ConditionalOnMissingBean(ConnectionPool.class)
|
@ConditionalOnMissingBean(ConnectionPool.class)
|
||||||
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
|
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
|
||||||
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
|
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
|
||||||
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
|
int maxTotalConnections = httpClientProperties.getMaxConnections();
|
||||||
Long timeToLive = httpClientProperties.getTimeToLive();
|
long timeToLive = httpClientProperties.getTimeToLive();
|
||||||
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
|
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
|
||||||
return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
|
return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
|
||||||
}
|
}
|
||||||
@@ -57,11 +58,12 @@ public class OkHttpFeignConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool,
|
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool,
|
||||||
FeignHttpClientProperties httpClientProperties) {
|
FeignHttpClientProperties httpClientProperties) {
|
||||||
Boolean followRedirects = httpClientProperties.isFollowRedirects();
|
boolean followRedirects = httpClientProperties.isFollowRedirects();
|
||||||
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
|
int connectTimeout = httpClientProperties.getConnectionTimeout();
|
||||||
|
Duration reaTimeout = httpClientProperties.getOkHttp().getReadTimeout();
|
||||||
this.okHttpClient = httpClientFactory.createBuilder(httpClientProperties.isDisableSslValidation())
|
this.okHttpClient = httpClientFactory.createBuilder(httpClientProperties.isDisableSslValidation())
|
||||||
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).followRedirects(followRedirects)
|
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).followRedirects(followRedirects)
|
||||||
.connectionPool(connectionPool).build();
|
.readTimeout(reaTimeout).connectionPool(connectionPool).build();
|
||||||
return this.okHttpClient;
|
return this.okHttpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -110,8 +110,9 @@ public class FeignBlockingLoadBalancerClient implements Client {
|
|||||||
}
|
}
|
||||||
String reconstructedUrl = loadBalancerClient.reconstructURI(instance, originalUri).toString();
|
String reconstructedUrl = loadBalancerClient.reconstructURI(instance, originalUri).toString();
|
||||||
Request newRequest = buildRequest(request, reconstructedUrl);
|
Request newRequest = buildRequest(request, reconstructedUrl);
|
||||||
|
LoadBalancerProperties loadBalancerProperties = loadBalancerClientFactory.getProperties(serviceId);
|
||||||
return executeWithLoadBalancerLifecycleProcessing(delegate, options, newRequest, lbRequest, lbResponse,
|
return executeWithLoadBalancerLifecycleProcessing(delegate, options, newRequest, lbRequest, lbResponse,
|
||||||
supportedLifecycleProcessors);
|
supportedLifecycleProcessors, loadBalancerProperties.isUseRawStatusCodeInResponseData());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Request buildRequest(Request request, String reconstructedUrl) {
|
protected Request buildRequest(Request request, String reconstructedUrl) {
|
||||||
|
|||||||
+9
-5
@@ -50,14 +50,15 @@ final class LoadBalancerUtils {
|
|||||||
static Response executeWithLoadBalancerLifecycleProcessing(Client feignClient, Request.Options options,
|
static Response executeWithLoadBalancerLifecycleProcessing(Client feignClient, Request.Options options,
|
||||||
Request feignRequest, org.springframework.cloud.client.loadbalancer.Request lbRequest,
|
Request feignRequest, org.springframework.cloud.client.loadbalancer.Request lbRequest,
|
||||||
org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse,
|
org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse,
|
||||||
Set<LoadBalancerLifecycle> supportedLifecycleProcessors, boolean loadBalanced) throws IOException {
|
Set<LoadBalancerLifecycle> supportedLifecycleProcessors, boolean loadBalanced, boolean useRawStatusCodes)
|
||||||
|
throws IOException {
|
||||||
supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStartRequest(lbRequest, lbResponse));
|
supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStartRequest(lbRequest, lbResponse));
|
||||||
try {
|
try {
|
||||||
Response response = feignClient.execute(feignRequest, options);
|
Response response = feignClient.execute(feignRequest, options);
|
||||||
if (loadBalanced) {
|
if (loadBalanced) {
|
||||||
supportedLifecycleProcessors.forEach(
|
supportedLifecycleProcessors.forEach(
|
||||||
lifecycle -> lifecycle.onComplete(new CompletionContext<>(CompletionContext.Status.SUCCESS,
|
lifecycle -> lifecycle.onComplete(new CompletionContext<>(CompletionContext.Status.SUCCESS,
|
||||||
lbRequest, lbResponse, buildResponseData(response))));
|
lbRequest, lbResponse, buildResponseData(response, useRawStatusCodes))));
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@@ -70,9 +71,12 @@ final class LoadBalancerUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static ResponseData buildResponseData(Response response) {
|
static ResponseData buildResponseData(Response response, boolean useRawStatusCodes) {
|
||||||
HttpHeaders responseHeaders = new HttpHeaders();
|
HttpHeaders responseHeaders = new HttpHeaders();
|
||||||
response.headers().forEach((key, value) -> responseHeaders.put(key, new ArrayList<>(value)));
|
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,
|
return new ResponseData(HttpStatus.resolve(response.status()), responseHeaders, null,
|
||||||
buildRequestData(response.request()));
|
buildRequestData(response.request()));
|
||||||
}
|
}
|
||||||
@@ -87,9 +91,9 @@ final class LoadBalancerUtils {
|
|||||||
static Response executeWithLoadBalancerLifecycleProcessing(Client feignClient, Request.Options options,
|
static Response executeWithLoadBalancerLifecycleProcessing(Client feignClient, Request.Options options,
|
||||||
Request feignRequest, org.springframework.cloud.client.loadbalancer.Request lbRequest,
|
Request feignRequest, org.springframework.cloud.client.loadbalancer.Request lbRequest,
|
||||||
org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse,
|
org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse,
|
||||||
Set<LoadBalancerLifecycle> supportedLifecycleProcessors) throws IOException {
|
Set<LoadBalancerLifecycle> supportedLifecycleProcessors, boolean useRawStatusCodes) throws IOException {
|
||||||
return executeWithLoadBalancerLifecycleProcessing(feignClient, options, feignRequest, lbRequest, lbResponse,
|
return executeWithLoadBalancerLifecycleProcessing(feignClient, options, feignRequest, lbRequest, lbResponse,
|
||||||
supportedLifecycleProcessors, true);
|
supportedLifecycleProcessors, true, useRawStatusCodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -163,9 +163,10 @@ public class RetryableFeignBlockingLoadBalancerClient implements Client {
|
|||||||
}
|
}
|
||||||
org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse = new DefaultResponse(
|
org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse = new DefaultResponse(
|
||||||
retrievedServiceInstance);
|
retrievedServiceInstance);
|
||||||
|
LoadBalancerProperties loadBalancerProperties = loadBalancerClientFactory.getProperties(serviceId);
|
||||||
Response response = LoadBalancerUtils.executeWithLoadBalancerLifecycleProcessing(delegate, options,
|
Response response = LoadBalancerUtils.executeWithLoadBalancerLifecycleProcessing(delegate, options,
|
||||||
feignRequest, lbRequest, lbResponse, supportedLifecycleProcessors,
|
feignRequest, lbRequest, lbResponse, supportedLifecycleProcessors, retrievedServiceInstance != null,
|
||||||
retrievedServiceInstance != null);
|
loadBalancerProperties.isUseRawStatusCodeInResponseData());
|
||||||
int responseStatus = response.status();
|
int responseStatus = response.status();
|
||||||
if (retryPolicy != null && retryPolicy.retryableStatusCode(responseStatus)) {
|
if (retryPolicy != null && retryPolicy.retryableStatusCode(responseStatus)) {
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
|
|||||||
+1
@@ -43,6 +43,7 @@ import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
|||||||
* @author Tim Ysewyn
|
* @author Tim Ysewyn
|
||||||
* @since 3.0.0
|
* @since 3.0.0
|
||||||
*/
|
*/
|
||||||
|
@Deprecated // spring-security-oauth2 reached EOL
|
||||||
public class OAuth2FeignRequestInterceptor implements RequestInterceptor {
|
public class OAuth2FeignRequestInterceptor implements RequestInterceptor {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+46
-9
@@ -16,13 +16,17 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.openfeign.support;
|
package org.springframework.cloud.openfeign.support;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import feign.okhttp.OkHttpClient;
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Ryan Baxter
|
* @author Ryan Baxter
|
||||||
* @author Nguyen Ky Thanh
|
* @author Nguyen Ky Thanh
|
||||||
|
* @author Olga Maciaszek-Sharma
|
||||||
*/
|
*/
|
||||||
@ConfigurationProperties(prefix = "feign.httpclient")
|
@ConfigurationProperties(prefix = "feign.httpclient")
|
||||||
public class FeignHttpClientProperties {
|
public class FeignHttpClientProperties {
|
||||||
@@ -88,8 +92,13 @@ public class FeignHttpClientProperties {
|
|||||||
*/
|
*/
|
||||||
private Hc5Properties hc5 = new Hc5Properties();
|
private Hc5Properties hc5 = new Hc5Properties();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Additional {@link OkHttpClient}-specific properties.
|
||||||
|
*/
|
||||||
|
private OkHttp okHttp = new OkHttp();
|
||||||
|
|
||||||
public int getConnectionTimerRepeat() {
|
public int getConnectionTimerRepeat() {
|
||||||
return this.connectionTimerRepeat;
|
return connectionTimerRepeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setConnectionTimerRepeat(int connectionTimerRepeat) {
|
public void setConnectionTimerRepeat(int connectionTimerRepeat) {
|
||||||
@@ -97,7 +106,7 @@ public class FeignHttpClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDisableSslValidation() {
|
public boolean isDisableSslValidation() {
|
||||||
return this.disableSslValidation;
|
return disableSslValidation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDisableSslValidation(boolean disableSslValidation) {
|
public void setDisableSslValidation(boolean disableSslValidation) {
|
||||||
@@ -105,7 +114,7 @@ public class FeignHttpClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxConnections() {
|
public int getMaxConnections() {
|
||||||
return this.maxConnections;
|
return maxConnections;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMaxConnections(int maxConnections) {
|
public void setMaxConnections(int maxConnections) {
|
||||||
@@ -113,7 +122,7 @@ public class FeignHttpClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxConnectionsPerRoute() {
|
public int getMaxConnectionsPerRoute() {
|
||||||
return this.maxConnectionsPerRoute;
|
return maxConnectionsPerRoute;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMaxConnectionsPerRoute(int maxConnectionsPerRoute) {
|
public void setMaxConnectionsPerRoute(int maxConnectionsPerRoute) {
|
||||||
@@ -121,7 +130,7 @@ public class FeignHttpClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public long getTimeToLive() {
|
public long getTimeToLive() {
|
||||||
return this.timeToLive;
|
return timeToLive;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTimeToLive(long timeToLive) {
|
public void setTimeToLive(long timeToLive) {
|
||||||
@@ -129,7 +138,7 @@ public class FeignHttpClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public TimeUnit getTimeToLiveUnit() {
|
public TimeUnit getTimeToLiveUnit() {
|
||||||
return this.timeToLiveUnit;
|
return timeToLiveUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTimeToLiveUnit(TimeUnit timeToLiveUnit) {
|
public void setTimeToLiveUnit(TimeUnit timeToLiveUnit) {
|
||||||
@@ -137,7 +146,7 @@ public class FeignHttpClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFollowRedirects() {
|
public boolean isFollowRedirects() {
|
||||||
return this.followRedirects;
|
return followRedirects;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFollowRedirects(boolean followRedirects) {
|
public void setFollowRedirects(boolean followRedirects) {
|
||||||
@@ -145,7 +154,7 @@ public class FeignHttpClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getConnectionTimeout() {
|
public int getConnectionTimeout() {
|
||||||
return this.connectionTimeout;
|
return connectionTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setConnectionTimeout(int connectionTimeout) {
|
public void setConnectionTimeout(int connectionTimeout) {
|
||||||
@@ -160,6 +169,14 @@ public class FeignHttpClientProperties {
|
|||||||
this.hc5 = hc5;
|
this.hc5 = hc5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OkHttp getOkHttp() {
|
||||||
|
return okHttp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOkHttp(OkHttp okHttp) {
|
||||||
|
this.okHttp = okHttp;
|
||||||
|
}
|
||||||
|
|
||||||
public static class Hc5Properties {
|
public static class Hc5Properties {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -203,7 +220,7 @@ public class FeignHttpClientProperties {
|
|||||||
private TimeUnit socketTimeoutUnit = DEFAULT_SOCKET_TIMEOUT_UNIT;
|
private TimeUnit socketTimeoutUnit = DEFAULT_SOCKET_TIMEOUT_UNIT;
|
||||||
|
|
||||||
public PoolConcurrencyPolicy getPoolConcurrencyPolicy() {
|
public PoolConcurrencyPolicy getPoolConcurrencyPolicy() {
|
||||||
return this.poolConcurrencyPolicy;
|
return poolConcurrencyPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPoolConcurrencyPolicy(PoolConcurrencyPolicy poolConcurrencyPolicy) {
|
public void setPoolConcurrencyPolicy(PoolConcurrencyPolicy poolConcurrencyPolicy) {
|
||||||
@@ -272,4 +289,24 @@ public class FeignHttpClientProperties {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link OkHttpClient}-specific properties.
|
||||||
|
*/
|
||||||
|
public static class OkHttp {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link OkHttpClient} read timeout; defaults to 60 seconds.
|
||||||
|
*/
|
||||||
|
private Duration readTimeout = Duration.ofSeconds(60);
|
||||||
|
|
||||||
|
public Duration getReadTimeout() {
|
||||||
|
return readTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReadTimeout(Duration readTimeout) {
|
||||||
|
this.readTimeout = readTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+15
@@ -196,6 +196,21 @@ public class PageJacksonModule extends Module {
|
|||||||
return delegate.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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -247,7 +247,7 @@ public class SpringEncoder implements Encoder {
|
|||||||
return Objects.equals(APPLICATION_FORM_URLENCODED, requestContentType);
|
return Objects.equals(APPLICATION_FORM_URLENCODED, requestContentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean binaryContentType(FeignOutputMessage outputMessage) {
|
protected boolean binaryContentType(FeignOutputMessage outputMessage) {
|
||||||
MediaType contentType = outputMessage.getHeaders().getContentType();
|
MediaType contentType = outputMessage.getHeaders().getContentType();
|
||||||
return contentType == null || Stream
|
return contentType == null || Stream
|
||||||
.of(MediaType.APPLICATION_CBOR, MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_PDF,
|
.of(MediaType.APPLICATION_CBOR, MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_PDF,
|
||||||
|
|||||||
+6
@@ -14,6 +14,12 @@
|
|||||||
"description": "If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker.",
|
"description": "If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker.",
|
||||||
"defaultValue": "false"
|
"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",
|
"name": "feign.circuitbreaker.group.enabled",
|
||||||
"type": "java.lang.Boolean",
|
"type": "java.lang.Boolean",
|
||||||
|
|||||||
+91
-10
@@ -17,35 +17,56 @@
|
|||||||
package org.springframework.cloud.openfeign;
|
package org.springframework.cloud.openfeign;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import feign.Client;
|
||||||
import feign.Feign;
|
import feign.Feign;
|
||||||
import feign.Logger;
|
import feign.Logger;
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.core.annotation.Order;
|
import org.springframework.core.annotation.Order;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.doCallRealMethod;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Matt King
|
* @author Matt King
|
||||||
* @author Sam Kruglov
|
* @author Sam Kruglov
|
||||||
|
* @author Felix Dittrich
|
||||||
|
* @author Olga Maciaszek-Sharma
|
||||||
*/
|
*/
|
||||||
class FeignBuilderCustomizerTests {
|
class FeignBuilderCustomizerTests {
|
||||||
|
|
||||||
|
private static final Targeter targeterSpy = spy(DefaultTargeter.class);
|
||||||
|
|
||||||
|
private static final Client defaultClient = mock(Client.class);
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testBuilderCustomizer() {
|
void testBuilderCustomizer() {
|
||||||
|
ArgumentCaptor<Feign.Builder> feignBuilderCaptor = ArgumentCaptor.forClass(Feign.Builder.class);
|
||||||
|
doCallRealMethod().when(targeterSpy).target(any(), feignBuilderCaptor.capture(), any(), any());
|
||||||
|
|
||||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||||
FeignBuilderCustomizerTests.SampleConfiguration2.class);
|
FeignBuilderCustomizerTests.SampleConfiguration2.class);
|
||||||
|
|
||||||
FeignClientFactoryBean clientFactoryBean = context.getBean(FeignClientFactoryBean.class);
|
FeignClientFactoryBean clientFactoryBean = context.getBean(FeignClientFactoryBean.class);
|
||||||
FeignContext feignContext = context.getBean(FeignContext.class);
|
clientFactoryBean.getTarget();
|
||||||
|
|
||||||
Feign.Builder builder = clientFactoryBean.feign(feignContext);
|
Assertions.assertNotNull(feignBuilderCaptor.getValue());
|
||||||
|
Feign.Builder builder = feignBuilderCaptor.getValue();
|
||||||
assertFeignBuilderField(builder, "logLevel", Logger.Level.HEADERS);
|
assertFeignBuilderField(builder, "logLevel", Logger.Level.HEADERS);
|
||||||
assertFeignBuilderField(builder, "decode404", true);
|
assertFeignBuilderField(builder, "decode404", true);
|
||||||
|
|
||||||
@@ -62,13 +83,17 @@ class FeignBuilderCustomizerTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testBuildCustomizerOrdered() {
|
void testBuildCustomizerOrdered() {
|
||||||
|
ArgumentCaptor<Feign.Builder> feignBuilderCaptor = ArgumentCaptor.forClass(Feign.Builder.class);
|
||||||
|
doCallRealMethod().when(targeterSpy).target(any(), feignBuilderCaptor.capture(), any(), any());
|
||||||
|
|
||||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||||
FeignBuilderCustomizerTests.SampleConfiguration3.class);
|
FeignBuilderCustomizerTests.SampleConfiguration3.class);
|
||||||
|
|
||||||
FeignClientFactoryBean clientFactoryBean = context.getBean(FeignClientFactoryBean.class);
|
FeignClientFactoryBean clientFactoryBean = context.getBean(FeignClientFactoryBean.class);
|
||||||
FeignContext feignContext = context.getBean(FeignContext.class);
|
clientFactoryBean.getTarget();
|
||||||
|
|
||||||
Feign.Builder builder = clientFactoryBean.feign(feignContext);
|
Assertions.assertNotNull(feignBuilderCaptor.getValue());
|
||||||
|
Feign.Builder builder = feignBuilderCaptor.getValue();
|
||||||
assertFeignBuilderField(builder, "logLevel", Logger.Level.FULL);
|
assertFeignBuilderField(builder, "logLevel", Logger.Level.FULL);
|
||||||
assertFeignBuilderField(builder, "decode404", true);
|
assertFeignBuilderField(builder, "decode404", true);
|
||||||
|
|
||||||
@@ -77,15 +102,19 @@ class FeignBuilderCustomizerTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testBuildCustomizerOrderedWithAdditional() {
|
void testBuildCustomizerOrderedWithAdditional() {
|
||||||
|
ArgumentCaptor<Feign.Builder> feignBuilderCaptor = ArgumentCaptor.forClass(Feign.Builder.class);
|
||||||
|
doCallRealMethod().when(targeterSpy).target(any(), feignBuilderCaptor.capture(), any(), any());
|
||||||
|
|
||||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||||
FeignBuilderCustomizerTests.SampleConfiguration3.class);
|
FeignBuilderCustomizerTests.SampleConfiguration3.class);
|
||||||
|
|
||||||
FeignClientFactoryBean clientFactoryBean = context.getBean(FeignClientFactoryBean.class);
|
FeignClientFactoryBean clientFactoryBean = context.getBean(FeignClientFactoryBean.class);
|
||||||
clientFactoryBean.addCustomizer(builder -> builder.logLevel(Logger.Level.BASIC));
|
clientFactoryBean.addCustomizer(builder -> builder.logLevel(Logger.Level.BASIC));
|
||||||
clientFactoryBean.addCustomizer(Feign.Builder::doNotCloseAfterDecode);
|
clientFactoryBean.addCustomizer(Feign.Builder::doNotCloseAfterDecode);
|
||||||
FeignContext feignContext = context.getBean(FeignContext.class);
|
clientFactoryBean.getTarget();
|
||||||
|
|
||||||
Feign.Builder builder = clientFactoryBean.feign(feignContext);
|
Assertions.assertNotNull(feignBuilderCaptor.getValue());
|
||||||
|
Feign.Builder builder = feignBuilderCaptor.getValue();
|
||||||
assertFeignBuilderField(builder, "logLevel", Logger.Level.BASIC);
|
assertFeignBuilderField(builder, "logLevel", Logger.Level.BASIC);
|
||||||
assertFeignBuilderField(builder, "decode404", true);
|
assertFeignBuilderField(builder, "decode404", true);
|
||||||
assertFeignBuilderField(builder, "closeAfterDecode", false);
|
assertFeignBuilderField(builder, "closeAfterDecode", false);
|
||||||
@@ -93,16 +122,41 @@ class FeignBuilderCustomizerTests {
|
|||||||
context.close();
|
context.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FeignClientFactoryBean defaultFeignClientFactoryBean() {
|
@ParameterizedTest(name = "should use custom HttpClient with config: {0}")
|
||||||
|
@MethodSource("testConfiguration")
|
||||||
|
void testBuildCustomizerWithCustomHttpClient(Class configClass) {
|
||||||
|
ArgumentCaptor<Feign.Builder> feignBuilderCaptor = ArgumentCaptor.forClass(Feign.Builder.class);
|
||||||
|
doCallRealMethod().when(targeterSpy).target(any(), feignBuilderCaptor.capture(), any(), any());
|
||||||
|
Client customClientMock = mock(Client.class);
|
||||||
|
|
||||||
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(configClass);
|
||||||
|
FeignClientFactoryBean clientFactoryBean = context.getBean(FeignClientFactoryBean.class);
|
||||||
|
clientFactoryBean.addCustomizer(builder -> builder.client(customClientMock));
|
||||||
|
clientFactoryBean.getTarget();
|
||||||
|
|
||||||
|
Assertions.assertNotNull(feignBuilderCaptor.getValue());
|
||||||
|
Feign.Builder builder = feignBuilderCaptor.getValue();
|
||||||
|
assertFeignBuilderField(builder, "client", customClientMock);
|
||||||
|
|
||||||
|
context.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static FeignClientFactoryBean defaultFeignClientFactoryBean(String url) {
|
||||||
FeignClientFactoryBean feignClientFactoryBean = new FeignClientFactoryBean();
|
FeignClientFactoryBean feignClientFactoryBean = new FeignClientFactoryBean();
|
||||||
feignClientFactoryBean.setContextId("test");
|
feignClientFactoryBean.setContextId("test");
|
||||||
feignClientFactoryBean.setName("test");
|
feignClientFactoryBean.setName("test");
|
||||||
feignClientFactoryBean.setType(FeignClientFactoryTests.TestType.class);
|
feignClientFactoryBean.setType(FeignClientFactoryTests.TestType.class);
|
||||||
feignClientFactoryBean.setPath("");
|
feignClientFactoryBean.setPath("");
|
||||||
feignClientFactoryBean.setUrl("http://some.absolute.url");
|
if (url != null) {
|
||||||
|
feignClientFactoryBean.setUrl(url);
|
||||||
|
}
|
||||||
return feignClientFactoryBean;
|
return feignClientFactoryBean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Stream<Class> testConfiguration() {
|
||||||
|
return Stream.of(SampleConfiguration3.class, LoadBalancedSampleConfiguration.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@Import(FeignClientsConfiguration.class)
|
@Import(FeignClientsConfiguration.class)
|
||||||
protected static class SampleConfiguration2 {
|
protected static class SampleConfiguration2 {
|
||||||
@@ -129,7 +183,12 @@ class FeignBuilderCustomizerTests {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
FeignClientFactoryBean feignClientFactoryBean() {
|
FeignClientFactoryBean feignClientFactoryBean() {
|
||||||
return defaultFeignClientFactoryBean();
|
return defaultFeignClientFactoryBean("http://some.absolute.url");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
Targeter targeter() {
|
||||||
|
return targeterSpy;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -167,7 +226,29 @@ class FeignBuilderCustomizerTests {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
FeignClientFactoryBean feignClientFactoryBean() {
|
FeignClientFactoryBean feignClientFactoryBean() {
|
||||||
return defaultFeignClientFactoryBean();
|
return defaultFeignClientFactoryBean("http://some.absolute.url");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
Targeter targeter() {
|
||||||
|
return targeterSpy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
Client client() {
|
||||||
|
return defaultClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@Import(SampleConfiguration3.class)
|
||||||
|
protected static class LoadBalancedSampleConfiguration {
|
||||||
|
|
||||||
|
@Primary
|
||||||
|
@Bean
|
||||||
|
FeignClientFactoryBean feignClientFactoryBean() {
|
||||||
|
return defaultFeignClientFactoryBean(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+59
-4
@@ -18,24 +18,30 @@ package org.springframework.cloud.openfeign;
|
|||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import feign.Target;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.condition.DisabledForJreRange;
|
||||||
|
import org.junit.jupiter.api.condition.JRE;
|
||||||
|
|
||||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.mock.env.MockEnvironment;
|
import org.springframework.mock.env.MockEnvironment;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatCode;
|
import static org.assertj.core.api.Assertions.assertThatCode;
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
* @author Gang Li
|
* @author Gang Li
|
||||||
* @author Michal Domagala
|
* @author Michal Domagala
|
||||||
* @author Szymon Linowski
|
* @author Szymon Linowski
|
||||||
|
* @author Olga Maciaszek-Sharma
|
||||||
*/
|
*/
|
||||||
class FeignClientsRegistrarTests {
|
class FeignClientsRegistrarTests {
|
||||||
|
|
||||||
@@ -97,14 +103,39 @@ class FeignClientsRegistrarTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldPassSubLevelFeignClient() {
|
void shouldPassSubLevelFeignClient() {
|
||||||
AnnotationConfigApplicationContext config = new AnnotationConfigApplicationContext();
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||||
((DefaultListableBeanFactory) config.getBeanFactory()).setAllowBeanDefinitionOverriding(false);
|
((DefaultListableBeanFactory) context.getBeanFactory()).setAllowBeanDefinitionOverriding(false);
|
||||||
config.register(TopLevelSubLevelTestConfig.class);
|
context.register(TopLevelSubLevelTestConfig.class);
|
||||||
assertThatCode(() -> config.refresh())
|
assertThatCode(context::refresh)
|
||||||
.as("Case https://github.com/spring-cloud/spring-cloud-openfeign/issues/331 should be solved")
|
.as("Case https://github.com/spring-cloud/spring-cloud-openfeign/issues/331 should be solved")
|
||||||
.doesNotThrowAnyException();
|
.doesNotThrowAnyException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisabledForJreRange(min = JRE.JAVA_16)
|
||||||
|
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<NullUrlFeignClient> target = (Target.HardCodedTarget<NullUrlFeignClient>) ReflectionTestUtils
|
||||||
|
.getField(invocationHandlerLambda, "arg$3");
|
||||||
|
assertThat(target.name()).isEqualTo("nullUrlFeignClient");
|
||||||
|
assertThat(target.url()).isEqualTo("http://nullUrlFeignClient");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldResolveAndValidateNullName() {
|
||||||
|
assertThatIllegalStateException().isThrownBy(() -> {
|
||||||
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||||
|
context.register(NullExpressionNameFeignClientTestConfig.class);
|
||||||
|
context.refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@FeignClient(name = "fallbackTestClient", url = "http://localhost:8080/", fallback = FallbackClient.class)
|
@FeignClient(name = "fallbackTestClient", url = "http://localhost:8080/", fallback = FallbackClient.class)
|
||||||
protected interface FallbackClient {
|
protected interface FallbackClient {
|
||||||
|
|
||||||
@@ -122,6 +153,16 @@ class FeignClientsRegistrarTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
@EnableFeignClients(clients = { FeignClientsRegistrarTests.FallbackClient.class })
|
@EnableFeignClients(clients = { FeignClientsRegistrarTests.FallbackClient.class })
|
||||||
@@ -142,4 +183,18 @@ class FeignClientsRegistrarTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@EnableFeignClients(clients = NullUrlFeignClient.class)
|
||||||
|
protected static class NullUrlFeignClientTestConfig {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@EnableFeignClients(clients = NullExpressionNameFeignClient.class)
|
||||||
|
protected static class NullExpressionNameFeignClientTestConfig {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-5
@@ -45,26 +45,33 @@ class FeignOkHttpConfigurationTests {
|
|||||||
void setUp() {
|
void setUp() {
|
||||||
this.context = new SpringApplicationBuilder()
|
this.context = new SpringApplicationBuilder()
|
||||||
.properties("debug=true", "feign.httpclient.disableSslValidation=true", "feign.okhttp.enabled=true",
|
.properties("debug=true", "feign.httpclient.disableSslValidation=true", "feign.okhttp.enabled=true",
|
||||||
"feign.httpclient.enabled=false")
|
"feign.httpclient.enabled=false", "feign.httpclient.okhttp.read-timeout=9s")
|
||||||
.web(WebApplicationType.NONE).sources(HttpClientConfiguration.class, FeignAutoConfiguration.class)
|
.web(WebApplicationType.NONE).sources(HttpClientConfiguration.class, FeignAutoConfiguration.class)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
void tearDown() {
|
void tearDown() {
|
||||||
if (this.context != null) {
|
if (context != null) {
|
||||||
this.context.close();
|
context.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void disableSslTest() {
|
void disableSslTest() {
|
||||||
OkHttpClient httpClient = this.context.getBean(OkHttpClient.class);
|
OkHttpClient httpClient = context.getBean(OkHttpClient.class);
|
||||||
HostnameVerifier hostnameVerifier = (HostnameVerifier) this.getField(httpClient, "hostnameVerifier");
|
HostnameVerifier hostnameVerifier = (HostnameVerifier) this.getField(httpClient, "hostnameVerifier");
|
||||||
assertThat(hostnameVerifier instanceof OkHttpClientFactory.TrustAllHostnames).isTrue();
|
assertThat(hostnameVerifier instanceof OkHttpClientFactory.TrustAllHostnames).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> Object getField(Object target, String name) {
|
@Test
|
||||||
|
void shouldConfigureReadTimeout() {
|
||||||
|
OkHttpClient httpClient = context.getBean(OkHttpClient.class);
|
||||||
|
|
||||||
|
assertThat(httpClient.readTimeoutMillis()).isEqualTo(9000);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object getField(Object target, String name) {
|
||||||
Field field = ReflectionUtils.findField(target.getClass(), name);
|
Field field = ReflectionUtils.findField(target.getClass(), name);
|
||||||
ReflectionUtils.makeAccessible(field);
|
ReflectionUtils.makeAccessible(field);
|
||||||
Object value = ReflectionUtils.getField(field, target);
|
Object value = ReflectionUtils.getField(field, target);
|
||||||
|
|||||||
+78
@@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+53
-1
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.openfeign.circuitbreaker;
|
package org.springframework.cloud.openfeign.circuitbreaker;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
@@ -47,6 +48,7 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
@@ -62,6 +64,9 @@ class CircuitBreakerTests {
|
|||||||
@Autowired
|
@Autowired
|
||||||
TestClient testClient;
|
TestClient testClient;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
ExceptionClient exceptionClient;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
TestClientWithFactory testClientWithFactory;
|
TestClientWithFactory testClientWithFactory;
|
||||||
|
|
||||||
@@ -108,6 +113,17 @@ class CircuitBreakerTests {
|
|||||||
assertThat(testClientWithFactory.getException()).isEqualTo("Fixed response");
|
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)
|
@FeignClient(name = "test", url = "http://localhost:${server.port}/", fallback = Fallback.class)
|
||||||
protected interface TestClient {
|
protected interface TestClient {
|
||||||
|
|
||||||
@@ -119,6 +135,18 @@ class CircuitBreakerTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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
|
@Component
|
||||||
static class Fallback implements TestClient {
|
static class Fallback implements TestClient {
|
||||||
|
|
||||||
@@ -156,6 +184,25 @@ class CircuitBreakerTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class ExceptionThrowingFallbackFactory implements FallbackFactory<ExceptionClient> {
|
||||||
|
|
||||||
|
@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 {
|
static class FallbackWithFactory implements TestClientWithFactory {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -173,7 +220,7 @@ class CircuitBreakerTests {
|
|||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
@RestController
|
@RestController
|
||||||
@EnableFeignClients(clients = { TestClient.class, TestClientWithFactory.class })
|
@EnableFeignClients(clients = { TestClient.class, TestClientWithFactory.class, ExceptionClient.class })
|
||||||
@Import(NoSecurityConfiguration.class)
|
@Import(NoSecurityConfiguration.class)
|
||||||
protected static class Application implements TestClient {
|
protected static class Application implements TestClient {
|
||||||
|
|
||||||
@@ -225,6 +272,11 @@ class CircuitBreakerTests {
|
|||||||
return new TestFallbackFactory();
|
return new TestFallbackFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
ExceptionThrowingFallbackFactory exceptionThrowingFallbackFactory() {
|
||||||
|
return new ExceptionThrowingFallbackFactory();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-2
@@ -33,6 +33,7 @@ import org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfigurati
|
|||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.springframework.test.util.ReflectionTestUtils.getField;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Olga Maciaszek-Sharma
|
* @author Olga Maciaszek-Sharma
|
||||||
@@ -58,9 +59,18 @@ class FeignLoadBalancerAutoConfigurationTests {
|
|||||||
@Test
|
@Test
|
||||||
void shouldInstantiateOkHttpFeignClientWhenEnabled() {
|
void shouldInstantiateOkHttpFeignClientWhenEnabled() {
|
||||||
ConfigurableApplicationContext context = initContext("feign.httpclient.enabled=false",
|
ConfigurableApplicationContext context = initContext("feign.httpclient.enabled=false",
|
||||||
"feign.okhttp.enabled=true", "spring.cloud.loadbalancer.retry.enabled=false");
|
"feign.okhttp.enabled=true", "spring.cloud.loadbalancer.retry.enabled=false",
|
||||||
|
"feign.httpclient.okhttp.read-timeout=9s");
|
||||||
assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
|
assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
|
||||||
assertLoadBalanced(context, OkHttpClient.class);
|
Map<String, FeignBlockingLoadBalancerClient> beans = context
|
||||||
|
.getBeansOfType(FeignBlockingLoadBalancerClient.class);
|
||||||
|
assertThat(beans).as("Missing bean of type %s", OkHttpClient.class).hasSize(1);
|
||||||
|
Client client = beans.get("feignClient").getDelegate();
|
||||||
|
assertThat(client).isInstanceOf(OkHttpClient.class);
|
||||||
|
OkHttpClient okHttpClient = (OkHttpClient) client;
|
||||||
|
okhttp3.OkHttpClient httpClient = (okhttp3.OkHttpClient) getField(okHttpClient, "delegate");
|
||||||
|
assertThat(httpClient.readTimeoutMillis()).isEqualTo(9000);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
+42
-1
@@ -78,7 +78,9 @@ import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
|||||||
* @author Olga Maciaszek-Sharma
|
* @author Olga Maciaszek-Sharma
|
||||||
* @author Szymon Linowski
|
* @author Szymon Linowski
|
||||||
* @author Sam Kruglov
|
* @author Sam Kruglov
|
||||||
*/
|
* @author Bhavya Agrawal
|
||||||
|
**/
|
||||||
|
|
||||||
class SpringMvcContractTests {
|
class SpringMvcContractTests {
|
||||||
|
|
||||||
private static final Class<?> EXECUTABLE_TYPE;
|
private static final Class<?> EXECUTABLE_TYPE;
|
||||||
@@ -171,6 +173,39 @@ class SpringMvcContractTests {
|
|||||||
assertThat(data.indexToName().get(0).iterator().next()).isEqualTo("id");
|
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
|
@Test
|
||||||
void testProcessAnnotations_SimpleGetMapping() throws Exception {
|
void testProcessAnnotations_SimpleGetMapping() throws Exception {
|
||||||
Method method = TestTemplate_Simple.class.getDeclaredMethod("getMappingTest", String.class);
|
Method method = TestTemplate_Simple.class.getDeclaredMethod("getMappingTest", String.class);
|
||||||
@@ -616,6 +651,12 @@ class SpringMvcContractTests {
|
|||||||
@PostMapping(produces = MediaType.APPLICATION_JSON_VALUE)
|
@PostMapping(produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
TestObject postMappingTest(@RequestBody TestObject object);
|
TestObject postMappingTest(@RequestBody TestObject object);
|
||||||
|
|
||||||
|
@GetMapping(value = "/", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
ResponseEntity<TestObject> getSlashPath(@RequestParam("id") String id);
|
||||||
|
|
||||||
|
@GetMapping(path = "test", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
|
ResponseEntity<TestObject> getTestNoLeadingSlash(@RequestParam("name") String name);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping("/prepend/{classId}")
|
@RequestMapping("/prepend/{classId}")
|
||||||
|
|||||||
@@ -6,18 +6,19 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<artifactId>spring-cloud-dependencies-parent</artifactId>
|
<artifactId>spring-cloud-dependencies-parent</artifactId>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<version>3.1.0</version>
|
<version>3.1.5-SNAPSHOT</version>
|
||||||
<relativePath/>
|
<relativePath/>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>spring-cloud-openfeign-dependencies</artifactId>
|
<artifactId>spring-cloud-openfeign-dependencies</artifactId>
|
||||||
<version>3.1.1-SNAPSHOT</version>
|
<version>3.1.5-SNAPSHOT</version>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
<name>spring-cloud-openfeign-dependencies</name>
|
<name>spring-cloud-openfeign-dependencies</name>
|
||||||
<description>Spring Cloud OpenFeign Dependencies</description>
|
<description>Spring Cloud OpenFeign Dependencies</description>
|
||||||
<properties>
|
<properties>
|
||||||
<feign.version>11.8</feign.version>
|
<feign.version>11.8</feign.version>
|
||||||
<feign-form.version>3.8.0</feign-form.version>
|
<feign-form.version>3.8.0</feign-form.version>
|
||||||
<spring-security-oauth2-autoconfigure.version>2.1.2.RELEASE</spring-security-oauth2-autoconfigure.version>
|
<!-- Deprecated - reached EOL -->
|
||||||
|
<spring-security-oauth2-autoconfigure.version>2.5.2</spring-security-oauth2-autoconfigure.version>
|
||||||
</properties>
|
</properties>
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@@ -57,7 +58,7 @@
|
|||||||
<repository>
|
<repository>
|
||||||
<id>spring-snapshots</id>
|
<id>spring-snapshots</id>
|
||||||
<name>Spring Snapshots</name>
|
<name>Spring Snapshots</name>
|
||||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
<url>https://repo.spring.io/snapshot</url>
|
||||||
<snapshots>
|
<snapshots>
|
||||||
<enabled>true</enabled>
|
<enabled>true</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
@@ -68,7 +69,7 @@
|
|||||||
<repository>
|
<repository>
|
||||||
<id>spring-milestones</id>
|
<id>spring-milestones</id>
|
||||||
<name>Spring Milestones</name>
|
<name>Spring Milestones</name>
|
||||||
<url>https://repo.spring.io/libs-milestone-local</url>
|
<url>https://repo.spring.io/milestone</url>
|
||||||
<snapshots>
|
<snapshots>
|
||||||
<enabled>false</enabled>
|
<enabled>false</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
@@ -86,7 +87,7 @@
|
|||||||
<pluginRepository>
|
<pluginRepository>
|
||||||
<id>spring-snapshots</id>
|
<id>spring-snapshots</id>
|
||||||
<name>Spring Snapshots</name>
|
<name>Spring Snapshots</name>
|
||||||
<url>https://repo.spring.io/libs-snapshot-local</url>
|
<url>https://repo.spring.io/snapshot</url>
|
||||||
<snapshots>
|
<snapshots>
|
||||||
<enabled>true</enabled>
|
<enabled>true</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
@@ -97,7 +98,7 @@
|
|||||||
<pluginRepository>
|
<pluginRepository>
|
||||||
<id>spring-milestones</id>
|
<id>spring-milestones</id>
|
||||||
<name>Spring Milestones</name>
|
<name>Spring Milestones</name>
|
||||||
<url>https://repo.spring.io/libs-milestone-local</url>
|
<url>https://repo.spring.io/milestone</url>
|
||||||
<snapshots>
|
<snapshots>
|
||||||
<enabled>false</enabled>
|
<enabled>false</enabled>
|
||||||
</snapshots>
|
</snapshots>
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-openfeign</artifactId>
|
<artifactId>spring-cloud-openfeign</artifactId>
|
||||||
<version>3.1.1-SNAPSHOT</version>
|
<version>3.1.5-SNAPSHOT</version>
|
||||||
<relativePath>..</relativePath>
|
<relativePath>..</relativePath>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
<suppress files=".*ProtobufSpringEncoderTest.*" checks="LineLengthCheck" />
|
<suppress files=".*ProtobufSpringEncoderTest.*" checks="LineLengthCheck" />
|
||||||
<suppress files=".*ProtobufTest.*" checks="LineLengthCheck" />
|
<suppress files=".*ProtobufTest.*" checks="LineLengthCheck" />
|
||||||
<suppress files=".*Hello.*" checks="RedundantModifier"/>
|
<suppress files=".*Hello.*" checks="RedundantModifier"/>
|
||||||
|
<suppress files=".*test.*" checks="JavadocStyle"/>
|
||||||
<!-- Important -->
|
<!-- Important -->
|
||||||
<suppress files=".*HttpEncoding.*" checks="InterfaceIsTypeCheck" />
|
<suppress files=".*HttpEncoding.*" checks="InterfaceIsTypeCheck" />
|
||||||
</suppressions>
|
</suppressions>
|
||||||
|
|||||||
Reference in New Issue
Block a user