Compare commits
111
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e7e74c5bf | ||
|
|
86551041d9 | ||
|
|
35a5b8d614 | ||
|
|
7047439624 | ||
|
|
5e61666f7c | ||
|
|
e3810f1586 | ||
|
|
9bdda59fbb | ||
|
|
e257ec31ff | ||
|
|
246805815f | ||
|
|
15054fa37e | ||
|
|
b231783e84 | ||
|
|
a7c804db63 | ||
|
|
4120ca2bc6 | ||
|
|
29295425f3 | ||
|
|
ef9ded650f | ||
|
|
3c25b010c3 | ||
|
|
f9a8dd13f2 | ||
|
|
f6fdd63cef | ||
|
|
410d0f29a3 | ||
|
|
82de713d06 | ||
|
|
5e455ed14b | ||
|
|
a3584a9cc8 | ||
|
|
ffb17a4552 | ||
|
|
20e2548a5d | ||
|
|
d93defe1c1 | ||
|
|
db8d10a511 | ||
|
|
6649c86be1 | ||
|
|
3f0c2850ee | ||
|
|
479e1ab743 | ||
|
|
77a1367796 | ||
|
|
89f8942dff | ||
|
|
d31ce6d428 | ||
|
|
3c3b9a5a14 | ||
|
|
582e4c0175 | ||
|
|
db3513b97b | ||
|
|
1178739437 | ||
|
|
2d9f51a17f | ||
|
|
faff6aef8a | ||
|
|
80710a9f20 | ||
|
|
d41f041efc | ||
|
|
2f13eb4816 | ||
|
|
62aa76fb97 | ||
|
|
5ce900828f | ||
|
|
48bb0cdba5 | ||
|
|
dc5eef0125 | ||
|
|
343f436b71 | ||
|
|
f184c3f293 | ||
|
|
f600140004 | ||
|
|
d07c229b0e | ||
|
|
84cb74a898 | ||
|
|
9677353c27 | ||
|
|
4766d95def | ||
|
|
bba3f2e92c | ||
|
|
c4fd4eb42e | ||
|
|
b56a4a1259 | ||
|
|
4a4da0e2d1 | ||
|
|
efca091fda | ||
|
|
2e2987e787 | ||
|
|
7cd7ed5c70 | ||
|
|
5a6a75d22b | ||
|
|
a790386732 | ||
|
|
fa5162c2fc | ||
|
|
6d3181e718 | ||
|
|
d0278f63b1 | ||
|
|
39e915e568 | ||
|
|
e03d7da36d | ||
|
|
dcd5a56c86 | ||
|
|
d492fe0742 | ||
|
|
c9fe3af3ef | ||
|
|
afb0b436c0 | ||
|
|
f857cca5b3 | ||
|
|
94ad7066a6 | ||
|
|
1b66594ed4 | ||
|
|
9a335536bc | ||
|
|
6ac428b21c | ||
|
|
30eafcd02d | ||
|
|
147136b85b | ||
|
|
45589b7977 | ||
|
|
f4b49d5542 | ||
|
|
a6b5663c69 | ||
|
|
8f421ced21 | ||
|
|
78653c3e8e | ||
|
|
1f3ee5cfec | ||
|
|
e4a8b0a6a6 | ||
|
|
c87e5f2dbf | ||
|
|
0afb60a72a | ||
|
|
a77bd2bdf1 | ||
|
|
56bee946d8 | ||
|
|
90181f34c0 | ||
|
|
6b9be3f513 | ||
|
|
efe264d2bb | ||
|
|
ffa18c4346 | ||
|
|
d6e1a9f6a1 | ||
|
|
a2e968465a | ||
|
|
6b656195ce | ||
|
|
186711914a | ||
|
|
569dd54ccc | ||
|
|
91895278bf | ||
|
|
8683a04048 | ||
|
|
7c531d9dae | ||
|
|
487390fc20 | ||
|
|
c187ff8792 | ||
|
|
826c9e9699 | ||
|
|
87ebb3cbdf | ||
|
|
14a43f976d | ||
|
|
08f0fe4ae1 | ||
|
|
8a08e1ec4b | ||
|
|
bf1a5e6d33 | ||
|
|
5560bc19be | ||
|
|
36fd5d7755 | ||
|
|
3b191bcb9e |
+8
-2
@@ -6,10 +6,10 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-openfeign</artifactId>
|
<artifactId>spring-cloud-openfeign</artifactId>
|
||||||
<version>2.2.7.BUILD-SNAPSHOT</version>
|
<version>3.0.0-RC1</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>spring-cloud-openfeign-docs</artifactId>
|
<artifactId>spring-cloud-openfeign-docs</artifactId>
|
||||||
<packaging>pom</packaging>
|
<packaging>jar</packaging>
|
||||||
<name>Spring Cloud OpenFeign Docs</name>
|
<name>Spring Cloud OpenFeign Docs</name>
|
||||||
<description>Spring Cloud Docs</description>
|
<description>Spring Cloud Docs</description>
|
||||||
<properties>
|
<properties>
|
||||||
@@ -24,6 +24,9 @@
|
|||||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<sourceDirectory>src/main/asciidoc</sourceDirectory>
|
||||||
|
</build>
|
||||||
<profiles>
|
<profiles>
|
||||||
<profile>
|
<profile>
|
||||||
<id>docs</id>
|
<id>docs</id>
|
||||||
@@ -34,9 +37,11 @@
|
|||||||
<artifactId>git-commit-id-plugin</artifactId>
|
<artifactId>git-commit-id-plugin</artifactId>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-resources-plugin</artifactId>
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
@@ -48,6 +53,7 @@
|
|||||||
<artifactId>asciidoctor-maven-plugin</artifactId>
|
<artifactId>asciidoctor-maven-plugin</artifactId>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-antrun-plugin</artifactId>
|
<artifactId>maven-antrun-plugin</artifactId>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
|||||||
@@ -2,24 +2,24 @@
|
|||||||
|Name | Default | Description
|
|Name | Default | Description
|
||||||
|
|
||||||
|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.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.compression.response.useGzipDecoder | false | Enables the default gzip decoder to be used.
|
|feign.compression.response.useGzipDecoder | `false` | Enables the default gzip decoder to be used.
|
||||||
|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.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.time-to-live | `900` |
|
||||||
|feign.httpclient.time-to-live-unit | |
|
|feign.httpclient.time-to-live-unit | |
|
||||||
|feign.hystrix.enabled | false | If true, an OpenFeign client will be wrapped with a Hystrix circuit breaker.
|
|feign.hystrix.enabled | `false` | If true, an OpenFeign client will be wrapped with a Hystrix circuit breaker.
|
||||||
|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.
|
||||||
|
|
||||||
|===
|
|===
|
||||||
@@ -15,7 +15,7 @@ To use Feign create an interface and annotate it.
|
|||||||
It has pluggable annotation support including Feign annotations and JAX-RS annotations.
|
It has pluggable annotation support including Feign annotations and JAX-RS annotations.
|
||||||
Feign also supports pluggable encoders and decoders.
|
Feign also supports pluggable encoders and decoders.
|
||||||
Spring Cloud adds support for Spring MVC annotations and for using the same `HttpMessageConverters` used by default in Spring Web.
|
Spring Cloud adds support for Spring MVC annotations and for using the same `HttpMessageConverters` used by default in Spring Web.
|
||||||
Spring Cloud integrates Ribbon and Eureka, as well as Spring Cloud LoadBalancer to provide a load-balanced http client when using Feign.
|
Spring Cloud integrates Eureka, as well as Spring Cloud LoadBalancer to provide a load-balanced http client when using Feign.
|
||||||
|
|
||||||
[[netflix-feign-starter]]
|
[[netflix-feign-starter]]
|
||||||
=== How to Include Feign
|
=== How to Include Feign
|
||||||
@@ -55,7 +55,7 @@ public interface StoreClient {
|
|||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
In the `@FeignClient` annotation the String value ("stores" above) is an arbitrary client name, which is used to create either a https://github.com/Netflix/ribbon[Ribbon] load-balancer (see <<spring-cloud-ribbon,below for details of Ribbon support>>) or https://github.com/spring-cloud/spring-cloud-commons/blob/master/spring-cloud-commons/src/main/java/org/springframework/cloud/client/loadbalancer/reactive/ReactiveLoadBalancer.java[Spring Cloud LoadBalancer].
|
In the `@FeignClient` annotation the String value ("stores" above) is an arbitrary client name, which is used to create a https://github.com/spring-cloud/spring-cloud-commons/blob/master/spring-cloud-loadbalancer/src/main/java/org/springframework/cloud/loadbalancer/blocking/client/BlockingLoadBalancerClient.java[Spring Cloud LoadBalancer client].
|
||||||
You can also specify a URL using the `url` attribute
|
You can also specify a URL using the `url` attribute
|
||||||
(absolute value or just a hostname). The name of the bean in the
|
(absolute value or just a hostname). The name of the bean in the
|
||||||
application context is the fully qualified name of the interface.
|
application context is the fully qualified name of the interface.
|
||||||
@@ -68,9 +68,7 @@ it will resolve the service in the Eureka service registry. If you
|
|||||||
don't want to use Eureka, you can simply configure a list of servers
|
don't want to use Eureka, you can simply configure a list of servers
|
||||||
in your external configuration using https://cloud.spring.io/spring-cloud-static/spring-cloud-commons/current/reference/html/#simplediscoveryclient[`SimpleDiscoveryClient`].
|
in your external configuration using https://cloud.spring.io/spring-cloud-static/spring-cloud-commons/current/reference/html/#simplediscoveryclient[`SimpleDiscoveryClient`].
|
||||||
|
|
||||||
WARNING: In order to maintain backward compatibility, is used as the default load-balancer implementation.
|
Spring Cloud OpenFeign supports all the features available for the blocking mode of Spring Cloud LoadBalancer. You can read more about them in the https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#spring-cloud-loadbalancer[project documentation].
|
||||||
However, Spring Cloud Netflix Ribbon is now in maintenance mode, so we recommend using Spring Cloud LoadBalancer instead.
|
|
||||||
To do this, set the value of `spring.cloud.loadbalancer.ribbon.enabled` to `false`.
|
|
||||||
|
|
||||||
[[spring-cloud-feign-overriding-defaults]]
|
[[spring-cloud-feign-overriding-defaults]]
|
||||||
=== Overriding Feign Defaults
|
=== Overriding Feign Defaults
|
||||||
@@ -94,8 +92,6 @@ In this case the client is composed from the components already in `FeignClients
|
|||||||
|
|
||||||
NOTE: `FooConfiguration` does not need to be annotated with `@Configuration`. However, if it is, then take care to exclude it from any `@ComponentScan` that would otherwise include this configuration as it will become the default source for `feign.Decoder`, `feign.Encoder`, `feign.Contract`, etc., when specified. This can be avoided by putting it in a separate, non-overlapping package from any `@ComponentScan` or `@SpringBootApplication`, or it can be explicitly excluded in `@ComponentScan`.
|
NOTE: `FooConfiguration` does not need to be annotated with `@Configuration`. However, if it is, then take care to exclude it from any `@ComponentScan` that would otherwise include this configuration as it will become the default source for `feign.Decoder`, `feign.Encoder`, `feign.Contract`, etc., when specified. This can be avoided by putting it in a separate, non-overlapping package from any `@ComponentScan` or `@SpringBootApplication`, or it can be explicitly excluded in `@ComponentScan`.
|
||||||
|
|
||||||
NOTE: The `serviceId` attribute is now deprecated in favor of the `name` attribute.
|
|
||||||
|
|
||||||
NOTE: Using `contextId` attribute of the `@FeignClient` annotation in addition to changing the name of
|
NOTE: Using `contextId` attribute of the `@FeignClient` annotation in addition to changing the name of
|
||||||
the `ApplicationContext` ensemble, it will override the alias of the client name
|
the `ApplicationContext` ensemble, it will override the alias of the client name
|
||||||
and it will be used as part of the name of the configuration bean created for that client.
|
and it will be used as part of the name of the configuration bean created for that client.
|
||||||
@@ -119,10 +115,10 @@ Spring Cloud OpenFeign provides the following beans by default for feign (`BeanT
|
|||||||
* `Logger` feignLogger: `Slf4jLogger`
|
* `Logger` feignLogger: `Slf4jLogger`
|
||||||
* `Contract` feignContract: `SpringMvcContract`
|
* `Contract` feignContract: `SpringMvcContract`
|
||||||
* `Feign.Builder` feignBuilder: `HystrixFeign.Builder`
|
* `Feign.Builder` feignBuilder: `HystrixFeign.Builder`
|
||||||
* `Client` feignClient: if Ribbon is in the classpath and is enabled it is a `LoadBalancerFeignClient`, otherwise if Spring Cloud LoadBalancer is in the classpath, `FeignBlockingLoadBalancerClient` is used.
|
* `Client` feignClient: if Spring Cloud LoadBalancer is in the classpath, `FeignBlockingLoadBalancerClient` is used.
|
||||||
If none of them is in the classpath, the default feign client is used.
|
If none of them is in the classpath, the default feign client is used.
|
||||||
|
|
||||||
NOTE: `spring-cloud-starter-openfeign` supports both `spring-cloud-starter-netflix-ribbon` and `spring-cloud-starter-loadbalancer`. However, as they are optional dependencies, you need to make sure the one you want to use has been added to your project.
|
NOTE: `spring-cloud-starter-openfeign` supports `spring-cloud-starter-loadbalancer`. However, as is an optional dependency, you need to make sure it been added to your project if you want to use it.
|
||||||
|
|
||||||
The OkHttpClient and ApacheHttpClient feign clients can be used by setting `feign.okhttp.enabled` or `feign.httpclient.enabled` to `true`, respectively, and having them on the classpath.
|
The OkHttpClient and ApacheHttpClient feign clients can be used by setting `feign.okhttp.enabled` or `feign.httpclient.enabled` to `true`, respectively, and having them on the classpath.
|
||||||
You can customize the HTTP client used by providing a bean of either `org.apache.http.impl.client.CloseableHttpClient` when using Apache or `okhttp3.OkHttpClient` when using OK HTTP.
|
You can customize the HTTP client used by providing a bean of either `org.apache.http.impl.client.CloseableHttpClient` when using Apache or `okhttp3.OkHttpClient` when using OK HTTP.
|
||||||
@@ -286,32 +282,6 @@ We can configure timeouts on both the default and the named client. OpenFeign wo
|
|||||||
|
|
||||||
NOTE: In case the server is not running or available a packet results in _connection refused_. The communication ends either with an error message or in a fallback. This can happen _before_ the `connectTimeout` if it is set very low. The time taken to perform a lookup and to receive such a packet causes a significant part of this delay. It is subject to change based on the remote host that involves a DNS lookup.
|
NOTE: In case the server is not running or available a packet results in _connection refused_. The communication ends either with an error message or in a fallback. This can happen _before_ the `connectTimeout` if it is set very low. The time taken to perform a lookup and to receive such a packet causes a significant part of this delay. It is subject to change based on the remote host that involves a DNS lookup.
|
||||||
|
|
||||||
When Hystrix <<spring-cloud-openfeign#spring-cloud-feign-hystrix,is enabled>>, its timeout configuration link:https://github.com/Netflix/Hystrix/wiki/Configuration#execution.isolation.thread.timeoutInMilliseconds[defaults] to 1000 milliseconds. Hence, it might occur before the client timeout that we configured earlier. Increasing this timeout prevents it from happening.
|
|
||||||
|
|
||||||
[source,yaml]
|
|
||||||
----
|
|
||||||
feign:
|
|
||||||
client:
|
|
||||||
config:
|
|
||||||
default:
|
|
||||||
connectTimeout: 5000
|
|
||||||
readTimeout: 5000
|
|
||||||
hystrix:
|
|
||||||
enabled: true
|
|
||||||
|
|
||||||
hystrix:
|
|
||||||
command:
|
|
||||||
default:
|
|
||||||
execution:
|
|
||||||
timeout:
|
|
||||||
enabled: true
|
|
||||||
isolation:
|
|
||||||
thread:
|
|
||||||
timeoutInMilliseconds: 60000
|
|
||||||
----
|
|
||||||
|
|
||||||
NOTE: When the Hystrix timeout is enabled and its timeout is set longer than that of a feign client, `HystrixTimeoutException` wraps a feign exception. Otherwise, the only difference is the cause of the exception. The purpose of `HystrixTimeoutException` is to wrap any runtime exception that occurs first and throw an instance of itself.
|
|
||||||
|
|
||||||
=== Creating Feign Clients Manually
|
=== Creating Feign Clients Manually
|
||||||
|
|
||||||
In some cases it might be necessary to customize your Feign Clients in a way that is not
|
In some cases it might be necessary to customize your Feign Clients in a way that is not
|
||||||
|
|||||||
@@ -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>2.2.7.BUILD-SNAPSHOT</version>
|
<version>3.0.0-RC1</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>2.3.1.RELEASE</version>
|
<version>3.0.0-RC1</version>
|
||||||
<relativePath/>
|
<relativePath/>
|
||||||
</parent>
|
</parent>
|
||||||
<scm>
|
<scm>
|
||||||
@@ -26,8 +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>2.2.7.BUILD-SNAPSHOT</spring-cloud-commons.version>
|
<spring-cloud-commons.version>3.0.0-RC1</spring-cloud-commons.version>
|
||||||
<spring-cloud-netflix.version>2.2.7.BUILD-SNAPSHOT</spring-cloud-netflix.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>
|
||||||
@@ -94,13 +93,6 @@
|
|||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.cloud</groupId>
|
|
||||||
<artifactId>spring-cloud-netflix-dependencies</artifactId>
|
|
||||||
<version>${spring-cloud-netflix.version}</version>
|
|
||||||
<type>pom</type>
|
|
||||||
<scope>import</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-commons-dependencies</artifactId>
|
<artifactId>spring-cloud-commons-dependencies</artifactId>
|
||||||
|
|||||||
@@ -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>2.2.7.BUILD-SNAPSHOT</version>
|
<version>3.0.0-RC1</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>
|
||||||
@@ -21,10 +21,6 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.cloud</groupId>
|
|
||||||
<artifactId>spring-cloud-netflix-ribbon</artifactId>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
@@ -89,11 +85,6 @@
|
|||||||
<artifactId>spring-cloud-context</artifactId>
|
<artifactId>spring-cloud-context</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.netflix.ribbon</groupId>
|
|
||||||
<artifactId>ribbon-loadbalancer</artifactId>
|
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.github.openfeign</groupId>
|
<groupId>io.github.openfeign</groupId>
|
||||||
<artifactId>feign-core</artifactId>
|
<artifactId>feign-core</artifactId>
|
||||||
@@ -113,46 +104,11 @@
|
|||||||
<artifactId>feign-httpclient</artifactId>
|
<artifactId>feign-httpclient</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>io.github.openfeign</groupId>
|
|
||||||
<artifactId>feign-hystrix</artifactId>
|
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.github.openfeign</groupId>
|
<groupId>io.github.openfeign</groupId>
|
||||||
<artifactId>feign-okhttp</artifactId>
|
<artifactId>feign-okhttp</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.netflix.hystrix</groupId>
|
|
||||||
<artifactId>hystrix-core</artifactId>
|
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.netflix.hystrix</groupId>
|
|
||||||
<artifactId>hystrix-serialization</artifactId>
|
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.netflix.hystrix</groupId>
|
|
||||||
<artifactId>hystrix-metrics-event-stream</artifactId>
|
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.netflix.hystrix</groupId>
|
|
||||||
<artifactId>hystrix-javanica</artifactId>
|
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.netflix.ribbon</groupId>
|
|
||||||
<artifactId>ribbon-core</artifactId>
|
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.netflix.ribbon</groupId>
|
|
||||||
<artifactId>ribbon-httpclient</artifactId>
|
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.reactivex</groupId>
|
<groupId>io.reactivex</groupId>
|
||||||
<artifactId>rxjava</artifactId>
|
<artifactId>rxjava</artifactId>
|
||||||
@@ -178,6 +134,11 @@
|
|||||||
<artifactId>spring-boot-starter-test</artifactId>
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.vintage</groupId>
|
||||||
|
<artifactId>junit-vintage-engine</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-test-support</artifactId>
|
<artifactId>spring-cloud-test-support</artifactId>
|
||||||
@@ -193,16 +154,6 @@
|
|||||||
<artifactId>reactor-test</artifactId>
|
<artifactId>reactor-test</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.netflix.ribbon</groupId>
|
|
||||||
<artifactId>ribbon</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.cloud</groupId>
|
|
||||||
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-starter-data-rest</artifactId>
|
<artifactId>spring-boot-starter-data-rest</artifactId>
|
||||||
|
|||||||
+1
-2
@@ -43,8 +43,7 @@ public interface AnnotatedParameterProcessor {
|
|||||||
* @param method the method that contains the annotation
|
* @param method the method that contains the annotation
|
||||||
* @return whether the parameter is http
|
* @return whether the parameter is http
|
||||||
*/
|
*/
|
||||||
boolean processArgument(AnnotatedParameterContext context, Annotation annotation,
|
boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method);
|
||||||
Method method);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the parameter context.
|
* Specifies the parameter context.
|
||||||
|
|||||||
+2
-2
@@ -25,8 +25,8 @@ import feign.Target;
|
|||||||
class DefaultTargeter implements Targeter {
|
class DefaultTargeter implements Targeter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
|
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
|
||||||
FeignContext context, Target.HardCodedTarget<T> target) {
|
Target.HardCodedTarget<T> target) {
|
||||||
return feign.target(target);
|
return feign.target(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-38
@@ -41,7 +41,6 @@ import org.apache.http.impl.client.CloseableHttpClient;
|
|||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.cloud.client.actuator.HasFeatures;
|
import org.springframework.cloud.client.actuator.HasFeatures;
|
||||||
@@ -61,8 +60,7 @@ import org.springframework.context.annotation.Import;
|
|||||||
*/
|
*/
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@ConditionalOnClass(Feign.class)
|
@ConditionalOnClass(Feign.class)
|
||||||
@EnableConfigurationProperties({ FeignClientProperties.class,
|
@EnableConfigurationProperties({ FeignClientProperties.class, FeignHttpClientProperties.class })
|
||||||
FeignHttpClientProperties.class })
|
|
||||||
@Import(DefaultGzipDecoderConfiguration.class)
|
@Import(DefaultGzipDecoderConfiguration.class)
|
||||||
public class FeignAutoConfiguration {
|
public class FeignAutoConfiguration {
|
||||||
|
|
||||||
@@ -84,19 +82,6 @@ public class FeignAutoConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
|
|
||||||
protected static class HystrixFeignTargeterConfiguration {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean
|
|
||||||
public Targeter feignTargeter() {
|
|
||||||
return new HystrixTargeter();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
|
||||||
@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
|
|
||||||
protected static class DefaultFeignTargeterConfiguration {
|
protected static class DefaultFeignTargeterConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@@ -108,12 +93,11 @@ public class FeignAutoConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// the following configuration is for alternate feign clients if
|
// the following configuration is for alternate feign clients if
|
||||||
// ribbon is not on the class path.
|
// SC loadbalancer is not on the class path.
|
||||||
// see corresponding configurations in FeignRibbonClientAutoConfiguration
|
// see corresponding configurations in FeignRibbonClientAutoConfiguration
|
||||||
// for load balanced ribbon clients.
|
// for load-balanced clients.
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@ConditionalOnClass(ApacheHttpClient.class)
|
@ConditionalOnClass(ApacheHttpClient.class)
|
||||||
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
|
|
||||||
@ConditionalOnMissingBean(CloseableHttpClient.class)
|
@ConditionalOnMissingBean(CloseableHttpClient.class)
|
||||||
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
|
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
|
||||||
protected static class HttpClientFeignConfiguration {
|
protected static class HttpClientFeignConfiguration {
|
||||||
@@ -131,13 +115,10 @@ public class FeignAutoConfiguration {
|
|||||||
public HttpClientConnectionManager connectionManager(
|
public HttpClientConnectionManager connectionManager(
|
||||||
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
|
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
|
||||||
FeignHttpClientProperties httpClientProperties) {
|
FeignHttpClientProperties httpClientProperties) {
|
||||||
final HttpClientConnectionManager connectionManager = connectionManagerFactory
|
final HttpClientConnectionManager connectionManager = connectionManagerFactory.newConnectionManager(
|
||||||
.newConnectionManager(httpClientProperties.isDisableSslValidation(),
|
httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(),
|
||||||
httpClientProperties.getMaxConnections(),
|
httpClientProperties.getMaxConnectionsPerRoute(), httpClientProperties.getTimeToLive(),
|
||||||
httpClientProperties.getMaxConnectionsPerRoute(),
|
httpClientProperties.getTimeToLiveUnit(), this.registryBuilder);
|
||||||
httpClientProperties.getTimeToLive(),
|
|
||||||
httpClientProperties.getTimeToLiveUnit(),
|
|
||||||
this.registryBuilder);
|
|
||||||
this.connectionManagerTimer.schedule(new TimerTask() {
|
this.connectionManagerTimer.schedule(new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -153,10 +134,8 @@ public class FeignAutoConfiguration {
|
|||||||
FeignHttpClientProperties httpClientProperties) {
|
FeignHttpClientProperties httpClientProperties) {
|
||||||
RequestConfig defaultRequestConfig = RequestConfig.custom()
|
RequestConfig defaultRequestConfig = RequestConfig.custom()
|
||||||
.setConnectTimeout(httpClientProperties.getConnectionTimeout())
|
.setConnectTimeout(httpClientProperties.getConnectionTimeout())
|
||||||
.setRedirectsEnabled(httpClientProperties.isFollowRedirects())
|
.setRedirectsEnabled(httpClientProperties.isFollowRedirects()).build();
|
||||||
.build();
|
this.httpClient = httpClientFactory.createBuilder().setConnectionManager(httpClientConnectionManager)
|
||||||
this.httpClient = httpClientFactory.createBuilder()
|
|
||||||
.setConnectionManager(httpClientConnectionManager)
|
|
||||||
.setDefaultRequestConfig(defaultRequestConfig).build();
|
.setDefaultRequestConfig(defaultRequestConfig).build();
|
||||||
return this.httpClient;
|
return this.httpClient;
|
||||||
}
|
}
|
||||||
@@ -186,7 +165,6 @@ public class FeignAutoConfiguration {
|
|||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@ConditionalOnClass(OkHttpClient.class)
|
@ConditionalOnClass(OkHttpClient.class)
|
||||||
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
|
|
||||||
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
|
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
|
||||||
@ConditionalOnProperty("feign.okhttp.enabled")
|
@ConditionalOnProperty("feign.okhttp.enabled")
|
||||||
protected static class OkHttpFeignConfiguration {
|
protected static class OkHttpFeignConfiguration {
|
||||||
@@ -195,8 +173,7 @@ public class FeignAutoConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean(ConnectionPool.class)
|
@ConditionalOnMissingBean(ConnectionPool.class)
|
||||||
public ConnectionPool httpClientConnectionPool(
|
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
|
||||||
FeignHttpClientProperties httpClientProperties,
|
|
||||||
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
|
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
|
||||||
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
|
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
|
||||||
Long timeToLive = httpClientProperties.getTimeToLive();
|
Long timeToLive = httpClientProperties.getTimeToLive();
|
||||||
@@ -205,16 +182,14 @@ public class FeignAutoConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
|
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool,
|
||||||
ConnectionPool connectionPool,
|
|
||||||
FeignHttpClientProperties httpClientProperties) {
|
FeignHttpClientProperties httpClientProperties) {
|
||||||
Boolean followRedirects = httpClientProperties.isFollowRedirects();
|
Boolean followRedirects = httpClientProperties.isFollowRedirects();
|
||||||
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
|
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
|
||||||
Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
|
Boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
|
||||||
this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation)
|
this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation)
|
||||||
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
|
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).followRedirects(followRedirects)
|
||||||
.followRedirects(followRedirects).connectionPool(connectionPool)
|
.connectionPool(connectionPool).build();
|
||||||
.build();
|
|
||||||
return this.okHttpClient;
|
return this.okHttpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2019 the original author or authors.
|
* Copyright 2013-2020 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|||||||
+4
-13
@@ -27,9 +27,9 @@ import org.springframework.core.annotation.AliasFor;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Annotation for interfaces declaring that a REST client with that interface should be
|
* Annotation for interfaces declaring that a REST client with that interface should be
|
||||||
* created (e.g. for autowiring into another component). If ribbon is available it will be
|
* created (e.g. for autowiring into another component). If SC LoadBalancer is available
|
||||||
* used to load balance the backend requests, and the load balancer can be configured
|
* it will be used to load balance the backend requests, and the load balancer can be
|
||||||
* using a <code>@RibbonClient</code> with the same name (i.e. value) as the feign client.
|
* configured using the same name (i.e. value) as the feign client.
|
||||||
*
|
*
|
||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
* @author Venil Noronha
|
* @author Venil Noronha
|
||||||
@@ -49,14 +49,6 @@ public @interface FeignClient {
|
|||||||
@AliasFor("name")
|
@AliasFor("name")
|
||||||
String value() default "";
|
String value() default "";
|
||||||
|
|
||||||
/**
|
|
||||||
* The service id with optional protocol prefix. Synonym for {@link #value() value}.
|
|
||||||
* @deprecated use {@link #name() name} instead
|
|
||||||
* @return the service id with optional protocol prefix
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
String serviceId() default "";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This will be used as the bean name instead of name if present, but will not be used
|
* This will be used as the bean name instead of name if present, but will not be used
|
||||||
* as a service id.
|
* as a service id.
|
||||||
@@ -114,8 +106,7 @@ public @interface FeignClient {
|
|||||||
Class<?> fallbackFactory() default void.class;
|
Class<?> fallbackFactory() default void.class;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return path prefix to be used by all method-level mappings. Can be used with or
|
* @return path prefix to be used by all method-level mappings.
|
||||||
* without <code>@RibbonClient</code>.
|
|
||||||
*/
|
*/
|
||||||
String path() default "";
|
String path() default "";
|
||||||
|
|
||||||
|
|||||||
+12
-12
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
package org.springframework.cloud.openfeign;
|
||||||
|
|
||||||
import feign.hystrix.FallbackFactory;
|
|
||||||
|
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -41,6 +39,11 @@ public class FeignClientBuilder {
|
|||||||
return new Builder<>(this.applicationContext, type, name);
|
return new Builder<>(this.applicationContext, type, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <T> Builder<T> forType(final Class<T> type, final FeignClientFactoryBean clientFactoryBean,
|
||||||
|
final String name) {
|
||||||
|
return new Builder<>(this.applicationContext, clientFactoryBean, type, name);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builder of feign targets.
|
* Builder of feign targets.
|
||||||
*
|
*
|
||||||
@@ -50,9 +53,13 @@ public class FeignClientBuilder {
|
|||||||
|
|
||||||
private FeignClientFactoryBean feignClientFactoryBean;
|
private FeignClientFactoryBean feignClientFactoryBean;
|
||||||
|
|
||||||
private Builder(final ApplicationContext applicationContext, final Class<T> type,
|
private Builder(final ApplicationContext applicationContext, final Class<T> type, final String name) {
|
||||||
final String name) {
|
this(applicationContext, new FeignClientFactoryBean(), type, name);
|
||||||
this.feignClientFactoryBean = new FeignClientFactoryBean();
|
}
|
||||||
|
|
||||||
|
private Builder(final ApplicationContext applicationContext, final FeignClientFactoryBean clientFactoryBean,
|
||||||
|
final Class<T> type, final String name) {
|
||||||
|
this.feignClientFactoryBean = clientFactoryBean;
|
||||||
|
|
||||||
this.feignClientFactoryBean.setApplicationContext(applicationContext);
|
this.feignClientFactoryBean.setApplicationContext(applicationContext);
|
||||||
this.feignClientFactoryBean.setType(type);
|
this.feignClientFactoryBean.setType(type);
|
||||||
@@ -95,13 +102,6 @@ public class FeignClientBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder<T> fallbackFactory(
|
|
||||||
final Class<? extends FallbackFactory<? extends T>> fallbackFactory) {
|
|
||||||
FeignClientsRegistrar.validateFallbackFactory(fallbackFactory);
|
|
||||||
this.feignClientFactoryBean.setFallbackFactory(fallbackFactory);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the created Feign client
|
* @return the created Feign client
|
||||||
*/
|
*/
|
||||||
|
|||||||
+46
-84
@@ -43,7 +43,6 @@ import org.springframework.beans.factory.InitializingBean;
|
|||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
import org.springframework.cloud.openfeign.clientconfig.FeignClientConfigurer;
|
import org.springframework.cloud.openfeign.clientconfig.FeignClientConfigurer;
|
||||||
import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
|
import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
|
||||||
import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;
|
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ApplicationContextAware;
|
import org.springframework.context.ApplicationContextAware;
|
||||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||||
@@ -59,8 +58,7 @@ import org.springframework.util.StringUtils;
|
|||||||
* @author Olga Maciaszek-Sharma
|
* @author Olga Maciaszek-Sharma
|
||||||
* @author Ilia Ilinykh
|
* @author Ilia Ilinykh
|
||||||
*/
|
*/
|
||||||
class FeignClientFactoryBean
|
public class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
|
||||||
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
|
|
||||||
|
|
||||||
/***********************************
|
/***********************************
|
||||||
* WARNING! Nothing in this class should be @Autowired. It causes NPEs because of some
|
* WARNING! Nothing in this class should be @Autowired. It causes NPEs because of some
|
||||||
@@ -117,37 +115,29 @@ class FeignClientFactoryBean
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void applyBuildCustomizers(FeignContext context, Feign.Builder builder) {
|
private void applyBuildCustomizers(FeignContext context, Feign.Builder builder) {
|
||||||
Map<String, FeignBuilderCustomizer> customizerMap = context
|
Map<String, FeignBuilderCustomizer> customizerMap = context.getInstances(contextId,
|
||||||
.getInstances(contextId, FeignBuilderCustomizer.class);
|
FeignBuilderCustomizer.class);
|
||||||
|
|
||||||
if (customizerMap != null) {
|
if (customizerMap != null) {
|
||||||
customizerMap.values().stream()
|
customizerMap.values().stream().sorted(AnnotationAwareOrderComparator.INSTANCE)
|
||||||
.sorted(AnnotationAwareOrderComparator.INSTANCE)
|
.forEach(feignBuilderCustomizer -> feignBuilderCustomizer.customize(builder));
|
||||||
.forEach(feignBuilderCustomizer -> feignBuilderCustomizer
|
|
||||||
.customize(builder));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void configureFeign(FeignContext context, Feign.Builder builder) {
|
protected void configureFeign(FeignContext context, Feign.Builder builder) {
|
||||||
FeignClientProperties properties = applicationContext
|
FeignClientProperties properties = applicationContext.getBean(FeignClientProperties.class);
|
||||||
.getBean(FeignClientProperties.class);
|
|
||||||
|
|
||||||
FeignClientConfigurer feignClientConfigurer = getOptional(context,
|
FeignClientConfigurer feignClientConfigurer = getOptional(context, FeignClientConfigurer.class);
|
||||||
FeignClientConfigurer.class);
|
|
||||||
setInheritParentContext(feignClientConfigurer.inheritParentConfiguration());
|
setInheritParentContext(feignClientConfigurer.inheritParentConfiguration());
|
||||||
|
|
||||||
if (properties != null && inheritParentContext) {
|
if (properties != null && inheritParentContext) {
|
||||||
if (properties.isDefaultToProperties()) {
|
if (properties.isDefaultToProperties()) {
|
||||||
configureUsingConfiguration(context, builder);
|
configureUsingConfiguration(context, builder);
|
||||||
configureUsingProperties(
|
configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
|
||||||
properties.getConfig().get(properties.getDefaultConfig()),
|
|
||||||
builder);
|
|
||||||
configureUsingProperties(properties.getConfig().get(contextId), builder);
|
configureUsingProperties(properties.getConfig().get(contextId), builder);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
configureUsingProperties(
|
configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
|
||||||
properties.getConfig().get(properties.getDefaultConfig()),
|
|
||||||
builder);
|
|
||||||
configureUsingProperties(properties.getConfig().get(contextId), builder);
|
configureUsingProperties(properties.getConfig().get(contextId), builder);
|
||||||
configureUsingConfiguration(context, builder);
|
configureUsingConfiguration(context, builder);
|
||||||
}
|
}
|
||||||
@@ -157,8 +147,7 @@ class FeignClientFactoryBean
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void configureUsingConfiguration(FeignContext context,
|
protected void configureUsingConfiguration(FeignContext context, Feign.Builder builder) {
|
||||||
Feign.Builder builder) {
|
|
||||||
Logger.Level level = getInheritedAwareOptional(context, Logger.Level.class);
|
Logger.Level level = getInheritedAwareOptional(context, Logger.Level.class);
|
||||||
if (level != null) {
|
if (level != null) {
|
||||||
builder.logLevel(level);
|
builder.logLevel(level);
|
||||||
@@ -167,51 +156,45 @@ class FeignClientFactoryBean
|
|||||||
if (retryer != null) {
|
if (retryer != null) {
|
||||||
builder.retryer(retryer);
|
builder.retryer(retryer);
|
||||||
}
|
}
|
||||||
ErrorDecoder errorDecoder = getInheritedAwareOptional(context,
|
ErrorDecoder errorDecoder = getInheritedAwareOptional(context, ErrorDecoder.class);
|
||||||
ErrorDecoder.class);
|
|
||||||
if (errorDecoder != null) {
|
if (errorDecoder != null) {
|
||||||
builder.errorDecoder(errorDecoder);
|
builder.errorDecoder(errorDecoder);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
FeignErrorDecoderFactory errorDecoderFactory = getOptional(context,
|
FeignErrorDecoderFactory errorDecoderFactory = getOptional(context, FeignErrorDecoderFactory.class);
|
||||||
FeignErrorDecoderFactory.class);
|
|
||||||
if (errorDecoderFactory != null) {
|
if (errorDecoderFactory != null) {
|
||||||
ErrorDecoder factoryErrorDecoder = errorDecoderFactory.create(type);
|
ErrorDecoder factoryErrorDecoder = errorDecoderFactory.create(type);
|
||||||
builder.errorDecoder(factoryErrorDecoder);
|
builder.errorDecoder(factoryErrorDecoder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Request.Options options = getInheritedAwareOptional(context,
|
Request.Options options = getInheritedAwareOptional(context, Request.Options.class);
|
||||||
Request.Options.class);
|
|
||||||
if (options != null) {
|
if (options != null) {
|
||||||
builder.options(options);
|
builder.options(options);
|
||||||
readTimeoutMillis = options.readTimeoutMillis();
|
readTimeoutMillis = options.readTimeoutMillis();
|
||||||
connectTimeoutMillis = options.connectTimeoutMillis();
|
connectTimeoutMillis = options.connectTimeoutMillis();
|
||||||
}
|
}
|
||||||
Map<String, RequestInterceptor> requestInterceptors = getInheritedAwareInstances(
|
Map<String, RequestInterceptor> requestInterceptors = getInheritedAwareInstances(context,
|
||||||
context, RequestInterceptor.class);
|
RequestInterceptor.class);
|
||||||
if (requestInterceptors != null) {
|
if (requestInterceptors != null) {
|
||||||
List<RequestInterceptor> interceptors = new ArrayList<>(
|
List<RequestInterceptor> interceptors = new ArrayList<>(requestInterceptors.values());
|
||||||
requestInterceptors.values());
|
|
||||||
AnnotationAwareOrderComparator.sort(interceptors);
|
AnnotationAwareOrderComparator.sort(interceptors);
|
||||||
builder.requestInterceptors(interceptors);
|
builder.requestInterceptors(interceptors);
|
||||||
}
|
}
|
||||||
QueryMapEncoder queryMapEncoder = getInheritedAwareOptional(context,
|
QueryMapEncoder queryMapEncoder = getInheritedAwareOptional(context, QueryMapEncoder.class);
|
||||||
QueryMapEncoder.class);
|
|
||||||
if (queryMapEncoder != null) {
|
if (queryMapEncoder != null) {
|
||||||
builder.queryMapEncoder(queryMapEncoder);
|
builder.queryMapEncoder(queryMapEncoder);
|
||||||
}
|
}
|
||||||
if (decode404) {
|
if (decode404) {
|
||||||
builder.decode404();
|
builder.decode404();
|
||||||
}
|
}
|
||||||
ExceptionPropagationPolicy exceptionPropagationPolicy = getInheritedAwareOptional(
|
ExceptionPropagationPolicy exceptionPropagationPolicy = getInheritedAwareOptional(context,
|
||||||
context, ExceptionPropagationPolicy.class);
|
ExceptionPropagationPolicy.class);
|
||||||
if (exceptionPropagationPolicy != null) {
|
if (exceptionPropagationPolicy != null) {
|
||||||
builder.exceptionPropagationPolicy(exceptionPropagationPolicy);
|
builder.exceptionPropagationPolicy(exceptionPropagationPolicy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void configureUsingProperties(
|
protected void configureUsingProperties(FeignClientProperties.FeignClientConfiguration config,
|
||||||
FeignClientProperties.FeignClientConfiguration config,
|
|
||||||
Feign.Builder builder) {
|
Feign.Builder builder) {
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
return;
|
return;
|
||||||
@@ -221,13 +204,11 @@ class FeignClientFactoryBean
|
|||||||
builder.logLevel(config.getLoggerLevel());
|
builder.logLevel(config.getLoggerLevel());
|
||||||
}
|
}
|
||||||
|
|
||||||
connectTimeoutMillis = config.getConnectTimeout() != null
|
connectTimeoutMillis = config.getConnectTimeout() != null ? config.getConnectTimeout() : connectTimeoutMillis;
|
||||||
? config.getConnectTimeout() : connectTimeoutMillis;
|
readTimeoutMillis = config.getReadTimeout() != null ? config.getReadTimeout() : readTimeoutMillis;
|
||||||
readTimeoutMillis = config.getReadTimeout() != null ? config.getReadTimeout()
|
|
||||||
: readTimeoutMillis;
|
|
||||||
|
|
||||||
builder.options(new Request.Options(connectTimeoutMillis, TimeUnit.MILLISECONDS,
|
builder.options(new Request.Options(connectTimeoutMillis, TimeUnit.MILLISECONDS, readTimeoutMillis,
|
||||||
readTimeoutMillis, TimeUnit.MILLISECONDS, true));
|
TimeUnit.MILLISECONDS, true));
|
||||||
|
|
||||||
if (config.getRetryer() != null) {
|
if (config.getRetryer() != null) {
|
||||||
Retryer retryer = getOrInstantiate(config.getRetryer());
|
Retryer retryer = getOrInstantiate(config.getRetryer());
|
||||||
@@ -239,8 +220,7 @@ class FeignClientFactoryBean
|
|||||||
builder.errorDecoder(errorDecoder);
|
builder.errorDecoder(errorDecoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.getRequestInterceptors() != null
|
if (config.getRequestInterceptors() != null && !config.getRequestInterceptors().isEmpty()) {
|
||||||
&& !config.getRequestInterceptors().isEmpty()) {
|
|
||||||
// this will add request interceptor to builder, not replace existing
|
// this will add request interceptor to builder, not replace existing
|
||||||
for (Class<RequestInterceptor> bean : config.getRequestInterceptors()) {
|
for (Class<RequestInterceptor> bean : config.getRequestInterceptors()) {
|
||||||
RequestInterceptor interceptor = getOrInstantiate(bean);
|
RequestInterceptor interceptor = getOrInstantiate(bean);
|
||||||
@@ -259,13 +239,11 @@ class FeignClientFactoryBean
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Objects.nonNull(config.getDefaultRequestHeaders())) {
|
if (Objects.nonNull(config.getDefaultRequestHeaders())) {
|
||||||
builder.requestInterceptor(requestTemplate -> requestTemplate
|
builder.requestInterceptor(requestTemplate -> requestTemplate.headers(config.getDefaultRequestHeaders()));
|
||||||
.headers(config.getDefaultRequestHeaders()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Objects.nonNull(config.getDefaultQueryParameters())) {
|
if (Objects.nonNull(config.getDefaultQueryParameters())) {
|
||||||
builder.requestInterceptor(requestTemplate -> requestTemplate
|
builder.requestInterceptor(requestTemplate -> requestTemplate.queries(config.getDefaultQueryParameters()));
|
||||||
.queries(config.getDefaultQueryParameters()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Objects.nonNull(config.getDecoder())) {
|
if (Objects.nonNull(config.getDecoder())) {
|
||||||
@@ -293,8 +271,7 @@ class FeignClientFactoryBean
|
|||||||
protected <T> T get(FeignContext context, Class<T> type) {
|
protected <T> T get(FeignContext context, Class<T> type) {
|
||||||
T instance = context.getInstance(contextId, type);
|
T instance = context.getInstance(contextId, type);
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException("No bean found of type " + type + " for " + contextId);
|
||||||
"No bean found of type " + type + " for " + contextId);
|
|
||||||
}
|
}
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
@@ -312,8 +289,7 @@ class FeignClientFactoryBean
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> Map<String, T> getInheritedAwareInstances(FeignContext context,
|
protected <T> Map<String, T> getInheritedAwareInstances(FeignContext context, Class<T> type) {
|
||||||
Class<T> type) {
|
|
||||||
if (inheritParentContext) {
|
if (inheritParentContext) {
|
||||||
return context.getInstances(contextId, type);
|
return context.getInstances(contextId, type);
|
||||||
}
|
}
|
||||||
@@ -322,8 +298,7 @@ class FeignClientFactoryBean
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
|
protected <T> T loadBalance(Feign.Builder builder, FeignContext context, HardCodedTarget<T> target) {
|
||||||
HardCodedTarget<T> target) {
|
|
||||||
Client client = getOptional(context, Client.class);
|
Client client = getOptional(context, Client.class);
|
||||||
if (client != null) {
|
if (client != null) {
|
||||||
builder.client(client);
|
builder.client(client);
|
||||||
@@ -332,11 +307,11 @@ class FeignClientFactoryBean
|
|||||||
}
|
}
|
||||||
|
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-netflix-ribbon?");
|
"No Feign Client for loadBalancing defined. Did you forget to include spring-cloud-starter-loadbalancer?");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getObject() throws Exception {
|
public Object getObject() {
|
||||||
return getTarget();
|
return getTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,8 +332,7 @@ class FeignClientFactoryBean
|
|||||||
url = name;
|
url = name;
|
||||||
}
|
}
|
||||||
url += cleanPath();
|
url += cleanPath();
|
||||||
return (T) loadBalance(builder, context,
|
return (T) loadBalance(builder, context, new HardCodedTarget<>(type, name, url));
|
||||||
new HardCodedTarget<>(type, name, url));
|
|
||||||
}
|
}
|
||||||
if (StringUtils.hasText(url) && !url.startsWith("http")) {
|
if (StringUtils.hasText(url) && !url.startsWith("http")) {
|
||||||
url = "http://" + url;
|
url = "http://" + url;
|
||||||
@@ -366,11 +340,6 @@ class FeignClientFactoryBean
|
|||||||
String url = this.url + cleanPath();
|
String url = this.url + cleanPath();
|
||||||
Client client = getOptional(context, Client.class);
|
Client client = getOptional(context, Client.class);
|
||||||
if (client != null) {
|
if (client != null) {
|
||||||
if (client instanceof LoadBalancerFeignClient) {
|
|
||||||
// not load balancing because we have a url,
|
|
||||||
// but ribbon is on the classpath, so unwrap
|
|
||||||
client = ((LoadBalancerFeignClient) client).getDelegate();
|
|
||||||
}
|
|
||||||
if (client instanceof FeignBlockingLoadBalancerClient) {
|
if (client instanceof FeignBlockingLoadBalancerClient) {
|
||||||
// not load balancing because we have a url,
|
// not load balancing because we have a url,
|
||||||
// but Spring Cloud LoadBalancer is on the classpath, so unwrap
|
// but Spring Cloud LoadBalancer is on the classpath, so unwrap
|
||||||
@@ -379,8 +348,7 @@ class FeignClientFactoryBean
|
|||||||
builder.client(client);
|
builder.client(client);
|
||||||
}
|
}
|
||||||
Targeter targeter = get(context, Targeter.class);
|
Targeter targeter = get(context, Targeter.class);
|
||||||
return (T) targeter.target(this, builder, context,
|
return (T) targeter.target(this, builder, context, new HardCodedTarget<>(type, name, url));
|
||||||
new HardCodedTarget<>(type, name, url));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String cleanPath() {
|
private String cleanPath() {
|
||||||
@@ -496,32 +464,26 @@ class FeignClientFactoryBean
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
FeignClientFactoryBean that = (FeignClientFactoryBean) o;
|
FeignClientFactoryBean that = (FeignClientFactoryBean) o;
|
||||||
return Objects.equals(applicationContext, that.applicationContext)
|
return Objects.equals(applicationContext, that.applicationContext) && decode404 == that.decode404
|
||||||
&& decode404 == that.decode404
|
&& inheritParentContext == that.inheritParentContext && Objects.equals(fallback, that.fallback)
|
||||||
&& inheritParentContext == that.inheritParentContext
|
&& Objects.equals(fallbackFactory, that.fallbackFactory) && Objects.equals(name, that.name)
|
||||||
&& Objects.equals(fallback, that.fallback)
|
&& Objects.equals(path, that.path) && Objects.equals(type, that.type) && Objects.equals(url, that.url);
|
||||||
&& Objects.equals(fallbackFactory, that.fallbackFactory)
|
|
||||||
&& Objects.equals(name, that.name) && Objects.equals(path, that.path)
|
|
||||||
&& Objects.equals(type, that.type) && Objects.equals(url, that.url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(applicationContext, decode404, inheritParentContext, fallback,
|
return Objects.hash(applicationContext, decode404, inheritParentContext, fallback, fallbackFactory, name, path,
|
||||||
fallbackFactory, name, path, type, url);
|
type, url);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new StringBuilder("FeignClientFactoryBean{").append("type=").append(type)
|
return new StringBuilder("FeignClientFactoryBean{").append("type=").append(type).append(", ").append("name='")
|
||||||
.append(", ").append("name='").append(name).append("', ").append("url='")
|
.append(name).append("', ").append("url='").append(url).append("', ").append("path='").append(path)
|
||||||
.append(url).append("', ").append("path='").append(path).append("', ")
|
.append("', ").append("decode404=").append(decode404).append(", ").append("inheritParentContext=")
|
||||||
.append("decode404=").append(decode404).append(", ")
|
.append(inheritParentContext).append(", ").append("applicationContext=").append(applicationContext)
|
||||||
.append("inheritParentContext=").append(inheritParentContext).append(", ")
|
.append(", ").append("fallback=").append(fallback).append(", ").append("fallbackFactory=")
|
||||||
.append("applicationContext=").append(applicationContext).append(", ")
|
.append(fallbackFactory).append("}").toString();
|
||||||
.append("fallback=").append(fallback).append(", ")
|
|
||||||
.append("fallbackFactory=").append(fallbackFactory).append("}")
|
|
||||||
.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-27
@@ -94,10 +94,8 @@ public class FeignClientProperties {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
FeignClientProperties that = (FeignClientProperties) o;
|
FeignClientProperties that = (FeignClientProperties) o;
|
||||||
return defaultToProperties == that.defaultToProperties
|
return defaultToProperties == that.defaultToProperties && Objects.equals(defaultConfig, that.defaultConfig)
|
||||||
&& Objects.equals(defaultConfig, that.defaultConfig)
|
&& Objects.equals(config, that.config) && Objects.equals(decodeSlash, that.decodeSlash);
|
||||||
&& Objects.equals(config, that.config)
|
|
||||||
&& Objects.equals(decodeSlash, that.decodeSlash);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -180,8 +178,7 @@ public class FeignClientProperties {
|
|||||||
return requestInterceptors;
|
return requestInterceptors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRequestInterceptors(
|
public void setRequestInterceptors(List<Class<RequestInterceptor>> requestInterceptors) {
|
||||||
List<Class<RequestInterceptor>> requestInterceptors) {
|
|
||||||
this.requestInterceptors = requestInterceptors;
|
this.requestInterceptors = requestInterceptors;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,8 +186,7 @@ public class FeignClientProperties {
|
|||||||
return defaultRequestHeaders;
|
return defaultRequestHeaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDefaultRequestHeaders(
|
public void setDefaultRequestHeaders(Map<String, Collection<String>> defaultRequestHeaders) {
|
||||||
Map<String, Collection<String>> defaultRequestHeaders) {
|
|
||||||
this.defaultRequestHeaders = defaultRequestHeaders;
|
this.defaultRequestHeaders = defaultRequestHeaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,8 +194,7 @@ public class FeignClientProperties {
|
|||||||
return defaultQueryParameters;
|
return defaultQueryParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDefaultQueryParameters(
|
public void setDefaultQueryParameters(Map<String, Collection<String>> defaultQueryParameters) {
|
||||||
Map<String, Collection<String>> defaultQueryParameters) {
|
|
||||||
this.defaultQueryParameters = defaultQueryParameters;
|
this.defaultQueryParameters = defaultQueryParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,8 +234,7 @@ public class FeignClientProperties {
|
|||||||
return exceptionPropagationPolicy;
|
return exceptionPropagationPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setExceptionPropagationPolicy(
|
public void setExceptionPropagationPolicy(ExceptionPropagationPolicy exceptionPropagationPolicy) {
|
||||||
ExceptionPropagationPolicy exceptionPropagationPolicy) {
|
|
||||||
this.exceptionPropagationPolicy = exceptionPropagationPolicy;
|
this.exceptionPropagationPolicy = exceptionPropagationPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,28 +247,21 @@ public class FeignClientProperties {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
FeignClientConfiguration that = (FeignClientConfiguration) o;
|
FeignClientConfiguration that = (FeignClientConfiguration) o;
|
||||||
return loggerLevel == that.loggerLevel
|
return loggerLevel == that.loggerLevel && Objects.equals(connectTimeout, that.connectTimeout)
|
||||||
&& Objects.equals(connectTimeout, that.connectTimeout)
|
&& Objects.equals(readTimeout, that.readTimeout) && Objects.equals(retryer, that.retryer)
|
||||||
&& Objects.equals(readTimeout, that.readTimeout)
|
|
||||||
&& Objects.equals(retryer, that.retryer)
|
|
||||||
&& Objects.equals(errorDecoder, that.errorDecoder)
|
&& Objects.equals(errorDecoder, that.errorDecoder)
|
||||||
&& Objects.equals(requestInterceptors, that.requestInterceptors)
|
&& Objects.equals(requestInterceptors, that.requestInterceptors)
|
||||||
&& Objects.equals(decode404, that.decode404)
|
&& Objects.equals(decode404, that.decode404) && Objects.equals(encoder, that.encoder)
|
||||||
&& Objects.equals(encoder, that.encoder)
|
&& Objects.equals(decoder, that.decoder) && Objects.equals(contract, that.contract)
|
||||||
&& Objects.equals(decoder, that.decoder)
|
&& Objects.equals(exceptionPropagationPolicy, that.exceptionPropagationPolicy)
|
||||||
&& Objects.equals(contract, that.contract)
|
|
||||||
&& Objects.equals(exceptionPropagationPolicy,
|
|
||||||
that.exceptionPropagationPolicy)
|
|
||||||
&& Objects.equals(defaultRequestHeaders, that.defaultRequestHeaders)
|
&& Objects.equals(defaultRequestHeaders, that.defaultRequestHeaders)
|
||||||
&& Objects.equals(defaultQueryParameters,
|
&& Objects.equals(defaultQueryParameters, that.defaultQueryParameters);
|
||||||
that.defaultQueryParameters);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(loggerLevel, connectTimeout, readTimeout, retryer,
|
return Objects.hash(loggerLevel, connectTimeout, readTimeout, retryer, errorDecoder, requestInterceptors,
|
||||||
errorDecoder, requestInterceptors, decode404, encoder, decoder,
|
decode404, encoder, decoder, contract, exceptionPropagationPolicy, defaultQueryParameters,
|
||||||
contract, exceptionPropagationPolicy, defaultQueryParameters,
|
|
||||||
defaultRequestHeaders);
|
defaultRequestHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-5
@@ -64,8 +64,7 @@ class FeignClientSpecification implements NamedContextFactory.Specification {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
FeignClientSpecification that = (FeignClientSpecification) o;
|
FeignClientSpecification that = (FeignClientSpecification) o;
|
||||||
return Objects.equals(this.name, that.name)
|
return Objects.equals(this.name, that.name) && Arrays.equals(this.configuration, that.configuration);
|
||||||
&& Arrays.equals(this.configuration, that.configuration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -75,9 +74,8 @@ class FeignClientSpecification implements NamedContextFactory.Specification {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new StringBuilder("FeignClientSpecification{").append("name='")
|
return new StringBuilder("FeignClientSpecification{").append("name='").append(this.name).append("', ")
|
||||||
.append(this.name).append("', ").append("configuration=")
|
.append("configuration=").append(Arrays.toString(this.configuration)).append("}").toString();
|
||||||
.append(Arrays.toString(this.configuration)).append("}").toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-37
@@ -20,7 +20,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.Module;
|
import com.fasterxml.jackson.databind.Module;
|
||||||
import com.netflix.hystrix.HystrixCommand;
|
|
||||||
import feign.Contract;
|
import feign.Contract;
|
||||||
import feign.Feign;
|
import feign.Feign;
|
||||||
import feign.Logger;
|
import feign.Logger;
|
||||||
@@ -29,7 +28,6 @@ import feign.codec.Decoder;
|
|||||||
import feign.codec.Encoder;
|
import feign.codec.Encoder;
|
||||||
import feign.form.MultipartFormContentProcessor;
|
import feign.form.MultipartFormContentProcessor;
|
||||||
import feign.form.spring.SpringFormEncoder;
|
import feign.form.spring.SpringFormEncoder;
|
||||||
import feign.hystrix.HystrixFeign;
|
|
||||||
import feign.optionals.OptionalDecoder;
|
import feign.optionals.OptionalDecoder;
|
||||||
|
|
||||||
import org.springframework.beans.factory.ObjectFactory;
|
import org.springframework.beans.factory.ObjectFactory;
|
||||||
@@ -38,7 +36,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
|
||||||
import org.springframework.boot.autoconfigure.data.web.SpringDataWebProperties;
|
import org.springframework.boot.autoconfigure.data.web.SpringDataWebProperties;
|
||||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||||
import org.springframework.cloud.openfeign.clientconfig.FeignClientConfigurer;
|
import org.springframework.cloud.openfeign.clientconfig.FeignClientConfigurer;
|
||||||
@@ -88,8 +85,7 @@ public class FeignClientsConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public Decoder feignDecoder() {
|
public Decoder feignDecoder() {
|
||||||
return new OptionalDecoder(
|
return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
|
||||||
new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@@ -102,18 +98,13 @@ public class FeignClientsConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnClass(name = "org.springframework.data.domain.Pageable")
|
@ConditionalOnClass(name = "org.springframework.data.domain.Pageable")
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public Encoder feignEncoderPageable(
|
public Encoder feignEncoderPageable(ObjectProvider<AbstractFormWriter> formWriterProvider) {
|
||||||
ObjectProvider<AbstractFormWriter> formWriterProvider) {
|
PageableSpringEncoder encoder = new PageableSpringEncoder(springEncoder(formWriterProvider));
|
||||||
PageableSpringEncoder encoder = new PageableSpringEncoder(
|
|
||||||
springEncoder(formWriterProvider));
|
|
||||||
|
|
||||||
if (springDataWebProperties != null) {
|
if (springDataWebProperties != null) {
|
||||||
encoder.setPageParameter(
|
encoder.setPageParameter(springDataWebProperties.getPageable().getPageParameter());
|
||||||
springDataWebProperties.getPageable().getPageParameter());
|
encoder.setSizeParameter(springDataWebProperties.getPageable().getSizeParameter());
|
||||||
encoder.setSizeParameter(
|
encoder.setSortParameter(springDataWebProperties.getSort().getSortParameter());
|
||||||
springDataWebProperties.getPageable().getSizeParameter());
|
|
||||||
encoder.setSortParameter(
|
|
||||||
springDataWebProperties.getSort().getSortParameter());
|
|
||||||
}
|
}
|
||||||
return encoder;
|
return encoder;
|
||||||
}
|
}
|
||||||
@@ -121,10 +112,8 @@ public class FeignClientsConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public Contract feignContract(ConversionService feignConversionService) {
|
public Contract feignContract(ConversionService feignConversionService) {
|
||||||
boolean decodeSlash = feignClientProperties == null
|
boolean decodeSlash = feignClientProperties == null || feignClientProperties.isDecodeSlash();
|
||||||
|| feignClientProperties.isDecodeSlash();
|
return new SpringMvcContract(this.parameterProcessors, feignConversionService, decodeSlash);
|
||||||
return new SpringMvcContract(this.parameterProcessors, feignConversionService,
|
|
||||||
decodeSlash);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@@ -178,35 +167,19 @@ public class FeignClientsConfiguration {
|
|||||||
AbstractFormWriter formWriter = formWriterProvider.getIfAvailable();
|
AbstractFormWriter formWriter = formWriterProvider.getIfAvailable();
|
||||||
|
|
||||||
if (formWriter != null) {
|
if (formWriter != null) {
|
||||||
return new SpringEncoder(new SpringPojoFormEncoder(formWriter),
|
return new SpringEncoder(new SpringPojoFormEncoder(formWriter), this.messageConverters);
|
||||||
this.messageConverters);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return new SpringEncoder(new SpringFormEncoder(), this.messageConverters);
|
return new SpringEncoder(new SpringFormEncoder(), this.messageConverters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
|
||||||
@ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class })
|
|
||||||
protected static class HystrixFeignConfiguration {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@Scope("prototype")
|
|
||||||
@ConditionalOnMissingBean
|
|
||||||
@ConditionalOnProperty(name = "feign.hystrix.enabled")
|
|
||||||
public Feign.Builder feignHystrixBuilder() {
|
|
||||||
return HystrixFeign.builder();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SpringPojoFormEncoder extends SpringFormEncoder {
|
private class SpringPojoFormEncoder extends SpringFormEncoder {
|
||||||
|
|
||||||
SpringPojoFormEncoder(AbstractFormWriter formWriter) {
|
SpringPojoFormEncoder(AbstractFormWriter formWriter) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
MultipartFormContentProcessor processor = (MultipartFormContentProcessor) getContentProcessor(
|
MultipartFormContentProcessor processor = (MultipartFormContentProcessor) getContentProcessor(MULTIPART);
|
||||||
MULTIPART);
|
|
||||||
processor.addFirstWriter(formWriter);
|
processor.addFirstWriter(formWriter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+23
-42
@@ -54,8 +54,7 @@ import org.springframework.util.StringUtils;
|
|||||||
* @author Gang Li
|
* @author Gang Li
|
||||||
* @author Michal Domagala
|
* @author Michal Domagala
|
||||||
*/
|
*/
|
||||||
class FeignClientsRegistrar
|
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
|
||||||
implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
|
|
||||||
|
|
||||||
// patterned after Spring Integration IntegrationComponentScanRegistrar
|
// patterned after Spring Integration IntegrationComponentScanRegistrar
|
||||||
// and RibbonClientsConfigurationRegistgrar
|
// and RibbonClientsConfigurationRegistgrar
|
||||||
@@ -68,8 +67,7 @@ class FeignClientsRegistrar
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void validateFallback(final Class clazz) {
|
static void validateFallback(final Class clazz) {
|
||||||
Assert.isTrue(!clazz.isInterface(),
|
Assert.isTrue(!clazz.isInterface(), "Fallback class must implement the interface annotated by @FeignClient");
|
||||||
"Fallback class must implement the interface annotated by @FeignClient");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void validateFallbackFactory(final Class clazz) {
|
static void validateFallbackFactory(final Class clazz) {
|
||||||
@@ -134,16 +132,13 @@ class FeignClientsRegistrar
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerBeanDefinitions(AnnotationMetadata metadata,
|
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
|
||||||
BeanDefinitionRegistry registry) {
|
|
||||||
registerDefaultConfiguration(metadata, registry);
|
registerDefaultConfiguration(metadata, registry);
|
||||||
registerFeignClients(metadata, registry);
|
registerFeignClients(metadata, registry);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerDefaultConfiguration(AnnotationMetadata metadata,
|
private void registerDefaultConfiguration(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
|
||||||
BeanDefinitionRegistry registry) {
|
Map<String, Object> defaultAttrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
|
||||||
Map<String, Object> defaultAttrs = metadata
|
|
||||||
.getAnnotationAttributes(EnableFeignClients.class.getName(), true);
|
|
||||||
|
|
||||||
if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
|
if (defaultAttrs != null && defaultAttrs.containsKey("defaultConfiguration")) {
|
||||||
String name;
|
String name;
|
||||||
@@ -153,21 +148,16 @@ class FeignClientsRegistrar
|
|||||||
else {
|
else {
|
||||||
name = "default." + metadata.getClassName();
|
name = "default." + metadata.getClassName();
|
||||||
}
|
}
|
||||||
registerClientConfiguration(registry, name,
|
registerClientConfiguration(registry, name, defaultAttrs.get("defaultConfiguration"));
|
||||||
defaultAttrs.get("defaultConfiguration"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerFeignClients(AnnotationMetadata metadata,
|
public void registerFeignClients(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
|
||||||
BeanDefinitionRegistry registry) {
|
|
||||||
|
|
||||||
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
|
LinkedHashSet<BeanDefinition> candidateComponents = new LinkedHashSet<>();
|
||||||
Map<String, Object> attrs = metadata
|
Map<String, Object> attrs = metadata.getAnnotationAttributes(EnableFeignClients.class.getName());
|
||||||
.getAnnotationAttributes(EnableFeignClients.class.getName());
|
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(FeignClient.class);
|
||||||
AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(
|
final Class<?>[] clients = attrs == null ? null : (Class<?>[]) attrs.get("clients");
|
||||||
FeignClient.class);
|
|
||||||
final Class<?>[] clients = attrs == null ? null
|
|
||||||
: (Class<?>[]) attrs.get("clients");
|
|
||||||
if (clients == null || clients.length == 0) {
|
if (clients == null || clients.length == 0) {
|
||||||
ClassPathScanningCandidateComponentProvider scanner = getScanner();
|
ClassPathScanningCandidateComponentProvider scanner = getScanner();
|
||||||
scanner.setResourceLoader(this.resourceLoader);
|
scanner.setResourceLoader(this.resourceLoader);
|
||||||
@@ -188,26 +178,23 @@ class FeignClientsRegistrar
|
|||||||
// verify annotated class is an interface
|
// verify annotated class is an interface
|
||||||
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
|
AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
|
||||||
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
|
AnnotationMetadata annotationMetadata = beanDefinition.getMetadata();
|
||||||
Assert.isTrue(annotationMetadata.isInterface(),
|
Assert.isTrue(annotationMetadata.isInterface(), "@FeignClient can only be specified on an interface");
|
||||||
"@FeignClient can only be specified on an interface");
|
|
||||||
|
|
||||||
Map<String, Object> attributes = annotationMetadata
|
Map<String, Object> attributes = annotationMetadata
|
||||||
.getAnnotationAttributes(FeignClient.class.getCanonicalName());
|
.getAnnotationAttributes(FeignClient.class.getCanonicalName());
|
||||||
|
|
||||||
String name = getClientName(attributes);
|
String name = getClientName(attributes);
|
||||||
registerClientConfiguration(registry, name,
|
registerClientConfiguration(registry, name, attributes.get("configuration"));
|
||||||
attributes.get("configuration"));
|
|
||||||
|
|
||||||
registerFeignClient(registry, annotationMetadata, attributes);
|
registerFeignClient(registry, annotationMetadata, attributes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerFeignClient(BeanDefinitionRegistry registry,
|
private void registerFeignClient(BeanDefinitionRegistry registry, AnnotationMetadata annotationMetadata,
|
||||||
AnnotationMetadata annotationMetadata, Map<String, Object> attributes) {
|
Map<String, Object> attributes) {
|
||||||
String className = annotationMetadata.getClassName();
|
String className = annotationMetadata.getClassName();
|
||||||
BeanDefinitionBuilder definition = BeanDefinitionBuilder
|
BeanDefinitionBuilder definition = BeanDefinitionBuilder.genericBeanDefinition(FeignClientFactoryBean.class);
|
||||||
.genericBeanDefinition(FeignClientFactoryBean.class);
|
|
||||||
validate(attributes);
|
validate(attributes);
|
||||||
definition.addPropertyValue("url", getUrl(attributes));
|
definition.addPropertyValue("url", getUrl(attributes));
|
||||||
definition.addPropertyValue("path", getPath(attributes));
|
definition.addPropertyValue("path", getPath(attributes));
|
||||||
@@ -235,8 +222,7 @@ class FeignClientsRegistrar
|
|||||||
alias = qualifier;
|
alias = qualifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className,
|
BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition, className, new String[] { alias });
|
||||||
new String[] { alias });
|
|
||||||
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
|
BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,8 +276,7 @@ class FeignClientsRegistrar
|
|||||||
protected ClassPathScanningCandidateComponentProvider getScanner() {
|
protected ClassPathScanningCandidateComponentProvider getScanner() {
|
||||||
return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
|
return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
|
||||||
@Override
|
@Override
|
||||||
protected boolean isCandidateComponent(
|
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
|
||||||
AnnotatedBeanDefinition beanDefinition) {
|
|
||||||
boolean isCandidate = false;
|
boolean isCandidate = false;
|
||||||
if (beanDefinition.getMetadata().isIndependent()) {
|
if (beanDefinition.getMetadata().isIndependent()) {
|
||||||
if (!beanDefinition.getMetadata().isAnnotation()) {
|
if (!beanDefinition.getMetadata().isAnnotation()) {
|
||||||
@@ -323,8 +308,7 @@ class FeignClientsRegistrar
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (basePackages.isEmpty()) {
|
if (basePackages.isEmpty()) {
|
||||||
basePackages.add(
|
basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName()));
|
||||||
ClassUtils.getPackageName(importingClassMetadata.getClassName()));
|
|
||||||
}
|
}
|
||||||
return basePackages;
|
return basePackages;
|
||||||
}
|
}
|
||||||
@@ -358,18 +342,15 @@ class FeignClientsRegistrar
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new IllegalStateException("Either 'name' or 'value' must be provided in @"
|
throw new IllegalStateException(
|
||||||
+ FeignClient.class.getSimpleName());
|
"Either 'name' or 'value' must be provided in @" + FeignClient.class.getSimpleName());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
|
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {
|
||||||
Object configuration) {
|
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(FeignClientSpecification.class);
|
||||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder
|
|
||||||
.genericBeanDefinition(FeignClientSpecification.class);
|
|
||||||
builder.addConstructorArgValue(name);
|
builder.addConstructorArgValue(name);
|
||||||
builder.addConstructorArgValue(configuration);
|
builder.addConstructorArgValue(configuration);
|
||||||
registry.registerBeanDefinition(
|
registry.registerBeanDefinition(name + "." + FeignClientSpecification.class.getSimpleName(),
|
||||||
name + "." + FeignClientSpecification.class.getSimpleName(),
|
|
||||||
builder.getBeanDefinition());
|
builder.getBeanDefinition());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-101
@@ -1,101 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2020 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
|
||||||
|
|
||||||
import feign.Feign;
|
|
||||||
import feign.Target;
|
|
||||||
import feign.hystrix.FallbackFactory;
|
|
||||||
import feign.hystrix.HystrixFeign;
|
|
||||||
import feign.hystrix.SetterFactory;
|
|
||||||
|
|
||||||
import org.springframework.util.StringUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Spencer Gibb
|
|
||||||
* @author Erik Kringen
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
class HystrixTargeter implements Targeter {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
|
|
||||||
FeignContext context, Target.HardCodedTarget<T> target) {
|
|
||||||
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
|
|
||||||
return feign.target(target);
|
|
||||||
}
|
|
||||||
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
|
|
||||||
String name = StringUtils.isEmpty(factory.getContextId()) ? factory.getName()
|
|
||||||
: factory.getContextId();
|
|
||||||
SetterFactory setterFactory = getOptional(name, context, SetterFactory.class);
|
|
||||||
if (setterFactory != null) {
|
|
||||||
builder.setterFactory(setterFactory);
|
|
||||||
}
|
|
||||||
Class<?> fallback = factory.getFallback();
|
|
||||||
if (fallback != void.class) {
|
|
||||||
return targetWithFallback(name, context, target, builder, fallback);
|
|
||||||
}
|
|
||||||
Class<?> fallbackFactory = factory.getFallbackFactory();
|
|
||||||
if (fallbackFactory != void.class) {
|
|
||||||
return targetWithFallbackFactory(name, context, target, builder,
|
|
||||||
fallbackFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
return feign.target(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
|
|
||||||
Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
|
|
||||||
Class<?> fallbackFactoryClass) {
|
|
||||||
FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>) getFromContext(
|
|
||||||
"fallbackFactory", feignClientName, context, fallbackFactoryClass,
|
|
||||||
FallbackFactory.class);
|
|
||||||
return builder.target(target, fallbackFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> T targetWithFallback(String feignClientName, FeignContext context,
|
|
||||||
Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
|
|
||||||
Class<?> fallback) {
|
|
||||||
T fallbackInstance = getFromContext("fallback", feignClientName, context,
|
|
||||||
fallback, target.type());
|
|
||||||
return builder.target(target, fallbackInstance);
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> T getFromContext(String fallbackMechanism, String feignClientName,
|
|
||||||
FeignContext context, Class<?> beanType, Class<T> targetType) {
|
|
||||||
Object fallbackInstance = context.getInstance(feignClientName, beanType);
|
|
||||||
if (fallbackInstance == null) {
|
|
||||||
throw new IllegalStateException(String.format(
|
|
||||||
"No " + fallbackMechanism
|
|
||||||
+ " instance of type %s found for feign client %s",
|
|
||||||
beanType, feignClientName));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!targetType.isAssignableFrom(beanType)) {
|
|
||||||
throw new IllegalStateException(String.format("Incompatible "
|
|
||||||
+ fallbackMechanism
|
|
||||||
+ " instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
|
|
||||||
beanType, targetType, feignClientName));
|
|
||||||
}
|
|
||||||
return (T) fallbackInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> T getOptional(String feignClientName, FeignContext context,
|
|
||||||
Class<T> beanType) {
|
|
||||||
return context.getInstance(feignClientName, beanType);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
+3
-3
@@ -22,9 +22,9 @@ import feign.Target;
|
|||||||
/**
|
/**
|
||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
*/
|
*/
|
||||||
interface Targeter {
|
public interface Targeter {
|
||||||
|
|
||||||
<T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
|
<T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
|
||||||
FeignContext context, Target.HardCodedTarget<T> target);
|
Target.HardCodedTarget<T> target);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-8
@@ -48,15 +48,13 @@ public class MatrixVariableParameterProcessor implements AnnotatedParameterProce
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean processArgument(AnnotatedParameterContext context,
|
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
|
||||||
Annotation annotation, Method method) {
|
|
||||||
int parameterIndex = context.getParameterIndex();
|
int parameterIndex = context.getParameterIndex();
|
||||||
Class<?> parameterType = method.getParameterTypes()[parameterIndex];
|
Class<?> parameterType = method.getParameterTypes()[parameterIndex];
|
||||||
MethodMetadata data = context.getMethodMetadata();
|
MethodMetadata data = context.getMethodMetadata();
|
||||||
String name = ANNOTATION.cast(annotation).value();
|
String name = ANNOTATION.cast(annotation).value();
|
||||||
|
|
||||||
checkState(emptyToNull(name) != null,
|
checkState(emptyToNull(name) != null, "MatrixVariable annotation was empty on param %s.",
|
||||||
"MatrixVariable annotation was empty on param %s.",
|
|
||||||
context.getParameterIndex());
|
context.getParameterIndex());
|
||||||
|
|
||||||
context.setParameterName(name);
|
context.setParameterName(name);
|
||||||
@@ -65,8 +63,7 @@ public class MatrixVariableParameterProcessor implements AnnotatedParameterProce
|
|||||||
data.indexToExpander().put(parameterIndex, this::expandMap);
|
data.indexToExpander().put(parameterIndex, this::expandMap);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
data.indexToExpander().put(parameterIndex,
|
data.indexToExpander().put(parameterIndex, object -> ";" + name + "=" + object.toString());
|
||||||
object -> ";" + name + "=" + object.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -75,8 +72,7 @@ public class MatrixVariableParameterProcessor implements AnnotatedParameterProce
|
|||||||
private String expandMap(Object object) {
|
private String expandMap(Object object) {
|
||||||
Map<String, Object> paramMap = (Map) object;
|
Map<String, Object> paramMap = (Map) object;
|
||||||
|
|
||||||
return paramMap.keySet().stream()
|
return paramMap.keySet().stream().map(key -> ";" + key + "=" + paramMap.get(key).toString())
|
||||||
.map(key -> ";" + key + "=" + paramMap.get(key).toString())
|
|
||||||
.collect(Collectors.joining());
|
.collect(Collectors.joining());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-6
@@ -46,18 +46,15 @@ public class PathVariableParameterProcessor implements AnnotatedParameterProcess
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean processArgument(AnnotatedParameterContext context,
|
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
|
||||||
Annotation annotation, Method method) {
|
|
||||||
String name = ANNOTATION.cast(annotation).value();
|
String name = ANNOTATION.cast(annotation).value();
|
||||||
checkState(emptyToNull(name) != null,
|
checkState(emptyToNull(name) != null, "PathVariable annotation was empty on param %s.",
|
||||||
"PathVariable annotation was empty on param %s.",
|
|
||||||
context.getParameterIndex());
|
context.getParameterIndex());
|
||||||
context.setParameterName(name);
|
context.setParameterName(name);
|
||||||
|
|
||||||
MethodMetadata data = context.getMethodMetadata();
|
MethodMetadata data = context.getMethodMetadata();
|
||||||
String varName = '{' + name + '}';
|
String varName = '{' + name + '}';
|
||||||
if (!data.template().url().contains(varName)
|
if (!data.template().url().contains(varName) && !searchMapValues(data.template().queries(), varName)
|
||||||
&& !searchMapValues(data.template().queries(), varName)
|
|
||||||
&& !searchMapValues(data.template().headers(), varName)) {
|
&& !searchMapValues(data.template().headers(), varName)) {
|
||||||
data.formParams().add(name);
|
data.formParams().add(name);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -40,8 +40,7 @@ public class QueryMapParameterProcessor implements AnnotatedParameterProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean processArgument(AnnotatedParameterContext context,
|
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
|
||||||
Annotation annotation, Method method) {
|
|
||||||
int paramIndex = context.getParameterIndex();
|
int paramIndex = context.getParameterIndex();
|
||||||
MethodMetadata metadata = context.getMethodMetadata();
|
MethodMetadata metadata = context.getMethodMetadata();
|
||||||
if (metadata.queryMapIndex() == null) {
|
if (metadata.queryMapIndex() == null) {
|
||||||
|
|||||||
+4
-8
@@ -46,27 +46,23 @@ public class RequestHeaderParameterProcessor implements AnnotatedParameterProces
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean processArgument(AnnotatedParameterContext context,
|
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
|
||||||
Annotation annotation, Method method) {
|
|
||||||
int parameterIndex = context.getParameterIndex();
|
int parameterIndex = context.getParameterIndex();
|
||||||
Class<?> parameterType = method.getParameterTypes()[parameterIndex];
|
Class<?> parameterType = method.getParameterTypes()[parameterIndex];
|
||||||
MethodMetadata data = context.getMethodMetadata();
|
MethodMetadata data = context.getMethodMetadata();
|
||||||
|
|
||||||
if (Map.class.isAssignableFrom(parameterType)) {
|
if (Map.class.isAssignableFrom(parameterType)) {
|
||||||
checkState(data.headerMapIndex() == null,
|
checkState(data.headerMapIndex() == null, "Header map can only be present once.");
|
||||||
"Header map can only be present once.");
|
|
||||||
data.headerMapIndex(parameterIndex);
|
data.headerMapIndex(parameterIndex);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
String name = ANNOTATION.cast(annotation).value();
|
String name = ANNOTATION.cast(annotation).value();
|
||||||
checkState(emptyToNull(name) != null,
|
checkState(emptyToNull(name) != null, "RequestHeader.value() was empty on parameter %s", parameterIndex);
|
||||||
"RequestHeader.value() was empty on parameter %s", parameterIndex);
|
|
||||||
context.setParameterName(name);
|
context.setParameterName(name);
|
||||||
|
|
||||||
Collection<String> header = context.setTemplateParameter(name,
|
Collection<String> header = context.setTemplateParameter(name, data.template().headers().get(name));
|
||||||
data.template().headers().get(name));
|
|
||||||
data.template().header(name, header);
|
data.template().header(name, header);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-8
@@ -46,15 +46,13 @@ public class RequestParamParameterProcessor implements AnnotatedParameterProcess
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean processArgument(AnnotatedParameterContext context,
|
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
|
||||||
Annotation annotation, Method method) {
|
|
||||||
int parameterIndex = context.getParameterIndex();
|
int parameterIndex = context.getParameterIndex();
|
||||||
Class<?> parameterType = method.getParameterTypes()[parameterIndex];
|
Class<?> parameterType = method.getParameterTypes()[parameterIndex];
|
||||||
MethodMetadata data = context.getMethodMetadata();
|
MethodMetadata data = context.getMethodMetadata();
|
||||||
|
|
||||||
if (Map.class.isAssignableFrom(parameterType)) {
|
if (Map.class.isAssignableFrom(parameterType)) {
|
||||||
checkState(data.queryMapIndex() == null,
|
checkState(data.queryMapIndex() == null, "Query map can only be present once.");
|
||||||
"Query map can only be present once.");
|
|
||||||
data.queryMapIndex(parameterIndex);
|
data.queryMapIndex(parameterIndex);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -62,12 +60,10 @@ public class RequestParamParameterProcessor implements AnnotatedParameterProcess
|
|||||||
|
|
||||||
RequestParam requestParam = ANNOTATION.cast(annotation);
|
RequestParam requestParam = ANNOTATION.cast(annotation);
|
||||||
String name = requestParam.value();
|
String name = requestParam.value();
|
||||||
checkState(emptyToNull(name) != null,
|
checkState(emptyToNull(name) != null, "RequestParam.value() was empty on parameter %s", parameterIndex);
|
||||||
"RequestParam.value() was empty on parameter %s", parameterIndex);
|
|
||||||
context.setParameterName(name);
|
context.setParameterName(name);
|
||||||
|
|
||||||
Collection<String> query = context.setTemplateParameter(name,
|
Collection<String> query = context.setTemplateParameter(name, data.template().queries().get(name));
|
||||||
data.template().queries().get(name));
|
|
||||||
data.template().query(name, query);
|
data.template().query(name, query);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-6
@@ -44,19 +44,16 @@ public class RequestPartParameterProcessor implements AnnotatedParameterProcesso
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean processArgument(AnnotatedParameterContext context,
|
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
|
||||||
Annotation annotation, Method method) {
|
|
||||||
int parameterIndex = context.getParameterIndex();
|
int parameterIndex = context.getParameterIndex();
|
||||||
MethodMetadata data = context.getMethodMetadata();
|
MethodMetadata data = context.getMethodMetadata();
|
||||||
|
|
||||||
String name = ANNOTATION.cast(annotation).value();
|
String name = ANNOTATION.cast(annotation).value();
|
||||||
checkState(emptyToNull(name) != null,
|
checkState(emptyToNull(name) != null, "RequestPart.value() was empty on parameter %s", parameterIndex);
|
||||||
"RequestPart.value() was empty on parameter %s", parameterIndex);
|
|
||||||
context.setParameterName(name);
|
context.setParameterName(name);
|
||||||
|
|
||||||
data.formParams().add(name);
|
data.formParams().add(name);
|
||||||
Collection<String> names = context.setTemplateParameter(name,
|
Collection<String> names = context.setTemplateParameter(name, data.indexToName().get(parameterIndex));
|
||||||
data.indexToName().get(parameterIndex));
|
|
||||||
data.indexToName().put(parameterIndex, names);
|
data.indexToName().put(parameterIndex, names);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-26
@@ -53,8 +53,8 @@ public class HttpClientFeignConfiguration {
|
|||||||
|
|
||||||
private static final Log LOG = LogFactory.getLog(HttpClientFeignConfiguration.class);
|
private static final Log LOG = LogFactory.getLog(HttpClientFeignConfiguration.class);
|
||||||
|
|
||||||
private final Timer connectionManagerTimer = new Timer(
|
private final Timer connectionManagerTimer = new Timer("FeignApacheHttpClientConfiguration.connectionManagerTimer",
|
||||||
"FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
|
true);
|
||||||
|
|
||||||
private CloseableHttpClient httpClient;
|
private CloseableHttpClient httpClient;
|
||||||
|
|
||||||
@@ -66,12 +66,10 @@ public class HttpClientFeignConfiguration {
|
|||||||
public HttpClientConnectionManager connectionManager(
|
public HttpClientConnectionManager connectionManager(
|
||||||
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
|
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
|
||||||
FeignHttpClientProperties httpClientProperties) {
|
FeignHttpClientProperties httpClientProperties) {
|
||||||
final HttpClientConnectionManager connectionManager = connectionManagerFactory
|
final HttpClientConnectionManager connectionManager = connectionManagerFactory.newConnectionManager(
|
||||||
.newConnectionManager(httpClientProperties.isDisableSslValidation(),
|
httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(),
|
||||||
httpClientProperties.getMaxConnections(),
|
httpClientProperties.getMaxConnectionsPerRoute(), httpClientProperties.getTimeToLive(),
|
||||||
httpClientProperties.getMaxConnectionsPerRoute(),
|
httpClientProperties.getTimeToLiveUnit(), this.registryBuilder);
|
||||||
httpClientProperties.getTimeToLive(),
|
|
||||||
httpClientProperties.getTimeToLiveUnit(), this.registryBuilder);
|
|
||||||
this.connectionManagerTimer.schedule(new TimerTask() {
|
this.connectionManagerTimer.schedule(new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -82,37 +80,29 @@ public class HttpClientFeignConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty(value = "feign.compression.response.enabled",
|
@ConditionalOnProperty(value = "feign.compression.response.enabled", havingValue = "true")
|
||||||
havingValue = "true")
|
public CloseableHttpClient customHttpClient(HttpClientConnectionManager httpClientConnectionManager,
|
||||||
public CloseableHttpClient customHttpClient(
|
|
||||||
HttpClientConnectionManager httpClientConnectionManager,
|
|
||||||
FeignHttpClientProperties httpClientProperties) {
|
FeignHttpClientProperties httpClientProperties) {
|
||||||
HttpClientBuilder builder = HttpClientBuilder.create().disableCookieManagement()
|
HttpClientBuilder builder = HttpClientBuilder.create().disableCookieManagement().useSystemProperties();
|
||||||
.useSystemProperties();
|
this.httpClient = createClient(builder, httpClientConnectionManager, httpClientProperties);
|
||||||
this.httpClient = createClient(builder, httpClientConnectionManager,
|
|
||||||
httpClientProperties);
|
|
||||||
return this.httpClient;
|
return this.httpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnProperty(value = "feign.compression.response.enabled",
|
@ConditionalOnProperty(value = "feign.compression.response.enabled", havingValue = "false", matchIfMissing = true)
|
||||||
havingValue = "false", matchIfMissing = true)
|
|
||||||
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
|
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
|
||||||
HttpClientConnectionManager httpClientConnectionManager,
|
HttpClientConnectionManager httpClientConnectionManager, FeignHttpClientProperties httpClientProperties) {
|
||||||
FeignHttpClientProperties httpClientProperties) {
|
this.httpClient = createClient(httpClientFactory.createBuilder(), httpClientConnectionManager,
|
||||||
this.httpClient = createClient(httpClientFactory.createBuilder(),
|
httpClientProperties);
|
||||||
httpClientConnectionManager, httpClientProperties);
|
|
||||||
return this.httpClient;
|
return this.httpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
private CloseableHttpClient createClient(HttpClientBuilder builder,
|
private CloseableHttpClient createClient(HttpClientBuilder builder,
|
||||||
HttpClientConnectionManager httpClientConnectionManager,
|
HttpClientConnectionManager httpClientConnectionManager, FeignHttpClientProperties httpClientProperties) {
|
||||||
FeignHttpClientProperties httpClientProperties) {
|
|
||||||
RequestConfig defaultRequestConfig = RequestConfig.custom()
|
RequestConfig defaultRequestConfig = RequestConfig.custom()
|
||||||
.setConnectTimeout(httpClientProperties.getConnectionTimeout())
|
.setConnectTimeout(httpClientProperties.getConnectionTimeout())
|
||||||
.setRedirectsEnabled(httpClientProperties.isFollowRedirects()).build();
|
.setRedirectsEnabled(httpClientProperties.isFollowRedirects()).build();
|
||||||
CloseableHttpClient httpClient = builder
|
CloseableHttpClient httpClient = builder.setDefaultRequestConfig(defaultRequestConfig)
|
||||||
.setDefaultRequestConfig(defaultRequestConfig)
|
|
||||||
.setConnectionManager(httpClientConnectionManager).build();
|
.setConnectionManager(httpClientConnectionManager).build();
|
||||||
return httpClient;
|
return httpClient;
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-8
@@ -46,8 +46,7 @@ public class OkHttpFeignConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean(ConnectionPool.class)
|
@ConditionalOnMissingBean(ConnectionPool.class)
|
||||||
public ConnectionPool httpClientConnectionPool(
|
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
|
||||||
FeignHttpClientProperties httpClientProperties,
|
|
||||||
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
|
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
|
||||||
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
|
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
|
||||||
Long timeToLive = httpClientProperties.getTimeToLive();
|
Long timeToLive = httpClientProperties.getTimeToLive();
|
||||||
@@ -56,15 +55,13 @@ public class OkHttpFeignConfiguration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
|
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory, ConnectionPool connectionPool,
|
||||||
ConnectionPool connectionPool,
|
|
||||||
FeignHttpClientProperties httpClientProperties) {
|
FeignHttpClientProperties httpClientProperties) {
|
||||||
Boolean followRedirects = httpClientProperties.isFollowRedirects();
|
Boolean followRedirects = httpClientProperties.isFollowRedirects();
|
||||||
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
|
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
|
||||||
this.okHttpClient = httpClientFactory
|
this.okHttpClient = httpClientFactory.createBuilder(httpClientProperties.isDisableSslValidation())
|
||||||
.createBuilder(httpClientProperties.isDisableSslValidation())
|
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).followRedirects(followRedirects)
|
||||||
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
|
.connectionPool(connectionPool).build();
|
||||||
.followRedirects(followRedirects).connectionPool(connectionPool).build();
|
|
||||||
return this.okHttpClient;
|
return this.okHttpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-2
@@ -48,8 +48,7 @@ public abstract class BaseRequestInterceptor implements RequestInterceptor {
|
|||||||
* @param name the header name
|
* @param name the header name
|
||||||
* @param values the header values
|
* @param values the header values
|
||||||
*/
|
*/
|
||||||
protected void addHeader(RequestTemplate requestTemplate, String name,
|
protected void addHeader(RequestTemplate requestTemplate, String name, String... values) {
|
||||||
String... values) {
|
|
||||||
|
|
||||||
if (!requestTemplate.headers().containsKey(name)) {
|
if (!requestTemplate.headers().containsKey(name)) {
|
||||||
requestTemplate.header(name, values);
|
requestTemplate.header(name, values);
|
||||||
|
|||||||
+1
-2
@@ -39,8 +39,7 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
@EnableConfigurationProperties(FeignClientEncodingProperties.class)
|
@EnableConfigurationProperties(FeignClientEncodingProperties.class)
|
||||||
@ConditionalOnClass(Feign.class)
|
@ConditionalOnClass(Feign.class)
|
||||||
@ConditionalOnBean(Client.class)
|
@ConditionalOnBean(Client.class)
|
||||||
@ConditionalOnProperty(value = "feign.compression.response.enabled",
|
@ConditionalOnProperty(value = "feign.compression.response.enabled", matchIfMissing = false)
|
||||||
matchIfMissing = false)
|
|
||||||
// The OK HTTP client uses "transparent" compression.
|
// The OK HTTP client uses "transparent" compression.
|
||||||
// If the accept-encoding header is present it disable transparent compression
|
// If the accept-encoding header is present it disable transparent compression
|
||||||
@ConditionalOnMissingBean(type = "okhttp3.OkHttpClient")
|
@ConditionalOnMissingBean(type = "okhttp3.OkHttpClient")
|
||||||
|
|||||||
+3
-4
@@ -33,8 +33,7 @@ public class FeignAcceptGzipEncodingInterceptor extends BaseRequestInterceptor {
|
|||||||
* Creates new instance of {@link FeignAcceptGzipEncodingInterceptor}.
|
* Creates new instance of {@link FeignAcceptGzipEncodingInterceptor}.
|
||||||
* @param properties the encoding properties
|
* @param properties the encoding properties
|
||||||
*/
|
*/
|
||||||
protected FeignAcceptGzipEncodingInterceptor(
|
protected FeignAcceptGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
|
||||||
FeignClientEncodingProperties properties) {
|
|
||||||
super(properties);
|
super(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,8 +43,8 @@ public class FeignAcceptGzipEncodingInterceptor extends BaseRequestInterceptor {
|
|||||||
@Override
|
@Override
|
||||||
public void apply(RequestTemplate template) {
|
public void apply(RequestTemplate template) {
|
||||||
|
|
||||||
addHeader(template, HttpEncoding.ACCEPT_ENCODING_HEADER,
|
addHeader(template, HttpEncoding.ACCEPT_ENCODING_HEADER, HttpEncoding.GZIP_ENCODING,
|
||||||
HttpEncoding.GZIP_ENCODING, HttpEncoding.DEFLATE_ENCODING);
|
HttpEncoding.DEFLATE_ENCODING);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-5
@@ -32,8 +32,7 @@ public class FeignClientEncodingProperties {
|
|||||||
/**
|
/**
|
||||||
* The list of supported mime types.
|
* The list of supported mime types.
|
||||||
*/
|
*/
|
||||||
private String[] mimeTypes = new String[] { "text/xml", "application/xml",
|
private String[] mimeTypes = new String[] { "text/xml", "application/xml", "application/json" };
|
||||||
"application/json" };
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The minimum threshold content size.
|
* The minimum threshold content size.
|
||||||
@@ -77,9 +76,8 @@ public class FeignClientEncodingProperties {
|
|||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new StringBuilder("FeignClientEncodingProperties{").append("mimeTypes=")
|
return new StringBuilder("FeignClientEncodingProperties{").append("mimeTypes=")
|
||||||
.append(Arrays.toString(this.mimeTypes)).append(", ")
|
.append(Arrays.toString(this.mimeTypes)).append(", ").append("minRequestSize=")
|
||||||
.append("minRequestSize=").append(this.minRequestSize).append("}")
|
.append(this.minRequestSize).append("}").toString();
|
||||||
.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-6
@@ -33,8 +33,7 @@ public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor
|
|||||||
* Creates new instance of {@link FeignContentGzipEncodingInterceptor}.
|
* Creates new instance of {@link FeignContentGzipEncodingInterceptor}.
|
||||||
* @param properties the encoding properties
|
* @param properties the encoding properties
|
||||||
*/
|
*/
|
||||||
protected FeignContentGzipEncodingInterceptor(
|
protected FeignContentGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
|
||||||
FeignClientEncodingProperties properties) {
|
|
||||||
super(properties);
|
super(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,8 +44,8 @@ public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor
|
|||||||
public void apply(RequestTemplate template) {
|
public void apply(RequestTemplate template) {
|
||||||
|
|
||||||
if (requiresCompression(template)) {
|
if (requiresCompression(template)) {
|
||||||
addHeader(template, HttpEncoding.CONTENT_ENCODING_HEADER,
|
addHeader(template, HttpEncoding.CONTENT_ENCODING_HEADER, HttpEncoding.GZIP_ENCODING,
|
||||||
HttpEncoding.GZIP_ENCODING, HttpEncoding.DEFLATE_ENCODING);
|
HttpEncoding.DEFLATE_ENCODING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,8 +92,7 @@ public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getProperties().getMimeTypes() == null
|
if (getProperties().getMimeTypes() == null || getProperties().getMimeTypes().length == 0) {
|
||||||
|| getProperties().getMimeTypes().length == 0) {
|
|
||||||
// no specific mime types has been set - matching everything
|
// no specific mime types has been set - matching everything
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-10
@@ -51,8 +51,7 @@ import static org.springframework.hateoas.MediaTypes.HAL_JSON;
|
|||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@ConditionalOnWebApplication
|
@ConditionalOnWebApplication
|
||||||
@ConditionalOnClass(RepresentationModel.class)
|
@ConditionalOnClass(RepresentationModel.class)
|
||||||
@AutoConfigureAfter({ JacksonAutoConfiguration.class,
|
@AutoConfigureAfter({ JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
|
||||||
HttpMessageConvertersAutoConfiguration.class,
|
|
||||||
RepositoryRestMvcAutoConfiguration.class })
|
RepositoryRestMvcAutoConfiguration.class })
|
||||||
@AutoConfigureBefore(HypermediaAutoConfiguration.class)
|
@AutoConfigureBefore(HypermediaAutoConfiguration.class)
|
||||||
public class FeignHalAutoConfiguration {
|
public class FeignHalAutoConfiguration {
|
||||||
@@ -60,24 +59,21 @@ public class FeignHalAutoConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public TypeConstrainedMappingJackson2HttpMessageConverter halJacksonHttpMessageConverter(
|
public TypeConstrainedMappingJackson2HttpMessageConverter halJacksonHttpMessageConverter(
|
||||||
ObjectProvider<ObjectMapper> objectMapper,
|
ObjectProvider<ObjectMapper> objectMapper, ObjectProvider<HalConfiguration> halConfiguration,
|
||||||
ObjectProvider<HalConfiguration> halConfiguration,
|
ObjectProvider<MessageResolver> messageResolver, ObjectProvider<CurieProvider> curieProvider,
|
||||||
ObjectProvider<MessageResolver> messageResolver,
|
|
||||||
ObjectProvider<CurieProvider> curieProvider,
|
|
||||||
ObjectProvider<LinkRelationProvider> linkRelationProvider) {
|
ObjectProvider<LinkRelationProvider> linkRelationProvider) {
|
||||||
|
|
||||||
ObjectMapper mapper = objectMapper.getIfAvailable(ObjectMapper::new).copy();
|
ObjectMapper mapper = objectMapper.getIfAvailable(ObjectMapper::new).copy();
|
||||||
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
|
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
|
||||||
|
|
||||||
HalConfiguration configuration = halConfiguration
|
HalConfiguration configuration = halConfiguration.getIfAvailable(HalConfiguration::new);
|
||||||
.getIfAvailable(HalConfiguration::new);
|
|
||||||
|
|
||||||
CurieProvider curieProviderInstance = curieProvider
|
CurieProvider curieProviderInstance = curieProvider
|
||||||
.getIfAvailable(() -> new DefaultCurieProvider(Collections.emptyMap()));
|
.getIfAvailable(() -> new DefaultCurieProvider(Collections.emptyMap()));
|
||||||
|
|
||||||
Jackson2HalModule.HalHandlerInstantiator halHandlerInstantiator = new Jackson2HalModule.HalHandlerInstantiator(
|
Jackson2HalModule.HalHandlerInstantiator halHandlerInstantiator = new Jackson2HalModule.HalHandlerInstantiator(
|
||||||
linkRelationProvider.getIfAvailable(), curieProviderInstance,
|
linkRelationProvider.getIfAvailable(), curieProviderInstance, messageResolver.getIfAvailable(),
|
||||||
messageResolver.getIfAvailable(), configuration);
|
configuration);
|
||||||
|
|
||||||
mapper.setHandlerInstantiator(halHandlerInstantiator);
|
mapper.setHandlerInstantiator(halHandlerInstantiator);
|
||||||
|
|
||||||
|
|||||||
+18
-17
@@ -16,51 +16,52 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.openfeign.loadbalancer;
|
package org.springframework.cloud.openfeign.loadbalancer;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import feign.Client;
|
import feign.Client;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
|
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
|
||||||
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
|
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
|
||||||
|
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Conditional;
|
import org.springframework.context.annotation.Conditional;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration instantiating a {@link BlockingLoadBalancerClient}-based {@link Client}
|
* Configuration instantiating a {@link LoadBalancerClient}-based {@link Client} object
|
||||||
* object that uses {@link Client.Default} under the hood.
|
* that uses {@link Client.Default} under the hood.
|
||||||
*
|
*
|
||||||
* @author Olga Maciaszek-Sharma
|
* @author Olga Maciaszek-Sharma
|
||||||
* @since 2.2.0
|
* @since 2.2.0
|
||||||
*/
|
*/
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
|
@EnableConfigurationProperties(LoadBalancerProperties.class)
|
||||||
class DefaultFeignLoadBalancerConfiguration {
|
class DefaultFeignLoadBalancerConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
@Conditional(OnRetryNotEnabledCondition.class)
|
@Conditional(OnRetryNotEnabledCondition.class)
|
||||||
public Client feignClient(BlockingLoadBalancerClient loadBalancerClient) {
|
public Client feignClient(LoadBalancerClient loadBalancerClient, LoadBalancerProperties properties,
|
||||||
return new FeignBlockingLoadBalancerClient(new Client.Default(null, null),
|
LoadBalancerClientFactory loadBalancerClientFactory) {
|
||||||
loadBalancerClient);
|
return new FeignBlockingLoadBalancerClient(new Client.Default(null, null), loadBalancerClient, properties,
|
||||||
|
loadBalancerClientFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
|
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
|
||||||
@ConditionalOnBean(LoadBalancedRetryFactory.class)
|
@ConditionalOnBean(LoadBalancedRetryFactory.class)
|
||||||
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled",
|
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "true",
|
||||||
havingValue = "true", matchIfMissing = true)
|
matchIfMissing = true)
|
||||||
public Client feignRetryClient(BlockingLoadBalancerClient loadBalancerClient,
|
public Client feignRetryClient(LoadBalancerClient loadBalancerClient,
|
||||||
List<LoadBalancedRetryFactory> loadBalancedRetryFactories) {
|
LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerProperties properties,
|
||||||
AnnotationAwareOrderComparator.sort(loadBalancedRetryFactories);
|
LoadBalancerClientFactory loadBalancerClientFactory) {
|
||||||
return new RetryableFeignBlockingLoadBalancerClient(
|
return new RetryableFeignBlockingLoadBalancerClient(new Client.Default(null, null), loadBalancerClient,
|
||||||
new Client.Default(null, null), loadBalancerClient,
|
loadBalancedRetryFactory, properties, loadBalancerClientFactory);
|
||||||
loadBalancedRetryFactories.get(0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+55
-20
@@ -19,6 +19,7 @@ package org.springframework.cloud.openfeign.loadbalancer;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import feign.Client;
|
import feign.Client;
|
||||||
import feign.Request;
|
import feign.Request;
|
||||||
@@ -27,58 +28,86 @@ import org.apache.commons.logging.Log;
|
|||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.cloud.client.ServiceInstance;
|
import org.springframework.cloud.client.ServiceInstance;
|
||||||
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
|
import org.springframework.cloud.client.loadbalancer.CompletionContext;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.DefaultRequest;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.LoadBalancerLifecycle;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.LoadBalancerLifecycleValidator;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.RequestDataContext;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.ResponseData;
|
||||||
|
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
import static org.springframework.cloud.openfeign.loadbalancer.LoadBalancerUtils.buildRequestData;
|
||||||
|
import static org.springframework.cloud.openfeign.loadbalancer.LoadBalancerUtils.executeWithLoadBalancerLifecycleProcessing;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link Client} implementation that uses {@link BlockingLoadBalancerClient} to select
|
* A {@link Client} implementation that uses {@link LoadBalancerClient} to select a
|
||||||
* a {@link ServiceInstance} to use while resolving the request host.
|
* {@link ServiceInstance} to use while resolving the request host.
|
||||||
*
|
*
|
||||||
* @author Olga Maciaszek-Sharma
|
* @author Olga Maciaszek-Sharma
|
||||||
* @since 2.2.0
|
* @since 2.2.0
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
public class FeignBlockingLoadBalancerClient implements Client {
|
public class FeignBlockingLoadBalancerClient implements Client {
|
||||||
|
|
||||||
private static final Log LOG = LogFactory
|
private static final Log LOG = LogFactory.getLog(FeignBlockingLoadBalancerClient.class);
|
||||||
.getLog(FeignBlockingLoadBalancerClient.class);
|
|
||||||
|
|
||||||
private final Client delegate;
|
private final Client delegate;
|
||||||
|
|
||||||
private final BlockingLoadBalancerClient loadBalancerClient;
|
private final LoadBalancerClient loadBalancerClient;
|
||||||
|
|
||||||
public FeignBlockingLoadBalancerClient(Client delegate,
|
private final LoadBalancerProperties properties;
|
||||||
BlockingLoadBalancerClient loadBalancerClient) {
|
|
||||||
|
private final LoadBalancerClientFactory loadBalancerClientFactory;
|
||||||
|
|
||||||
|
public FeignBlockingLoadBalancerClient(Client delegate, LoadBalancerClient loadBalancerClient,
|
||||||
|
LoadBalancerProperties properties, LoadBalancerClientFactory loadBalancerClientFactory) {
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
this.loadBalancerClient = loadBalancerClient;
|
this.loadBalancerClient = loadBalancerClient;
|
||||||
|
this.properties = properties;
|
||||||
|
this.loadBalancerClientFactory = loadBalancerClientFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response execute(Request request, Request.Options options) throws IOException {
|
public Response execute(Request request, Request.Options options) throws IOException {
|
||||||
final URI originalUri = URI.create(request.url());
|
final URI originalUri = URI.create(request.url());
|
||||||
String serviceId = originalUri.getHost();
|
String serviceId = originalUri.getHost();
|
||||||
Assert.state(serviceId != null,
|
Assert.state(serviceId != null, "Request URI does not contain a valid hostname: " + originalUri);
|
||||||
"Request URI does not contain a valid hostname: " + originalUri);
|
String hint = getHint(serviceId);
|
||||||
ServiceInstance instance = loadBalancerClient.choose(serviceId);
|
DefaultRequest<RequestDataContext> lbRequest = new DefaultRequest<>(
|
||||||
|
new RequestDataContext(buildRequestData(request), hint));
|
||||||
|
Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator
|
||||||
|
.getSupportedLifecycleProcessors(
|
||||||
|
loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),
|
||||||
|
RequestDataContext.class, ResponseData.class, ServiceInstance.class);
|
||||||
|
supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));
|
||||||
|
ServiceInstance instance = loadBalancerClient.choose(serviceId, lbRequest);
|
||||||
|
org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse = new DefaultResponse(
|
||||||
|
instance);
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
String message = "Load balancer does not contain an instance for the service "
|
String message = "Load balancer does not contain an instance for the service " + serviceId;
|
||||||
+ serviceId;
|
|
||||||
if (LOG.isWarnEnabled()) {
|
if (LOG.isWarnEnabled()) {
|
||||||
LOG.warn(message);
|
LOG.warn(message);
|
||||||
}
|
}
|
||||||
return Response.builder().request(request)
|
supportedLifecycleProcessors
|
||||||
.status(HttpStatus.SERVICE_UNAVAILABLE.value())
|
.forEach(lifecycle -> lifecycle.onComplete(new CompletionContext<ResponseData, ServiceInstance>(
|
||||||
|
CompletionContext.Status.DISCARD, lbResponse)));
|
||||||
|
return Response.builder().request(request).status(HttpStatus.SERVICE_UNAVAILABLE.value())
|
||||||
.body(message, StandardCharsets.UTF_8).build();
|
.body(message, StandardCharsets.UTF_8).build();
|
||||||
}
|
}
|
||||||
String reconstructedUrl = loadBalancerClient.reconstructURI(instance, originalUri)
|
String reconstructedUrl = loadBalancerClient.reconstructURI(instance, originalUri).toString();
|
||||||
.toString();
|
|
||||||
Request newRequest = buildRequest(request, reconstructedUrl);
|
Request newRequest = buildRequest(request, reconstructedUrl);
|
||||||
return delegate.execute(newRequest, options);
|
return executeWithLoadBalancerLifecycleProcessing(delegate, options, newRequest, lbResponse,
|
||||||
|
supportedLifecycleProcessors);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Request buildRequest(Request request, String reconstructedUrl) {
|
protected Request buildRequest(Request request, String reconstructedUrl) {
|
||||||
return Request.create(request.httpMethod(), reconstructedUrl, request.headers(),
|
return Request.create(request.httpMethod(), reconstructedUrl, request.headers(), request.body(),
|
||||||
request.body(), request.charset(), request.requestTemplate());
|
request.charset(), request.requestTemplate());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Visible for Sleuth instrumentation
|
// Visible for Sleuth instrumentation
|
||||||
@@ -86,4 +115,10 @@ public class FeignBlockingLoadBalancerClient implements Client {
|
|||||||
return delegate;
|
return delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getHint(String serviceId) {
|
||||||
|
String defaultHint = properties.getHint().getOrDefault("default", "default");
|
||||||
|
String hintPropertyValue = properties.getHint().get(serviceId);
|
||||||
|
return hintPropertyValue != null ? hintPropertyValue : defaultHint;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-11
@@ -19,38 +19,33 @@ package org.springframework.cloud.openfeign.loadbalancer;
|
|||||||
import feign.Client;
|
import feign.Client;
|
||||||
import feign.Feign;
|
import feign.Feign;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
|
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||||
|
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
||||||
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
||||||
import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration;
|
|
||||||
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
|
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An autoconfiguration that instantiates {@link BlockingLoadBalancerClient}-based
|
* An autoconfiguration that instantiates {@link LoadBalancerClient}-based implementations
|
||||||
* implementations of {@link Client}. In order to use this load-balancing mechanism, the
|
* of {@link Client}.
|
||||||
* Ribbon-based implementation has to be disabled by setting
|
|
||||||
* <code>spring.cloud.loadbalancer.ribbon.enabled</code> to <code>true</code>.
|
|
||||||
*
|
*
|
||||||
* @author Olga Maciaszek-Sharma
|
* @author Olga Maciaszek-Sharma
|
||||||
* @since 2.2.0
|
* @since 2.2.0
|
||||||
*/
|
*/
|
||||||
@ConditionalOnClass(Feign.class)
|
@ConditionalOnClass(Feign.class)
|
||||||
@ConditionalOnBean(BlockingLoadBalancerClient.class)
|
@ConditionalOnBean({ LoadBalancerClient.class, LoadBalancerClientFactory.class })
|
||||||
@AutoConfigureBefore(FeignAutoConfiguration.class)
|
@AutoConfigureBefore(FeignAutoConfiguration.class)
|
||||||
@AutoConfigureAfter(FeignRibbonClientAutoConfiguration.class)
|
|
||||||
@EnableConfigurationProperties(FeignHttpClientProperties.class)
|
@EnableConfigurationProperties(FeignHttpClientProperties.class)
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
// Order is important here, last should be the default, first should be optional
|
// Order is important here, last should be the default, first should be optional
|
||||||
// see
|
// see
|
||||||
// https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
|
// https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
|
||||||
@Import({ HttpClientFeignLoadBalancerConfiguration.class,
|
@Import({ HttpClientFeignLoadBalancerConfiguration.class, OkHttpFeignLoadBalancerConfiguration.class,
|
||||||
OkHttpFeignLoadBalancerConfiguration.class,
|
|
||||||
DefaultFeignLoadBalancerConfiguration.class })
|
DefaultFeignLoadBalancerConfiguration.class })
|
||||||
public class FeignLoadBalancerAutoConfiguration {
|
public class FeignLoadBalancerAutoConfiguration {
|
||||||
|
|
||||||
|
|||||||
+18
-18
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.openfeign.loadbalancer;
|
package org.springframework.cloud.openfeign.loadbalancer;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import feign.Client;
|
import feign.Client;
|
||||||
import feign.httpclient.ApacheHttpClient;
|
import feign.httpclient.ApacheHttpClient;
|
||||||
import org.apache.http.client.HttpClient;
|
import org.apache.http.client.HttpClient;
|
||||||
@@ -26,51 +24,53 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
|||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
|
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
|
||||||
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
|
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
|
||||||
|
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
||||||
import org.springframework.cloud.openfeign.clientconfig.HttpClientFeignConfiguration;
|
import org.springframework.cloud.openfeign.clientconfig.HttpClientFeignConfiguration;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Conditional;
|
import org.springframework.context.annotation.Conditional;
|
||||||
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.core.annotation.AnnotationAwareOrderComparator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration instantiating a {@link BlockingLoadBalancerClient}-based {@link Client}
|
* Configuration instantiating a {@link LoadBalancerClient}-based {@link Client} object
|
||||||
* object that uses {@link ApacheHttpClient} under the hood.
|
* that uses {@link ApacheHttpClient} under the hood.
|
||||||
*
|
*
|
||||||
* @author Olga Maciaszek-Sharma
|
* @author Olga Maciaszek-Sharma
|
||||||
* @since 2.2.0
|
* @since 2.2.0
|
||||||
*/
|
*/
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@ConditionalOnClass(ApacheHttpClient.class)
|
@ConditionalOnClass(ApacheHttpClient.class)
|
||||||
@ConditionalOnBean(BlockingLoadBalancerClient.class)
|
@ConditionalOnBean({ LoadBalancerClient.class, LoadBalancerClientFactory.class })
|
||||||
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
|
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
|
||||||
@Import(HttpClientFeignConfiguration.class)
|
@Import(HttpClientFeignConfiguration.class)
|
||||||
|
@EnableConfigurationProperties(LoadBalancerProperties.class)
|
||||||
class HttpClientFeignLoadBalancerConfiguration {
|
class HttpClientFeignLoadBalancerConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
@Conditional(OnRetryNotEnabledCondition.class)
|
@Conditional(OnRetryNotEnabledCondition.class)
|
||||||
public Client feignClient(BlockingLoadBalancerClient loadBalancerClient,
|
public Client feignClient(LoadBalancerClient loadBalancerClient, HttpClient httpClient,
|
||||||
HttpClient httpClient) {
|
LoadBalancerProperties properties, LoadBalancerClientFactory loadBalancerClientFactory) {
|
||||||
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
|
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
|
||||||
return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient);
|
return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient, properties, loadBalancerClientFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
|
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
|
||||||
@ConditionalOnBean(LoadBalancedRetryFactory.class)
|
@ConditionalOnBean(LoadBalancedRetryFactory.class)
|
||||||
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled",
|
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "true",
|
||||||
havingValue = "true", matchIfMissing = true)
|
matchIfMissing = true)
|
||||||
public Client feignRetryClient(BlockingLoadBalancerClient loadBalancerClient,
|
public Client feignRetryClient(LoadBalancerClient loadBalancerClient, HttpClient httpClient,
|
||||||
HttpClient httpClient,
|
LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerProperties properties,
|
||||||
List<LoadBalancedRetryFactory> loadBalancedRetryFactories) {
|
LoadBalancerClientFactory loadBalancerClientFactory) {
|
||||||
AnnotationAwareOrderComparator.sort(loadBalancedRetryFactories);
|
|
||||||
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
|
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
|
||||||
return new RetryableFeignBlockingLoadBalancerClient(delegate, loadBalancerClient,
|
return new RetryableFeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancedRetryFactory,
|
||||||
loadBalancedRetryFactories.get(0));
|
properties, loadBalancerClientFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+92
@@ -0,0 +1,92 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2020 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.cloud.openfeign.loadbalancer;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import feign.Client;
|
||||||
|
import feign.Request;
|
||||||
|
import feign.Response;
|
||||||
|
|
||||||
|
import org.springframework.cloud.client.ServiceInstance;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.CompletionContext;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.LoadBalancerLifecycle;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.RequestData;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.ResponseData;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Olga Maciaszek-Sharma
|
||||||
|
*
|
||||||
|
* A utility class for handling {@link LoadBalancerLifecycle} calls.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
|
final class LoadBalancerUtils {
|
||||||
|
|
||||||
|
private LoadBalancerUtils() {
|
||||||
|
throw new IllegalStateException("Can't instantiate a utility class");
|
||||||
|
}
|
||||||
|
|
||||||
|
static Response executeWithLoadBalancerLifecycleProcessing(Client feignClient, Request.Options options,
|
||||||
|
Request feignRequest, org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse,
|
||||||
|
Set<LoadBalancerLifecycle> supportedLifecycleProcessors, boolean loadBalanced) throws IOException {
|
||||||
|
try {
|
||||||
|
Response response = feignClient.execute(feignRequest, options);
|
||||||
|
if (loadBalanced) {
|
||||||
|
supportedLifecycleProcessors.forEach(
|
||||||
|
lifecycle -> lifecycle.onComplete(new CompletionContext<>(CompletionContext.Status.SUCCESS,
|
||||||
|
lbResponse, buildResponseData(response))));
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
catch (Exception exception) {
|
||||||
|
if (loadBalanced) {
|
||||||
|
supportedLifecycleProcessors.forEach(lifecycle -> lifecycle
|
||||||
|
.onComplete(new CompletionContext<>(CompletionContext.Status.FAILED, exception, lbResponse)));
|
||||||
|
}
|
||||||
|
throw exception;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ResponseData buildResponseData(Response response) {
|
||||||
|
HttpHeaders responseHeaders = new HttpHeaders();
|
||||||
|
response.headers().forEach((key, value) -> responseHeaders.put(key, new ArrayList<>(value)));
|
||||||
|
return new ResponseData(HttpStatus.resolve(response.status()), responseHeaders, null,
|
||||||
|
buildRequestData(response.request()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static RequestData buildRequestData(Request request) {
|
||||||
|
HttpHeaders requestHeaders = new HttpHeaders();
|
||||||
|
request.headers().forEach((key, value) -> requestHeaders.put(key, new ArrayList<>(value)));
|
||||||
|
return new RequestData(HttpMethod.resolve(request.httpMethod().name()), URI.create(request.url()),
|
||||||
|
requestHeaders, null, new HashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
static Response executeWithLoadBalancerLifecycleProcessing(Client feignClient, Request.Options options,
|
||||||
|
Request feignRequest, org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse,
|
||||||
|
Set<LoadBalancerLifecycle> supportedLifecycleProcessors) throws IOException {
|
||||||
|
return executeWithLoadBalancerLifecycleProcessing(feignClient, options, feignRequest, lbResponse,
|
||||||
|
supportedLifecycleProcessors, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+18
-18
@@ -16,8 +16,6 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.openfeign.loadbalancer;
|
package org.springframework.cloud.openfeign.loadbalancer;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import feign.Client;
|
import feign.Client;
|
||||||
import feign.okhttp.OkHttpClient;
|
import feign.okhttp.OkHttpClient;
|
||||||
|
|
||||||
@@ -25,18 +23,20 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
|||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
|
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
|
||||||
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
|
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
|
||||||
|
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
||||||
import org.springframework.cloud.openfeign.clientconfig.OkHttpFeignConfiguration;
|
import org.springframework.cloud.openfeign.clientconfig.OkHttpFeignConfiguration;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Conditional;
|
import org.springframework.context.annotation.Conditional;
|
||||||
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.core.annotation.AnnotationAwareOrderComparator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration instantiating a {@link BlockingLoadBalancerClient}-based {@link Client}
|
* Configuration instantiating a {@link LoadBalancerClient}-based {@link Client} object
|
||||||
* object that uses {@link OkHttpClient} under the hood.
|
* that uses {@link OkHttpClient} under the hood.
|
||||||
*
|
*
|
||||||
* @author Olga Maciaszek-Sharma
|
* @author Olga Maciaszek-Sharma
|
||||||
* @since 2.2.0
|
* @since 2.2.0
|
||||||
@@ -44,32 +44,32 @@ import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
|||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@ConditionalOnClass(OkHttpClient.class)
|
@ConditionalOnClass(OkHttpClient.class)
|
||||||
@ConditionalOnProperty("feign.okhttp.enabled")
|
@ConditionalOnProperty("feign.okhttp.enabled")
|
||||||
@ConditionalOnBean(BlockingLoadBalancerClient.class)
|
@ConditionalOnBean({ LoadBalancerClient.class, LoadBalancerClientFactory.class })
|
||||||
@Import(OkHttpFeignConfiguration.class)
|
@Import(OkHttpFeignConfiguration.class)
|
||||||
|
@EnableConfigurationProperties(LoadBalancerProperties.class)
|
||||||
class OkHttpFeignLoadBalancerConfiguration {
|
class OkHttpFeignLoadBalancerConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
@Conditional(OnRetryNotEnabledCondition.class)
|
@Conditional(OnRetryNotEnabledCondition.class)
|
||||||
public Client feignClient(okhttp3.OkHttpClient okHttpClient,
|
public Client feignClient(okhttp3.OkHttpClient okHttpClient, LoadBalancerClient loadBalancerClient,
|
||||||
BlockingLoadBalancerClient loadBalancerClient) {
|
LoadBalancerProperties properties, LoadBalancerClientFactory loadBalancerClientFactory) {
|
||||||
OkHttpClient delegate = new OkHttpClient(okHttpClient);
|
OkHttpClient delegate = new OkHttpClient(okHttpClient);
|
||||||
return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient);
|
return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient, properties, loadBalancerClientFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
|
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
|
||||||
@ConditionalOnBean(LoadBalancedRetryFactory.class)
|
@ConditionalOnBean(LoadBalancedRetryFactory.class)
|
||||||
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled",
|
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "true",
|
||||||
havingValue = "true", matchIfMissing = true)
|
matchIfMissing = true)
|
||||||
public Client feignRetryClient(BlockingLoadBalancerClient loadBalancerClient,
|
public Client feignRetryClient(LoadBalancerClient loadBalancerClient, okhttp3.OkHttpClient okHttpClient,
|
||||||
okhttp3.OkHttpClient okHttpClient,
|
LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerProperties properties,
|
||||||
List<LoadBalancedRetryFactory> loadBalancedRetryFactories) {
|
LoadBalancerClientFactory loadBalancerClientFactory) {
|
||||||
AnnotationAwareOrderComparator.sort(loadBalancedRetryFactories);
|
|
||||||
OkHttpClient delegate = new OkHttpClient(okHttpClient);
|
OkHttpClient delegate = new OkHttpClient(okHttpClient);
|
||||||
return new RetryableFeignBlockingLoadBalancerClient(delegate, loadBalancerClient,
|
return new RetryableFeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancedRetryFactory,
|
||||||
loadBalancedRetryFactories.get(0));
|
properties, loadBalancerClientFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -47,8 +47,7 @@ public class OnRetryNotEnabledCondition extends AnyNestedCondition {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled",
|
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "false")
|
||||||
havingValue = "false")
|
|
||||||
static class OnLoadBalancerRetryEnabledCondition {
|
static class OnLoadBalancerRetryEnabledCondition {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+89
-41
@@ -23,6 +23,7 @@ import java.util.Collection;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import feign.Client;
|
import feign.Client;
|
||||||
import feign.Request;
|
import feign.Request;
|
||||||
@@ -31,13 +32,23 @@ import org.apache.commons.logging.Log;
|
|||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.cloud.client.ServiceInstance;
|
import org.springframework.cloud.client.ServiceInstance;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.CompletionContext;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.DefaultRequest;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
|
||||||
import org.springframework.cloud.client.loadbalancer.InterceptorRetryPolicy;
|
import org.springframework.cloud.client.loadbalancer.InterceptorRetryPolicy;
|
||||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRecoveryCallback;
|
import org.springframework.cloud.client.loadbalancer.LoadBalancedRecoveryCallback;
|
||||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
|
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
|
||||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
|
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
|
||||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
|
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.LoadBalancerLifecycle;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.LoadBalancerLifecycleValidator;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.RequestData;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.ResponseData;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.RetryableRequestContext;
|
||||||
import org.springframework.cloud.client.loadbalancer.RetryableStatusCodeException;
|
import org.springframework.cloud.client.loadbalancer.RetryableStatusCodeException;
|
||||||
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
|
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpMethod;
|
import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.http.HttpRequest;
|
import org.springframework.http.HttpRequest;
|
||||||
@@ -46,6 +57,9 @@ import org.springframework.retry.backoff.BackOffPolicy;
|
|||||||
import org.springframework.retry.backoff.NoBackOffPolicy;
|
import org.springframework.retry.backoff.NoBackOffPolicy;
|
||||||
import org.springframework.retry.policy.NeverRetryPolicy;
|
import org.springframework.retry.policy.NeverRetryPolicy;
|
||||||
import org.springframework.retry.support.RetryTemplate;
|
import org.springframework.retry.support.RetryTemplate;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
import static org.springframework.cloud.openfeign.loadbalancer.LoadBalancerUtils.executeWithLoadBalancerLifecycleProcessing;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link Client} implementation that provides Spring Retry support for requests
|
* A {@link Client} implementation that provides Spring Retry support for requests
|
||||||
@@ -54,67 +68,100 @@ import org.springframework.retry.support.RetryTemplate;
|
|||||||
* @author Olga Maciaszek-Sharma
|
* @author Olga Maciaszek-Sharma
|
||||||
* @since 2.2.6
|
* @since 2.2.6
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
public class RetryableFeignBlockingLoadBalancerClient implements Client {
|
public class RetryableFeignBlockingLoadBalancerClient implements Client {
|
||||||
|
|
||||||
private static final Log LOG = LogFactory
|
private static final Log LOG = LogFactory.getLog(FeignBlockingLoadBalancerClient.class);
|
||||||
.getLog(FeignBlockingLoadBalancerClient.class);
|
|
||||||
|
|
||||||
private final Client delegate;
|
private final Client delegate;
|
||||||
|
|
||||||
private final BlockingLoadBalancerClient loadBalancerClient;
|
private final LoadBalancerClient loadBalancerClient;
|
||||||
|
|
||||||
private final LoadBalancedRetryFactory loadBalancedRetryFactory;
|
private final LoadBalancedRetryFactory loadBalancedRetryFactory;
|
||||||
|
|
||||||
public RetryableFeignBlockingLoadBalancerClient(Client delegate,
|
private final LoadBalancerProperties properties;
|
||||||
BlockingLoadBalancerClient loadBalancerClient,
|
|
||||||
LoadBalancedRetryFactory loadBalancedRetryFactory) {
|
private final LoadBalancerClientFactory loadBalancerClientFactory;
|
||||||
|
|
||||||
|
public RetryableFeignBlockingLoadBalancerClient(Client delegate, LoadBalancerClient loadBalancerClient,
|
||||||
|
LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerProperties properties,
|
||||||
|
LoadBalancerClientFactory loadBalancerClientFactory) {
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
this.loadBalancerClient = loadBalancerClient;
|
this.loadBalancerClient = loadBalancerClient;
|
||||||
this.loadBalancedRetryFactory = loadBalancedRetryFactory;
|
this.loadBalancedRetryFactory = loadBalancedRetryFactory;
|
||||||
|
this.properties = properties;
|
||||||
|
this.loadBalancerClientFactory = loadBalancerClientFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response execute(Request request, Request.Options options) throws IOException {
|
public Response execute(Request request, Request.Options options) throws IOException {
|
||||||
final URI originalUri = URI.create(request.url());
|
final URI originalUri = URI.create(request.url());
|
||||||
String serviceId = originalUri.getHost();
|
String serviceId = originalUri.getHost();
|
||||||
final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryFactory
|
Assert.state(serviceId != null, "Request URI does not contain a valid hostname: " + originalUri);
|
||||||
.createRetryPolicy(serviceId, loadBalancerClient);
|
final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryFactory.createRetryPolicy(serviceId,
|
||||||
|
loadBalancerClient);
|
||||||
RetryTemplate retryTemplate = buildRetryTemplate(serviceId, request, retryPolicy);
|
RetryTemplate retryTemplate = buildRetryTemplate(serviceId, request, retryPolicy);
|
||||||
return retryTemplate.execute(context -> {
|
return retryTemplate.execute(context -> {
|
||||||
Request feignRequest = null;
|
Request feignRequest = null;
|
||||||
|
ServiceInstance retrievedServiceInstance = null;
|
||||||
|
Set<LoadBalancerLifecycle> supportedLifecycleProcessors = LoadBalancerLifecycleValidator
|
||||||
|
.getSupportedLifecycleProcessors(
|
||||||
|
loadBalancerClientFactory.getInstances(serviceId, LoadBalancerLifecycle.class),
|
||||||
|
RetryableRequestContext.class, RequestData.class, ServiceInstance.class);
|
||||||
// On retries the policy will choose the server and set it in the context
|
// On retries the policy will choose the server and set it in the context
|
||||||
// and extract the server and update the request being made
|
// and extract the server and update the request being made
|
||||||
if (context instanceof LoadBalancedRetryContext) {
|
if (context instanceof LoadBalancedRetryContext) {
|
||||||
ServiceInstance serviceInstance = ((LoadBalancedRetryContext) context)
|
LoadBalancedRetryContext lbContext = (LoadBalancedRetryContext) context;
|
||||||
.getServiceInstance();
|
ServiceInstance serviceInstance = lbContext.getServiceInstance();
|
||||||
if (serviceInstance != null) {
|
if (serviceInstance == null) {
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug(String.format(
|
LOG.debug("Service instance retrieved from LoadBalancedRetryContext: was null. "
|
||||||
"Using service instance from LoadBalancedRetryContext: %s",
|
+ "Reattempting service instance selection");
|
||||||
serviceInstance));
|
|
||||||
}
|
}
|
||||||
String reconstructedUrl = loadBalancerClient
|
ServiceInstance previousServiceInstance = lbContext.getPreviousServiceInstance();
|
||||||
.reconstructURI(serviceInstance, originalUri).toString();
|
String hint = getHint(serviceId);
|
||||||
|
DefaultRequest<RetryableRequestContext> lbRequest = new DefaultRequest<>(
|
||||||
|
new RetryableRequestContext(previousServiceInstance, request, hint));
|
||||||
|
supportedLifecycleProcessors.forEach(lifecycle -> lifecycle.onStart(lbRequest));
|
||||||
|
retrievedServiceInstance = loadBalancerClient.choose(serviceId, lbRequest);
|
||||||
|
if (LOG.isDebugEnabled()) {
|
||||||
|
LOG.debug(String.format("Selected service instance: %s", retrievedServiceInstance));
|
||||||
|
}
|
||||||
|
lbContext.setServiceInstance(retrievedServiceInstance);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (retrievedServiceInstance == null) {
|
||||||
|
if (LOG.isWarnEnabled()) {
|
||||||
|
LOG.warn("Service instance was not resolved, executing the original request");
|
||||||
|
}
|
||||||
|
org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse = new DefaultResponse(
|
||||||
|
retrievedServiceInstance);
|
||||||
|
supportedLifecycleProcessors.forEach(
|
||||||
|
lifecycle -> lifecycle.onComplete(new CompletionContext<ResponseData, ServiceInstance>(
|
||||||
|
CompletionContext.Status.DISCARD, lbResponse)));
|
||||||
|
feignRequest = request;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (LOG.isDebugEnabled()) {
|
||||||
|
LOG.debug(String.format("Using service instance from LoadBalancedRetryContext: %s",
|
||||||
|
retrievedServiceInstance));
|
||||||
|
}
|
||||||
|
String reconstructedUrl = loadBalancerClient.reconstructURI(retrievedServiceInstance, originalUri)
|
||||||
|
.toString();
|
||||||
feignRequest = buildRequest(request, reconstructedUrl);
|
feignRequest = buildRequest(request, reconstructedUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (feignRequest == null) {
|
org.springframework.cloud.client.loadbalancer.Response<ServiceInstance> lbResponse = new DefaultResponse(
|
||||||
if (LOG.isWarnEnabled()) {
|
retrievedServiceInstance);
|
||||||
LOG.warn(
|
Response response = executeWithLoadBalancerLifecycleProcessing(delegate, options, feignRequest, lbResponse,
|
||||||
"Service instance was not resolved, executing the original request");
|
supportedLifecycleProcessors, retrievedServiceInstance != null);
|
||||||
}
|
|
||||||
feignRequest = request;
|
|
||||||
}
|
|
||||||
Response response = delegate.execute(feignRequest, options);
|
|
||||||
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()) {
|
||||||
LOG.debug(
|
LOG.debug(String.format("Retrying on status code: %d", responseStatus));
|
||||||
String.format("Retrying on status code: %d", responseStatus));
|
|
||||||
}
|
}
|
||||||
response.close();
|
response.close();
|
||||||
throw new RetryableStatusCodeException(serviceId, responseStatus,
|
throw new RetryableStatusCodeException(serviceId, responseStatus, response, URI.create(request.url()));
|
||||||
response, URI.create(request.url()));
|
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
}, new LoadBalancedRecoveryCallback<Response, Response>() {
|
}, new LoadBalancedRecoveryCallback<Response, Response>() {
|
||||||
@@ -126,26 +173,21 @@ public class RetryableFeignBlockingLoadBalancerClient implements Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected Request buildRequest(Request request, String reconstructedUrl) {
|
protected Request buildRequest(Request request, String reconstructedUrl) {
|
||||||
return Request.create(request.httpMethod(), reconstructedUrl, request.headers(),
|
return Request.create(request.httpMethod(), reconstructedUrl, request.headers(), request.body(),
|
||||||
request.body(), request.charset(), request.requestTemplate());
|
request.charset(), request.requestTemplate());
|
||||||
}
|
}
|
||||||
|
|
||||||
private RetryTemplate buildRetryTemplate(String serviceId, Request request,
|
private RetryTemplate buildRetryTemplate(String serviceId, Request request, LoadBalancedRetryPolicy retryPolicy) {
|
||||||
LoadBalancedRetryPolicy retryPolicy) {
|
|
||||||
RetryTemplate retryTemplate = new RetryTemplate();
|
RetryTemplate retryTemplate = new RetryTemplate();
|
||||||
BackOffPolicy backOffPolicy = this.loadBalancedRetryFactory
|
BackOffPolicy backOffPolicy = this.loadBalancedRetryFactory.createBackOffPolicy(serviceId);
|
||||||
.createBackOffPolicy(serviceId);
|
retryTemplate.setBackOffPolicy(backOffPolicy == null ? new NoBackOffPolicy() : backOffPolicy);
|
||||||
retryTemplate.setBackOffPolicy(
|
RetryListener[] retryListeners = this.loadBalancedRetryFactory.createRetryListeners(serviceId);
|
||||||
backOffPolicy == null ? new NoBackOffPolicy() : backOffPolicy);
|
|
||||||
RetryListener[] retryListeners = this.loadBalancedRetryFactory
|
|
||||||
.createRetryListeners(serviceId);
|
|
||||||
if (retryListeners != null && retryListeners.length != 0) {
|
if (retryListeners != null && retryListeners.length != 0) {
|
||||||
retryTemplate.setListeners(retryListeners);
|
retryTemplate.setListeners(retryListeners);
|
||||||
}
|
}
|
||||||
|
|
||||||
retryTemplate.setRetryPolicy(retryPolicy == null ? new NeverRetryPolicy()
|
retryTemplate.setRetryPolicy(retryPolicy == null ? new NeverRetryPolicy()
|
||||||
: new InterceptorRetryPolicy(toHttpRequest(request), retryPolicy,
|
: new InterceptorRetryPolicy(toHttpRequest(request), retryPolicy, loadBalancerClient, serviceId));
|
||||||
loadBalancerClient, serviceId));
|
|
||||||
return retryTemplate;
|
return retryTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,4 +227,10 @@ public class RetryableFeignBlockingLoadBalancerClient implements Client {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getHint(String serviceId) {
|
||||||
|
String defaultHint = properties.getHint().getOrDefault("default", "default");
|
||||||
|
String hintPropertyValue = properties.getHint().get(serviceId);
|
||||||
|
return hintPropertyValue != null ? hintPropertyValue : defaultHint;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
-72
@@ -1,72 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2020 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign.ribbon;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.netflix.client.config.IClientConfig;
|
|
||||||
import com.netflix.loadbalancer.ILoadBalancer;
|
|
||||||
|
|
||||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
|
|
||||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
|
||||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
|
||||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Factory for SpringLoadBalancer instances that caches the entries created.
|
|
||||||
*
|
|
||||||
* @author Spencer Gibb
|
|
||||||
* @author Dave Syer
|
|
||||||
* @author Ryan Baxter
|
|
||||||
* @author Gang Li
|
|
||||||
*/
|
|
||||||
public class CachingSpringLoadBalancerFactory {
|
|
||||||
|
|
||||||
protected final SpringClientFactory factory;
|
|
||||||
|
|
||||||
protected LoadBalancedRetryFactory loadBalancedRetryFactory = null;
|
|
||||||
|
|
||||||
private volatile Map<String, FeignLoadBalancer> cache = new ConcurrentReferenceHashMap<>();
|
|
||||||
|
|
||||||
public CachingSpringLoadBalancerFactory(SpringClientFactory factory) {
|
|
||||||
this.factory = factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CachingSpringLoadBalancerFactory(SpringClientFactory factory,
|
|
||||||
LoadBalancedRetryFactory loadBalancedRetryPolicyFactory) {
|
|
||||||
this.factory = factory;
|
|
||||||
this.loadBalancedRetryFactory = loadBalancedRetryPolicyFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FeignLoadBalancer create(String clientName) {
|
|
||||||
FeignLoadBalancer client = this.cache.get(clientName);
|
|
||||||
if (client != null) {
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
IClientConfig config = this.factory.getClientConfig(clientName);
|
|
||||||
ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
|
|
||||||
ServerIntrospector serverIntrospector = this.factory.getInstance(clientName,
|
|
||||||
ServerIntrospector.class);
|
|
||||||
client = this.loadBalancedRetryFactory != null
|
|
||||||
? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
|
|
||||||
this.loadBalancedRetryFactory)
|
|
||||||
: new FeignLoadBalancer(lb, config, serverIntrospector);
|
|
||||||
this.cache.put(clientName, client);
|
|
||||||
return client;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-40
@@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2020 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign.ribbon;
|
|
||||||
|
|
||||||
import feign.Client;
|
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
|
||||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Spencer Gibb
|
|
||||||
*/
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
|
||||||
class DefaultFeignLoadBalancedConfiguration {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean
|
|
||||||
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
|
|
||||||
SpringClientFactory clientFactory) {
|
|
||||||
return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,
|
|
||||||
clientFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-245
@@ -1,245 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2020 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign.ribbon;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import com.netflix.client.AbstractLoadBalancerAwareClient;
|
|
||||||
import com.netflix.client.ClientRequest;
|
|
||||||
import com.netflix.client.IResponse;
|
|
||||||
import com.netflix.client.RequestSpecificRetryHandler;
|
|
||||||
import com.netflix.client.RetryHandler;
|
|
||||||
import com.netflix.client.config.IClientConfig;
|
|
||||||
import com.netflix.loadbalancer.ILoadBalancer;
|
|
||||||
import com.netflix.loadbalancer.Server;
|
|
||||||
import feign.Client;
|
|
||||||
import feign.Request;
|
|
||||||
import feign.Response;
|
|
||||||
|
|
||||||
import org.springframework.cloud.netflix.ribbon.RibbonProperties;
|
|
||||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
|
||||||
import org.springframework.http.HttpHeaders;
|
|
||||||
import org.springframework.http.HttpMethod;
|
|
||||||
import org.springframework.http.HttpRequest;
|
|
||||||
|
|
||||||
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToSecureConnectionIfNeeded;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Dave Syer
|
|
||||||
* @author Spencer Gibb
|
|
||||||
* @author Ryan Baxter
|
|
||||||
* @author Tim Ysewyn
|
|
||||||
* @author Olga Maciaszek-Sharma
|
|
||||||
*/
|
|
||||||
public class FeignLoadBalancer extends
|
|
||||||
AbstractLoadBalancerAwareClient<FeignLoadBalancer.RibbonRequest, FeignLoadBalancer.RibbonResponse> {
|
|
||||||
|
|
||||||
private final RibbonProperties ribbon;
|
|
||||||
|
|
||||||
protected int connectTimeout;
|
|
||||||
|
|
||||||
protected int readTimeout;
|
|
||||||
|
|
||||||
protected IClientConfig clientConfig;
|
|
||||||
|
|
||||||
protected ServerIntrospector serverIntrospector;
|
|
||||||
|
|
||||||
public FeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig,
|
|
||||||
ServerIntrospector serverIntrospector) {
|
|
||||||
super(lb, clientConfig);
|
|
||||||
this.setRetryHandler(RetryHandler.DEFAULT);
|
|
||||||
this.clientConfig = clientConfig;
|
|
||||||
this.ribbon = RibbonProperties.from(clientConfig);
|
|
||||||
RibbonProperties ribbon = this.ribbon;
|
|
||||||
this.connectTimeout = ribbon.getConnectTimeout();
|
|
||||||
this.readTimeout = ribbon.getReadTimeout();
|
|
||||||
this.serverIntrospector = serverIntrospector;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride)
|
|
||||||
throws IOException {
|
|
||||||
Request.Options options;
|
|
||||||
if (configOverride != null) {
|
|
||||||
RibbonProperties override = RibbonProperties.from(configOverride);
|
|
||||||
options = new Request.Options(override.connectTimeout(this.connectTimeout),
|
|
||||||
override.readTimeout(this.readTimeout));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
options = new Request.Options(this.connectTimeout, this.readTimeout);
|
|
||||||
}
|
|
||||||
Response response = request.client().execute(request.toRequest(), options);
|
|
||||||
return new RibbonResponse(request.getUri(), response);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
|
|
||||||
RibbonRequest request, IClientConfig requestConfig) {
|
|
||||||
if (this.ribbon.isOkToRetryOnAllOperations()) {
|
|
||||||
return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
|
|
||||||
requestConfig);
|
|
||||||
}
|
|
||||||
if (!request.toRequest().httpMethod().name().equals("GET")) {
|
|
||||||
return new RequestSpecificRetryHandler(true, false, this.getRetryHandler(),
|
|
||||||
requestConfig);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
|
|
||||||
requestConfig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public URI reconstructURIWithServer(Server server, URI original) {
|
|
||||||
URI uri = updateToSecureConnectionIfNeeded(original, this.clientConfig,
|
|
||||||
this.serverIntrospector, server);
|
|
||||||
return super.reconstructURIWithServer(server, uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static class RibbonRequest extends ClientRequest implements Cloneable {
|
|
||||||
|
|
||||||
private final Request request;
|
|
||||||
|
|
||||||
private final Client client;
|
|
||||||
|
|
||||||
protected RibbonRequest(Client client, Request request, URI uri) {
|
|
||||||
this.client = client;
|
|
||||||
setUri(uri);
|
|
||||||
this.request = toRequest(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Request toRequest(Request request) {
|
|
||||||
Map<String, Collection<String>> headers = new LinkedHashMap<>(
|
|
||||||
request.headers());
|
|
||||||
return Request.create(request.httpMethod(), getUri().toASCIIString(), headers,
|
|
||||||
request.body(), request.charset(), request.requestTemplate());
|
|
||||||
}
|
|
||||||
|
|
||||||
Request toRequest() {
|
|
||||||
return toRequest(this.request);
|
|
||||||
}
|
|
||||||
|
|
||||||
Client client() {
|
|
||||||
return this.client;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpRequest toHttpRequest() {
|
|
||||||
return new HttpRequest() {
|
|
||||||
@Override
|
|
||||||
public HttpMethod getMethod() {
|
|
||||||
return HttpMethod
|
|
||||||
.resolve(RibbonRequest.this.toRequest().httpMethod().name());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getMethodValue() {
|
|
||||||
return getMethod().name();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public URI getURI() {
|
|
||||||
return RibbonRequest.this.getUri();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public HttpHeaders getHeaders() {
|
|
||||||
Map<String, List<String>> headers = new HashMap<>();
|
|
||||||
Map<String, Collection<String>> feignHeaders = RibbonRequest.this
|
|
||||||
.toRequest().headers();
|
|
||||||
for (String key : feignHeaders.keySet()) {
|
|
||||||
headers.put(key, new ArrayList<>(feignHeaders.get(key)));
|
|
||||||
}
|
|
||||||
HttpHeaders httpHeaders = new HttpHeaders();
|
|
||||||
httpHeaders.putAll(headers);
|
|
||||||
return httpHeaders;
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public Request getRequest() {
|
|
||||||
return this.request;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Client getClient() {
|
|
||||||
return this.client;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object clone() {
|
|
||||||
return new RibbonRequest(this.client, this.request, getUri());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static class RibbonResponse implements IResponse {
|
|
||||||
|
|
||||||
private final URI uri;
|
|
||||||
|
|
||||||
private final Response response;
|
|
||||||
|
|
||||||
protected RibbonResponse(URI uri, Response response) {
|
|
||||||
this.uri = uri;
|
|
||||||
this.response = response;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getPayload() {
|
|
||||||
return this.response.body();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasPayload() {
|
|
||||||
return this.response.body() != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSuccess() {
|
|
||||||
return this.response.status() == 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public URI getRequestedURI() {
|
|
||||||
return this.uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, Collection<String>> getHeaders() {
|
|
||||||
return this.response.headers();
|
|
||||||
}
|
|
||||||
|
|
||||||
Response toResponse() {
|
|
||||||
return this.response;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() throws IOException {
|
|
||||||
if (this.response != null && this.response.body() != null) {
|
|
||||||
this.response.body().close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-129
@@ -1,129 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2020 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign.ribbon;
|
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.springframework.cloud.client.ServiceInstance;
|
|
||||||
import org.springframework.cloud.client.loadbalancer.InterceptorRetryPolicy;
|
|
||||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
|
|
||||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
|
|
||||||
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
|
|
||||||
import org.springframework.http.HttpRequest;
|
|
||||||
import org.springframework.retry.RetryContext;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Ryan Baxter
|
|
||||||
*/
|
|
||||||
public class FeignRetryPolicy extends InterceptorRetryPolicy {
|
|
||||||
|
|
||||||
private HttpRequest request;
|
|
||||||
|
|
||||||
private String serviceId;
|
|
||||||
|
|
||||||
public FeignRetryPolicy(HttpRequest request, LoadBalancedRetryPolicy policy,
|
|
||||||
ServiceInstanceChooser serviceInstanceChooser, String serviceName) {
|
|
||||||
super(request, policy, serviceInstanceChooser, serviceName);
|
|
||||||
this.request = request;
|
|
||||||
this.serviceId = serviceName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canRetry(RetryContext context) {
|
|
||||||
/*
|
|
||||||
* In InterceptorRetryPolicy.canRetry we ask the LoadBalancer to choose a server
|
|
||||||
* if one is not set in the retry context and then return true. RetryTemplat calls
|
|
||||||
* the canRetry method of the policy even on its first execution. So the fact that
|
|
||||||
* we didnt have a service instance set in the RetryContext signaled that it was
|
|
||||||
* the first execution and we should return true.
|
|
||||||
*
|
|
||||||
* In the Feign scenario, Feign as actually already queried the load balancer for
|
|
||||||
* a service instance and we set that service instance in the context when we call
|
|
||||||
* the open method of the policy. So in the Feign case we just return true if the
|
|
||||||
* retry count is 0 indicating we haven't yet made a failed request.
|
|
||||||
*/
|
|
||||||
if (context.getRetryCount() == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return super.canRetry(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RetryContext open(RetryContext parent) {
|
|
||||||
/*
|
|
||||||
* With Feign (unlike Ribbon) the request already has the URI for the service
|
|
||||||
* instance we are going to make the request to, so extract that information and
|
|
||||||
* set the service instance in the context. In the Ribbon scenario the URI in the
|
|
||||||
* request object still has the service id so we choose and set the service
|
|
||||||
* instance later on.
|
|
||||||
*/
|
|
||||||
LoadBalancedRetryContext context = new LoadBalancedRetryContext(parent,
|
|
||||||
this.request);
|
|
||||||
context.setServiceInstance(
|
|
||||||
new FeignRetryPolicyServiceInstance(this.serviceId, this.request));
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
class FeignRetryPolicyServiceInstance implements ServiceInstance {
|
|
||||||
|
|
||||||
private String serviceId;
|
|
||||||
|
|
||||||
private HttpRequest request;
|
|
||||||
|
|
||||||
private Map<String, String> metadata;
|
|
||||||
|
|
||||||
FeignRetryPolicyServiceInstance(String serviceId, HttpRequest request) {
|
|
||||||
this.serviceId = serviceId;
|
|
||||||
this.request = request;
|
|
||||||
this.metadata = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getServiceId() {
|
|
||||||
return this.serviceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getHost() {
|
|
||||||
return this.request.getURI().getHost();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getPort() {
|
|
||||||
return this.request.getURI().getPort();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSecure() {
|
|
||||||
return "https".equals(this.request.getURI().getScheme());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public URI getUri() {
|
|
||||||
return this.request.getURI();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, String> getMetadata() {
|
|
||||||
return this.metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-83
@@ -1,83 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2020 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign.ribbon;
|
|
||||||
|
|
||||||
import com.netflix.loadbalancer.ILoadBalancer;
|
|
||||||
import feign.Feign;
|
|
||||||
import feign.Request;
|
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
|
||||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
|
|
||||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
|
||||||
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
|
||||||
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.context.annotation.Import;
|
|
||||||
import org.springframework.context.annotation.Primary;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Autoconfiguration to be activated if Feign is in use and needs to be use Ribbon as a
|
|
||||||
* load balancer.
|
|
||||||
*
|
|
||||||
* @author Dave Syer
|
|
||||||
* @author Olga Maciaszek-Sharma
|
|
||||||
*/
|
|
||||||
@ConditionalOnClass({ ILoadBalancer.class, Feign.class })
|
|
||||||
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.ribbon.enabled",
|
|
||||||
matchIfMissing = true)
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
|
||||||
@AutoConfigureBefore(FeignAutoConfiguration.class)
|
|
||||||
@EnableConfigurationProperties({ FeignHttpClientProperties.class })
|
|
||||||
// Order is important here, last should be the default, first should be optional
|
|
||||||
// see
|
|
||||||
// https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
|
|
||||||
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
|
|
||||||
OkHttpFeignLoadBalancedConfiguration.class,
|
|
||||||
DefaultFeignLoadBalancedConfiguration.class })
|
|
||||||
public class FeignRibbonClientAutoConfiguration {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@Primary
|
|
||||||
@ConditionalOnMissingBean
|
|
||||||
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
|
|
||||||
public CachingSpringLoadBalancerFactory cachingLBClientFactory(
|
|
||||||
SpringClientFactory factory) {
|
|
||||||
return new CachingSpringLoadBalancerFactory(factory);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@Primary
|
|
||||||
@ConditionalOnMissingBean
|
|
||||||
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
|
|
||||||
public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(
|
|
||||||
SpringClientFactory factory, LoadBalancedRetryFactory retryFactory) {
|
|
||||||
return new CachingSpringLoadBalancerFactory(factory, retryFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean
|
|
||||||
public Request.Options feignRequestOptions() {
|
|
||||||
return LoadBalancerFeignClient.DEFAULT_OPTIONS;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-50
@@ -1,50 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2020 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign.ribbon;
|
|
||||||
|
|
||||||
import feign.Client;
|
|
||||||
import feign.httpclient.ApacheHttpClient;
|
|
||||||
import org.apache.http.client.HttpClient;
|
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
|
||||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
|
||||||
import org.springframework.cloud.openfeign.clientconfig.HttpClientFeignConfiguration;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.context.annotation.Import;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Spencer Gibb
|
|
||||||
* @author Olga Maciaszek-Sharma
|
|
||||||
*/
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
|
||||||
@ConditionalOnClass(ApacheHttpClient.class)
|
|
||||||
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
|
|
||||||
@Import(HttpClientFeignConfiguration.class)
|
|
||||||
class HttpClientFeignLoadBalancedConfiguration {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean(Client.class)
|
|
||||||
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
|
|
||||||
SpringClientFactory clientFactory, HttpClient httpClient) {
|
|
||||||
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
|
|
||||||
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-143
@@ -1,143 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2020 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign.ribbon;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URI;
|
|
||||||
|
|
||||||
import com.netflix.client.ClientException;
|
|
||||||
import com.netflix.client.config.CommonClientConfigKey;
|
|
||||||
import com.netflix.client.config.DefaultClientConfigImpl;
|
|
||||||
import com.netflix.client.config.IClientConfig;
|
|
||||||
import feign.Client;
|
|
||||||
import feign.Request;
|
|
||||||
import feign.Response;
|
|
||||||
|
|
||||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Dave Syer
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class LoadBalancerFeignClient implements Client {
|
|
||||||
|
|
||||||
static final Request.Options DEFAULT_OPTIONS = new Request.Options();
|
|
||||||
|
|
||||||
private final Client delegate;
|
|
||||||
|
|
||||||
private CachingSpringLoadBalancerFactory lbClientFactory;
|
|
||||||
|
|
||||||
private SpringClientFactory clientFactory;
|
|
||||||
|
|
||||||
public LoadBalancerFeignClient(Client delegate,
|
|
||||||
CachingSpringLoadBalancerFactory lbClientFactory,
|
|
||||||
SpringClientFactory clientFactory) {
|
|
||||||
this.delegate = delegate;
|
|
||||||
this.lbClientFactory = lbClientFactory;
|
|
||||||
this.clientFactory = clientFactory;
|
|
||||||
}
|
|
||||||
|
|
||||||
static URI cleanUrl(String originalUrl, String host) {
|
|
||||||
String newUrl = originalUrl;
|
|
||||||
if (originalUrl.startsWith("https://")) {
|
|
||||||
newUrl = originalUrl.substring(0, 8)
|
|
||||||
+ originalUrl.substring(8 + host.length());
|
|
||||||
}
|
|
||||||
else if (originalUrl.startsWith("http")) {
|
|
||||||
newUrl = originalUrl.substring(0, 7)
|
|
||||||
+ originalUrl.substring(7 + host.length());
|
|
||||||
}
|
|
||||||
StringBuffer buffer = new StringBuffer(newUrl);
|
|
||||||
if ((newUrl.startsWith("https://") && newUrl.length() == 8)
|
|
||||||
|| (newUrl.startsWith("http://") && newUrl.length() == 7)) {
|
|
||||||
buffer.append("/");
|
|
||||||
}
|
|
||||||
return URI.create(buffer.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Response execute(Request request, Request.Options options) throws IOException {
|
|
||||||
try {
|
|
||||||
URI asUri = URI.create(request.url());
|
|
||||||
String clientName = asUri.getHost();
|
|
||||||
URI uriWithoutHost = cleanUrl(request.url(), clientName);
|
|
||||||
FeignLoadBalancer.RibbonRequest ribbonRequest = new FeignLoadBalancer.RibbonRequest(
|
|
||||||
this.delegate, request, uriWithoutHost);
|
|
||||||
|
|
||||||
IClientConfig requestConfig = getClientConfig(options, clientName);
|
|
||||||
return lbClient(clientName)
|
|
||||||
.executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
|
|
||||||
}
|
|
||||||
catch (ClientException e) {
|
|
||||||
IOException io = findIOException(e);
|
|
||||||
if (io != null) {
|
|
||||||
throw io;
|
|
||||||
}
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IClientConfig getClientConfig(Request.Options options, String clientName) {
|
|
||||||
IClientConfig requestConfig;
|
|
||||||
if (options == DEFAULT_OPTIONS) {
|
|
||||||
requestConfig = this.clientFactory.getClientConfig(clientName);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
requestConfig = new FeignOptionsClientConfig(options);
|
|
||||||
}
|
|
||||||
return requestConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected IOException findIOException(Throwable t) {
|
|
||||||
if (t == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (t instanceof IOException) {
|
|
||||||
return (IOException) t;
|
|
||||||
}
|
|
||||||
return findIOException(t.getCause());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Client getDelegate() {
|
|
||||||
return this.delegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
private FeignLoadBalancer lbClient(String clientName) {
|
|
||||||
return this.lbClientFactory.create(clientName);
|
|
||||||
}
|
|
||||||
|
|
||||||
static class FeignOptionsClientConfig extends DefaultClientConfigImpl {
|
|
||||||
|
|
||||||
FeignOptionsClientConfig(Request.Options options) {
|
|
||||||
setProperty(CommonClientConfigKey.ConnectTimeout,
|
|
||||||
options.connectTimeoutMillis());
|
|
||||||
setProperty(CommonClientConfigKey.ReadTimeout, options.readTimeoutMillis());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void loadProperties(String clientName) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void loadDefaultValues() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-49
@@ -1,49 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2020 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign.ribbon;
|
|
||||||
|
|
||||||
import feign.Client;
|
|
||||||
import feign.okhttp.OkHttpClient;
|
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
|
||||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
|
||||||
import org.springframework.cloud.openfeign.clientconfig.OkHttpFeignConfiguration;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.context.annotation.Import;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Spencer Gibb
|
|
||||||
* @author Olga Maciaszek-Sharma
|
|
||||||
*/
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
|
||||||
@ConditionalOnClass(OkHttpClient.class)
|
|
||||||
@ConditionalOnProperty("feign.okhttp.enabled")
|
|
||||||
@Import(OkHttpFeignConfiguration.class)
|
|
||||||
class OkHttpFeignLoadBalancedConfiguration {
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean(Client.class)
|
|
||||||
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
|
|
||||||
SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) {
|
|
||||||
OkHttpClient delegate = new OkHttpClient(okHttpClient);
|
|
||||||
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-141
@@ -1,141 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2020 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign.ribbon;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URI;
|
|
||||||
|
|
||||||
import com.netflix.client.DefaultLoadBalancerRetryHandler;
|
|
||||||
import com.netflix.client.RequestSpecificRetryHandler;
|
|
||||||
import com.netflix.client.config.IClientConfig;
|
|
||||||
import com.netflix.loadbalancer.ILoadBalancer;
|
|
||||||
import com.netflix.loadbalancer.Server;
|
|
||||||
import feign.Request;
|
|
||||||
import feign.Response;
|
|
||||||
|
|
||||||
import org.springframework.cloud.client.ServiceInstance;
|
|
||||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRecoveryCallback;
|
|
||||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext;
|
|
||||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
|
|
||||||
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryPolicy;
|
|
||||||
import org.springframework.cloud.client.loadbalancer.ServiceInstanceChooser;
|
|
||||||
import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient;
|
|
||||||
import org.springframework.cloud.netflix.ribbon.RibbonProperties;
|
|
||||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
|
||||||
import org.springframework.retry.RetryListener;
|
|
||||||
import org.springframework.retry.backoff.BackOffPolicy;
|
|
||||||
import org.springframework.retry.backoff.NoBackOffPolicy;
|
|
||||||
import org.springframework.retry.policy.NeverRetryPolicy;
|
|
||||||
import org.springframework.retry.support.RetryTemplate;
|
|
||||||
import org.springframework.util.StreamUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link FeignLoadBalancer} that leverages Spring Retry to retry failed requests.
|
|
||||||
*
|
|
||||||
* @author Ryan Baxter
|
|
||||||
* @author Gang Li
|
|
||||||
*/
|
|
||||||
public class RetryableFeignLoadBalancer extends FeignLoadBalancer
|
|
||||||
implements ServiceInstanceChooser {
|
|
||||||
|
|
||||||
private final LoadBalancedRetryFactory loadBalancedRetryFactory;
|
|
||||||
|
|
||||||
public RetryableFeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig,
|
|
||||||
ServerIntrospector serverIntrospector,
|
|
||||||
LoadBalancedRetryFactory loadBalancedRetryFactory) {
|
|
||||||
super(lb, clientConfig, serverIntrospector);
|
|
||||||
this.loadBalancedRetryFactory = loadBalancedRetryFactory;
|
|
||||||
this.setRetryHandler(new DefaultLoadBalancerRetryHandler(clientConfig));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RibbonResponse execute(final RibbonRequest request,
|
|
||||||
IClientConfig configOverride) throws IOException {
|
|
||||||
final Request.Options options;
|
|
||||||
if (configOverride != null) {
|
|
||||||
RibbonProperties ribbon = RibbonProperties.from(configOverride);
|
|
||||||
options = new Request.Options(ribbon.connectTimeout(this.connectTimeout),
|
|
||||||
ribbon.readTimeout(this.readTimeout));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
options = new Request.Options(this.connectTimeout, this.readTimeout);
|
|
||||||
}
|
|
||||||
final LoadBalancedRetryPolicy retryPolicy = this.loadBalancedRetryFactory
|
|
||||||
.createRetryPolicy(this.getClientName(), this);
|
|
||||||
RetryTemplate retryTemplate = new RetryTemplate();
|
|
||||||
BackOffPolicy backOffPolicy = this.loadBalancedRetryFactory
|
|
||||||
.createBackOffPolicy(this.getClientName());
|
|
||||||
retryTemplate.setBackOffPolicy(
|
|
||||||
backOffPolicy == null ? new NoBackOffPolicy() : backOffPolicy);
|
|
||||||
RetryListener[] retryListeners = this.loadBalancedRetryFactory
|
|
||||||
.createRetryListeners(this.getClientName());
|
|
||||||
if (retryListeners != null && retryListeners.length != 0) {
|
|
||||||
retryTemplate.setListeners(retryListeners);
|
|
||||||
}
|
|
||||||
retryTemplate.setRetryPolicy(retryPolicy == null ? new NeverRetryPolicy()
|
|
||||||
: new FeignRetryPolicy(request.toHttpRequest(), retryPolicy, this,
|
|
||||||
this.getClientName()));
|
|
||||||
return retryTemplate.execute(retryContext -> {
|
|
||||||
Request feignRequest = null;
|
|
||||||
// on retries the policy will choose the server and set it in the context
|
|
||||||
// extract the server and update the request being made
|
|
||||||
if (retryContext instanceof LoadBalancedRetryContext) {
|
|
||||||
ServiceInstance service = ((LoadBalancedRetryContext) retryContext)
|
|
||||||
.getServiceInstance();
|
|
||||||
if (service != null) {
|
|
||||||
feignRequest = ((RibbonRequest) request
|
|
||||||
.replaceUri(reconstructURIWithServer(
|
|
||||||
new Server(service.getHost(), service.getPort()),
|
|
||||||
request.getUri()))).toRequest();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (feignRequest == null) {
|
|
||||||
feignRequest = request.toRequest();
|
|
||||||
}
|
|
||||||
Response response = request.client().execute(feignRequest, options);
|
|
||||||
if (retryPolicy != null
|
|
||||||
&& retryPolicy.retryableStatusCode(response.status())) {
|
|
||||||
byte[] byteArray = response.body() == null ? new byte[] {}
|
|
||||||
: StreamUtils.copyToByteArray(response.body().asInputStream());
|
|
||||||
response.close();
|
|
||||||
throw new RibbonResponseStatusCodeException(
|
|
||||||
RetryableFeignLoadBalancer.this.clientName, response, byteArray,
|
|
||||||
request.getUri());
|
|
||||||
}
|
|
||||||
return new RibbonResponse(request.getUri(), response);
|
|
||||||
}, new LoadBalancedRecoveryCallback<RibbonResponse, Response>() {
|
|
||||||
@Override
|
|
||||||
protected RibbonResponse createResponse(Response response, URI uri) {
|
|
||||||
return new RibbonResponse(uri, response);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
|
|
||||||
FeignLoadBalancer.RibbonRequest request, IClientConfig requestConfig) {
|
|
||||||
return new RequestSpecificRetryHandler(false, false, this.getRetryHandler(),
|
|
||||||
requestConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ServiceInstance choose(String serviceId) {
|
|
||||||
return new RibbonLoadBalancerClient.RibbonServer(serviceId,
|
|
||||||
this.getLoadBalancer().chooseServer(serviceId));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-49
@@ -1,49 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2020 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign.ribbon;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.net.URI;
|
|
||||||
|
|
||||||
import feign.Response;
|
|
||||||
|
|
||||||
import org.springframework.cloud.client.loadbalancer.RetryableStatusCodeException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link RetryableStatusCodeException} for {@link Response}s.
|
|
||||||
*
|
|
||||||
* @author Ryan Baxter
|
|
||||||
*/
|
|
||||||
public class RibbonResponseStatusCodeException extends RetryableStatusCodeException {
|
|
||||||
|
|
||||||
private final Response response;
|
|
||||||
|
|
||||||
public RibbonResponseStatusCodeException(String serviceId, Response response,
|
|
||||||
byte[] body, URI uri) {
|
|
||||||
super(serviceId, response.status(), response, uri);
|
|
||||||
this.response = Response.builder()
|
|
||||||
.body(new ByteArrayInputStream(body), body.length)
|
|
||||||
.headers(response.headers()).reason(response.reason())
|
|
||||||
.status(response.status()).request(response.request()).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Response getResponse() {
|
|
||||||
return this.response;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
+4
-7
@@ -29,7 +29,6 @@ import org.springframework.http.MediaType;
|
|||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import static feign.form.ContentProcessor.CRLF;
|
import static feign.form.ContentProcessor.CRLF;
|
||||||
import static feign.form.util.PojoUtil.isUserPojo;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Darren Foong
|
* @author Darren Foong
|
||||||
@@ -45,12 +44,10 @@ public abstract class AbstractFormWriter extends AbstractWriter {
|
|||||||
@Override
|
@Override
|
||||||
public void write(Output output, String key, Object object) throws EncodeException {
|
public void write(Output output, String key, Object object) throws EncodeException {
|
||||||
try {
|
try {
|
||||||
String string = new StringBuilder()
|
String string = new StringBuilder().append("Content-Disposition: form-data; name=\"").append(key)
|
||||||
.append("Content-Disposition: form-data; name=\"").append(key)
|
.append('"').append(CRLF).append("Content-Type: ").append(getContentType()).append("; charset=")
|
||||||
.append('"').append(CRLF).append("Content-Type: ")
|
.append(output.getCharset().name()).append(CRLF).append(CRLF).append(writeAsString(object))
|
||||||
.append(getContentType()).append("; charset=")
|
.toString();
|
||||||
.append(output.getCharset().name()).append(CRLF).append(CRLF)
|
|
||||||
.append(writeAsString(object)).toString();
|
|
||||||
|
|
||||||
output.write(string);
|
output.write(string);
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-10
@@ -45,19 +45,15 @@ public class DefaultGzipDecoder implements Decoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object decode(final Response response, Type type)
|
public Object decode(final Response response, Type type) throws IOException, FeignException {
|
||||||
throws IOException, FeignException {
|
Collection<String> encoding = response.headers().containsKey(HttpEncoding.CONTENT_ENCODING_HEADER)
|
||||||
Collection<String> encoding = response.headers()
|
? response.headers().get(HttpEncoding.CONTENT_ENCODING_HEADER) : null;
|
||||||
.containsKey(HttpEncoding.CONTENT_ENCODING_HEADER)
|
|
||||||
? response.headers().get(HttpEncoding.CONTENT_ENCODING_HEADER)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
if (encoding != null) {
|
if (encoding != null) {
|
||||||
if (encoding.contains(HttpEncoding.GZIP_ENCODING)) {
|
if (encoding.contains(HttpEncoding.GZIP_ENCODING)) {
|
||||||
String decompressedBody = decompress(response);
|
String decompressedBody = decompress(response);
|
||||||
if (decompressedBody != null) {
|
if (decompressedBody != null) {
|
||||||
Response decompressedResponse = response.toBuilder()
|
Response decompressedResponse = response.toBuilder().body(decompressedBody.getBytes()).build();
|
||||||
.body(decompressedBody.getBytes()).build();
|
|
||||||
return decoder.decode(decompressedResponse, type);
|
return decoder.decode(decompressedResponse, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -69,8 +65,7 @@ public class DefaultGzipDecoder implements Decoder {
|
|||||||
if (response.body() == null) {
|
if (response.body() == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try (GZIPInputStream gzipInputStream = new GZIPInputStream(
|
try (GZIPInputStream gzipInputStream = new GZIPInputStream(response.body().asInputStream());
|
||||||
response.body().asInputStream());
|
|
||||||
BufferedReader reader = new BufferedReader(
|
BufferedReader reader = new BufferedReader(
|
||||||
new InputStreamReader(gzipInputStream, StandardCharsets.UTF_8))) {
|
new InputStreamReader(gzipInputStream, StandardCharsets.UTF_8))) {
|
||||||
String outputString = "";
|
String outputString = "";
|
||||||
|
|||||||
+3
-4
@@ -43,8 +43,7 @@ public class DefaultGzipDecoderConfiguration {
|
|||||||
|
|
||||||
private ObjectFactory<HttpMessageConverters> messageConverters;
|
private ObjectFactory<HttpMessageConverters> messageConverters;
|
||||||
|
|
||||||
public DefaultGzipDecoderConfiguration(
|
public DefaultGzipDecoderConfiguration(ObjectFactory<HttpMessageConverters> messageConverters) {
|
||||||
ObjectFactory<HttpMessageConverters> messageConverters) {
|
|
||||||
this.messageConverters = messageConverters;
|
this.messageConverters = messageConverters;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,8 +51,8 @@ public class DefaultGzipDecoderConfiguration {
|
|||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
@ConditionalOnProperty("feign.compression.response.useGzipDecoder")
|
@ConditionalOnProperty("feign.compression.response.useGzipDecoder")
|
||||||
public Decoder defaultGzipDecoder() {
|
public Decoder defaultGzipDecoder() {
|
||||||
return new OptionalDecoder(new ResponseEntityDecoder(
|
return new OptionalDecoder(
|
||||||
new DefaultGzipDecoder(new SpringDecoder(messageConverters))));
|
new ResponseEntityDecoder(new DefaultGzipDecoder(new SpringDecoder(messageConverters))));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
-79
@@ -1,79 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2020 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign.support;
|
|
||||||
|
|
||||||
import com.netflix.hystrix.HystrixCommand;
|
|
||||||
import com.netflix.hystrix.HystrixCommandGroupKey;
|
|
||||||
import com.netflix.hystrix.HystrixThreadPoolKey;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convenience class for implementing feign fallbacks that return {@link HystrixCommand}.
|
|
||||||
* Also useful for return types of {@link rx.Observable} and
|
|
||||||
* {@link java.util.concurrent.Future}. For those return types, just call
|
|
||||||
* {@link FallbackCommand#observe()} or {@link FallbackCommand#queue()} respectively.
|
|
||||||
*
|
|
||||||
* @param <T> result type
|
|
||||||
* @author Spencer Gibb
|
|
||||||
*/
|
|
||||||
public class FallbackCommand<T> extends HystrixCommand<T> {
|
|
||||||
|
|
||||||
private T result;
|
|
||||||
|
|
||||||
public FallbackCommand(T result) {
|
|
||||||
this(result, "fallback");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected FallbackCommand(T result, String groupname) {
|
|
||||||
super(HystrixCommandGroupKey.Factory.asKey(groupname));
|
|
||||||
this.result = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FallbackCommand(T result, HystrixCommandGroupKey group) {
|
|
||||||
super(group);
|
|
||||||
this.result = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FallbackCommand(T result, HystrixCommandGroupKey group,
|
|
||||||
int executionIsolationThreadTimeoutInMilliseconds) {
|
|
||||||
super(group, executionIsolationThreadTimeoutInMilliseconds);
|
|
||||||
this.result = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FallbackCommand(T result, HystrixCommandGroupKey group,
|
|
||||||
HystrixThreadPoolKey threadPool) {
|
|
||||||
super(group, threadPool);
|
|
||||||
this.result = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FallbackCommand(T result, HystrixCommandGroupKey group,
|
|
||||||
HystrixThreadPoolKey threadPool,
|
|
||||||
int executionIsolationThreadTimeoutInMilliseconds) {
|
|
||||||
super(group, threadPool, executionIsolationThreadTimeoutInMilliseconds);
|
|
||||||
this.result = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FallbackCommand(T result, Setter setter) {
|
|
||||||
super(setter);
|
|
||||||
this.result = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected T run() throws Exception {
|
|
||||||
return this.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
+2
-4
@@ -54,10 +54,8 @@ public final class FeignUtils {
|
|||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Collection<String> addTemplateParameter(Collection<String> possiblyNull,
|
static Collection<String> addTemplateParameter(Collection<String> possiblyNull, String paramName) {
|
||||||
String paramName) {
|
Collection<String> params = ofNullable(possiblyNull).map(ArrayList::new).orElse(new ArrayList<>());
|
||||||
Collection<String> params = ofNullable(possiblyNull).map(ArrayList::new)
|
|
||||||
.orElse(new ArrayList<>());
|
|
||||||
params.add(String.format("{%s}", paramName));
|
params.add(String.format("{%s}", paramName));
|
||||||
return params;
|
return params;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -63,9 +63,8 @@ public class PageJacksonModule extends Module {
|
|||||||
|
|
||||||
private final Page<T> delegate;
|
private final Page<T> delegate;
|
||||||
|
|
||||||
SimplePageImpl(@JsonProperty("content") List<T> content,
|
SimplePageImpl(@JsonProperty("content") List<T> content, @JsonProperty("number") int number,
|
||||||
@JsonProperty("number") int number, @JsonProperty("size") int size,
|
@JsonProperty("size") int size, @JsonProperty("totalElements") long totalElements,
|
||||||
@JsonProperty("totalElements") long totalElements,
|
|
||||||
@JsonProperty("sort") Sort sort) {
|
@JsonProperty("sort") Sort sort) {
|
||||||
PageRequest pageRequest;
|
PageRequest pageRequest;
|
||||||
if (sort != null) {
|
if (sort != null) {
|
||||||
|
|||||||
+4
-8
@@ -75,8 +75,7 @@ public class PageableSpringEncoder implements Encoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void encode(Object object, Type bodyType, RequestTemplate template)
|
public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException {
|
||||||
throws EncodeException {
|
|
||||||
|
|
||||||
if (supports(object)) {
|
if (supports(object)) {
|
||||||
if (object instanceof Pageable) {
|
if (object instanceof Pageable) {
|
||||||
@@ -101,18 +100,15 @@ public class PageableSpringEncoder implements Encoder {
|
|||||||
delegate.encode(object, bodyType, template);
|
delegate.encode(object, bodyType, template);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new EncodeException(
|
throw new EncodeException("PageableSpringEncoder does not support the given object " + object.getClass()
|
||||||
"PageableSpringEncoder does not support the given object "
|
+ " and no delegate was provided for fallback!");
|
||||||
+ object.getClass()
|
|
||||||
+ " and no delegate was provided for fallback!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void applySort(RequestTemplate template, Sort sort) {
|
private void applySort(RequestTemplate template, Sort sort) {
|
||||||
Collection<String> existingSorts = template.queries().get("sort");
|
Collection<String> existingSorts = template.queries().get("sort");
|
||||||
List<String> sortQueries = existingSorts != null ? new ArrayList<>(existingSorts)
|
List<String> sortQueries = existingSorts != null ? new ArrayList<>(existingSorts) : new ArrayList<>();
|
||||||
: new ArrayList<>();
|
|
||||||
if (!sortParameter.equals("sort")) {
|
if (!sortParameter.equals("sort")) {
|
||||||
existingSorts = template.queries().get(sortParameter);
|
existingSorts = template.queries().get(sortParameter);
|
||||||
if (existingSorts != null) {
|
if (existingSorts != null) {
|
||||||
|
|||||||
+2
-4
@@ -46,8 +46,7 @@ public class ResponseEntityDecoder implements Decoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object decode(final Response response, Type type)
|
public Object decode(final Response response, Type type) throws IOException, FeignException {
|
||||||
throws IOException, FeignException {
|
|
||||||
|
|
||||||
if (isParameterizeHttpEntity(type)) {
|
if (isParameterizeHttpEntity(type)) {
|
||||||
type = ((ParameterizedType) type).getActualTypeArguments()[0];
|
type = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||||
@@ -86,8 +85,7 @@ public class ResponseEntityDecoder implements Decoder {
|
|||||||
headers.put(key, new LinkedList<>(response.headers().get(key)));
|
headers.put(key, new LinkedList<>(response.headers().get(key)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return new ResponseEntity<>((T) instance, headers,
|
return new ResponseEntity<>((T) instance, headers, HttpStatus.valueOf(response.status()));
|
||||||
HttpStatus.valueOf(response.status()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -48,8 +48,7 @@ public class SortJacksonModule extends Module {
|
|||||||
context.addSerializers(serializers);
|
context.addSerializers(serializers);
|
||||||
|
|
||||||
SimpleDeserializers deserializers = new SimpleDeserializers();
|
SimpleDeserializers deserializers = new SimpleDeserializers();
|
||||||
deserializers.addDeserializer(Sort.class,
|
deserializers.addDeserializer(Sort.class, new SortJsonComponent.SortDeserializer());
|
||||||
new SortJsonComponent.SortDeserializer());
|
|
||||||
context.addDeserializers(deserializers);
|
context.addDeserializers(deserializers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-6
@@ -44,8 +44,7 @@ public class SortJsonComponent {
|
|||||||
public static class SortSerializer extends JsonSerializer<Sort> {
|
public static class SortSerializer extends JsonSerializer<Sort> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void serialize(Sort value, JsonGenerator gen,
|
public void serialize(Sort value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
|
||||||
SerializerProvider serializers) throws IOException {
|
|
||||||
gen.writeStartArray();
|
gen.writeStartArray();
|
||||||
value.iterator().forEachRemaining(v -> {
|
value.iterator().forEachRemaining(v -> {
|
||||||
try {
|
try {
|
||||||
@@ -68,15 +67,14 @@ public class SortJsonComponent {
|
|||||||
public static class SortDeserializer extends JsonDeserializer<Sort> {
|
public static class SortDeserializer extends JsonDeserializer<Sort> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Sort deserialize(JsonParser jsonParser,
|
public Sort deserialize(JsonParser jsonParser, DeserializationContext deserializationContext)
|
||||||
DeserializationContext deserializationContext) throws IOException {
|
throws IOException {
|
||||||
TreeNode treeNode = jsonParser.getCodec().readTree(jsonParser);
|
TreeNode treeNode = jsonParser.getCodec().readTree(jsonParser);
|
||||||
if (treeNode.isArray()) {
|
if (treeNode.isArray()) {
|
||||||
ArrayNode arrayNode = (ArrayNode) treeNode;
|
ArrayNode arrayNode = (ArrayNode) treeNode;
|
||||||
List<Sort.Order> orders = new ArrayList<>();
|
List<Sort.Order> orders = new ArrayList<>();
|
||||||
for (JsonNode jsonNode : arrayNode) {
|
for (JsonNode jsonNode : arrayNode) {
|
||||||
Sort.Order order = new Sort.Order(
|
Sort.Order order = new Sort.Order(Sort.Direction.valueOf(jsonNode.get("direction").textValue()),
|
||||||
Sort.Direction.valueOf(jsonNode.get("direction").textValue()),
|
|
||||||
jsonNode.get("property").textValue());
|
jsonNode.get("property").textValue());
|
||||||
orders.add(order);
|
orders.add(order);
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-8
@@ -48,18 +48,15 @@ public class SpringDecoder implements Decoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object decode(final Response response, Type type)
|
public Object decode(final Response response, Type type) throws IOException, FeignException {
|
||||||
throws IOException, FeignException {
|
if (type instanceof Class || type instanceof ParameterizedType || type instanceof WildcardType) {
|
||||||
if (type instanceof Class || type instanceof ParameterizedType
|
|
||||||
|| type instanceof WildcardType) {
|
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
HttpMessageConverterExtractor<?> extractor = new HttpMessageConverterExtractor(
|
HttpMessageConverterExtractor<?> extractor = new HttpMessageConverterExtractor(type,
|
||||||
type, this.messageConverters.getObject().getConverters());
|
this.messageConverters.getObject().getConverters());
|
||||||
|
|
||||||
return extractor.extractData(new FeignResponseAdapter(response));
|
return extractor.extractData(new FeignResponseAdapter(response));
|
||||||
}
|
}
|
||||||
throw new DecodeException(response.status(),
|
throw new DecodeException(response.status(), "type is not an instance of Class or ParameterizedType: " + type,
|
||||||
"type is not an instance of Class or ParameterizedType: " + type,
|
|
||||||
response.request());
|
response.request());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+20
-30
@@ -69,19 +69,16 @@ public class SpringEncoder implements Encoder {
|
|||||||
this.messageConverters = messageConverters;
|
this.messageConverters = messageConverters;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SpringEncoder(SpringFormEncoder springFormEncoder,
|
public SpringEncoder(SpringFormEncoder springFormEncoder, ObjectFactory<HttpMessageConverters> messageConverters) {
|
||||||
ObjectFactory<HttpMessageConverters> messageConverters) {
|
|
||||||
this.springFormEncoder = springFormEncoder;
|
this.springFormEncoder = springFormEncoder;
|
||||||
this.messageConverters = messageConverters;
|
this.messageConverters = messageConverters;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void encode(Object requestBody, Type bodyType, RequestTemplate request)
|
public void encode(Object requestBody, Type bodyType, RequestTemplate request) throws EncodeException {
|
||||||
throws EncodeException {
|
|
||||||
// template.body(conversionService.convert(object, String.class));
|
// template.body(conversionService.convert(object, String.class));
|
||||||
if (requestBody != null) {
|
if (requestBody != null) {
|
||||||
Collection<String> contentTypes = request.headers()
|
Collection<String> contentTypes = request.headers().get(HttpEncoding.CONTENT_TYPE);
|
||||||
.get(HttpEncoding.CONTENT_TYPE);
|
|
||||||
|
|
||||||
MediaType requestContentType = null;
|
MediaType requestContentType = null;
|
||||||
if (contentTypes != null && !contentTypes.isEmpty()) {
|
if (contentTypes != null && !contentTypes.isEmpty()) {
|
||||||
@@ -95,24 +92,20 @@ public class SpringEncoder implements Encoder {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (bodyType == MultipartFile.class) {
|
if (bodyType == MultipartFile.class) {
|
||||||
log.warn(
|
log.warn("For MultipartFile to be handled correctly, the 'consumes' parameter of @RequestMapping "
|
||||||
"For MultipartFile to be handled correctly, the 'consumes' parameter of @RequestMapping "
|
+ "should be specified as MediaType.MULTIPART_FORM_DATA_VALUE");
|
||||||
+ "should be specified as MediaType.MULTIPART_FORM_DATA_VALUE");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (HttpMessageConverter messageConverter : this.messageConverters
|
for (HttpMessageConverter messageConverter : this.messageConverters.getObject().getConverters()) {
|
||||||
.getObject().getConverters()) {
|
|
||||||
FeignOutputMessage outputMessage;
|
FeignOutputMessage outputMessage;
|
||||||
try {
|
try {
|
||||||
if (messageConverter instanceof GenericHttpMessageConverter) {
|
if (messageConverter instanceof GenericHttpMessageConverter) {
|
||||||
outputMessage = checkAndWrite(requestBody, bodyType,
|
outputMessage = checkAndWrite(requestBody, bodyType, requestContentType,
|
||||||
requestContentType,
|
|
||||||
(GenericHttpMessageConverter) messageConverter, request);
|
(GenericHttpMessageConverter) messageConverter, request);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
outputMessage = checkAndWrite(requestBody, requestContentType,
|
outputMessage = checkAndWrite(requestBody, requestContentType, messageConverter, request);
|
||||||
messageConverter, request);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IOException | HttpMessageConversionException ex) {
|
catch (IOException | HttpMessageConversionException ex) {
|
||||||
@@ -131,20 +124,19 @@ public class SpringEncoder implements Encoder {
|
|||||||
charset = null;
|
charset = null;
|
||||||
}
|
}
|
||||||
else if (messageConverter instanceof ProtobufHttpMessageConverter
|
else if (messageConverter instanceof ProtobufHttpMessageConverter
|
||||||
&& ProtobufHttpMessageConverter.PROTOBUF.isCompatibleWith(
|
&& ProtobufHttpMessageConverter.PROTOBUF
|
||||||
outputMessage.getHeaders().getContentType())) {
|
.isCompatibleWith(outputMessage.getHeaders().getContentType())) {
|
||||||
charset = null;
|
charset = null;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
charset = StandardCharsets.UTF_8;
|
charset = StandardCharsets.UTF_8;
|
||||||
}
|
}
|
||||||
request.body(Request.Body.encoded(
|
request.body(Request.Body.encoded(outputMessage.getOutputStream().toByteArray(), charset));
|
||||||
outputMessage.getOutputStream().toByteArray(), charset));
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
String message = "Could not write request: no suitable HttpMessageConverter "
|
String message = "Could not write request: no suitable HttpMessageConverter " + "found for request type ["
|
||||||
+ "found for request type [" + requestBody.getClass().getName() + "]";
|
+ requestBody.getClass().getName() + "]";
|
||||||
if (requestContentType != null) {
|
if (requestContentType != null) {
|
||||||
message += " and content type [" + requestContentType + "]";
|
message += " and content type [" + requestContentType + "]";
|
||||||
}
|
}
|
||||||
@@ -153,8 +145,8 @@ public class SpringEncoder implements Encoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private FeignOutputMessage checkAndWrite(Object body, MediaType contentType,
|
private FeignOutputMessage checkAndWrite(Object body, MediaType contentType, HttpMessageConverter converter,
|
||||||
HttpMessageConverter converter, RequestTemplate request) throws IOException {
|
RequestTemplate request) throws IOException {
|
||||||
if (converter.canWrite(body.getClass(), contentType)) {
|
if (converter.canWrite(body.getClass(), contentType)) {
|
||||||
logBeforeWrite(body, contentType, converter);
|
logBeforeWrite(body, contentType, converter);
|
||||||
FeignOutputMessage outputMessage = new FeignOutputMessage(request);
|
FeignOutputMessage outputMessage = new FeignOutputMessage(request);
|
||||||
@@ -167,9 +159,8 @@ public class SpringEncoder implements Encoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private FeignOutputMessage checkAndWrite(Object body, Type genericType,
|
private FeignOutputMessage checkAndWrite(Object body, Type genericType, MediaType contentType,
|
||||||
MediaType contentType, GenericHttpMessageConverter converter,
|
GenericHttpMessageConverter converter, RequestTemplate request) throws IOException {
|
||||||
RequestTemplate request) throws IOException {
|
|
||||||
if (converter.canWrite(genericType, body.getClass(), contentType)) {
|
if (converter.canWrite(genericType, body.getClass(), contentType)) {
|
||||||
logBeforeWrite(body, contentType, converter);
|
logBeforeWrite(body, contentType, converter);
|
||||||
FeignOutputMessage outputMessage = new FeignOutputMessage(request);
|
FeignOutputMessage outputMessage = new FeignOutputMessage(request);
|
||||||
@@ -185,12 +176,11 @@ public class SpringEncoder implements Encoder {
|
|||||||
HttpMessageConverter messageConverter) {
|
HttpMessageConverter messageConverter) {
|
||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
if (requestContentType != null) {
|
if (requestContentType != null) {
|
||||||
log.debug("Writing [" + requestBody + "] as \"" + requestContentType
|
log.debug("Writing [" + requestBody + "] as \"" + requestContentType + "\" using [" + messageConverter
|
||||||
+ "\" using [" + messageConverter + "]");
|
+ "]");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log.debug(
|
log.debug("Writing [" + requestBody + "] using [" + messageConverter + "]");
|
||||||
"Writing [" + requestBody + "] using [" + messageConverter + "]");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+49
-108
@@ -80,18 +80,15 @@ import static org.springframework.core.annotation.AnnotatedElementUtils.findMerg
|
|||||||
* @author Darren Foong
|
* @author Darren Foong
|
||||||
* @author Ram Anaswara
|
* @author Ram Anaswara
|
||||||
*/
|
*/
|
||||||
public class SpringMvcContract extends Contract.BaseContract
|
public class SpringMvcContract extends Contract.BaseContract implements ResourceLoaderAware {
|
||||||
implements ResourceLoaderAware {
|
|
||||||
|
|
||||||
private static final String ACCEPT = "Accept";
|
private static final String ACCEPT = "Accept";
|
||||||
|
|
||||||
private static final String CONTENT_TYPE = "Content-Type";
|
private static final String CONTENT_TYPE = "Content-Type";
|
||||||
|
|
||||||
private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor
|
private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class);
|
||||||
.valueOf(String.class);
|
|
||||||
|
|
||||||
private static final TypeDescriptor ITERABLE_TYPE_DESCRIPTOR = TypeDescriptor
|
private static final TypeDescriptor ITERABLE_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Iterable.class);
|
||||||
.valueOf(Iterable.class);
|
|
||||||
|
|
||||||
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
|
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
|
||||||
|
|
||||||
@@ -111,22 +108,18 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
this(Collections.emptyList());
|
this(Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public SpringMvcContract(
|
public SpringMvcContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors) {
|
||||||
List<AnnotatedParameterProcessor> annotatedParameterProcessors) {
|
|
||||||
this(annotatedParameterProcessors, new DefaultConversionService());
|
this(annotatedParameterProcessors, new DefaultConversionService());
|
||||||
}
|
}
|
||||||
|
|
||||||
public SpringMvcContract(
|
public SpringMvcContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors,
|
||||||
List<AnnotatedParameterProcessor> annotatedParameterProcessors,
|
|
||||||
ConversionService conversionService) {
|
ConversionService conversionService) {
|
||||||
this(annotatedParameterProcessors, conversionService, true);
|
this(annotatedParameterProcessors, conversionService, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SpringMvcContract(
|
public SpringMvcContract(List<AnnotatedParameterProcessor> annotatedParameterProcessors,
|
||||||
List<AnnotatedParameterProcessor> annotatedParameterProcessors,
|
|
||||||
ConversionService conversionService, boolean decodeSlash) {
|
ConversionService conversionService, boolean decodeSlash) {
|
||||||
Assert.notNull(annotatedParameterProcessors,
|
Assert.notNull(annotatedParameterProcessors, "Parameter processors can not be null.");
|
||||||
"Parameter processors can not be null.");
|
|
||||||
Assert.notNull(conversionService, "ConversionService can not be null.");
|
Assert.notNull(conversionService, "ConversionService can not be null.");
|
||||||
|
|
||||||
List<AnnotatedParameterProcessor> processors = getDefaultAnnotatedArgumentsProcessors();
|
List<AnnotatedParameterProcessor> processors = getDefaultAnnotatedArgumentsProcessors();
|
||||||
@@ -146,26 +139,21 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
// Feign applies the Param.Expander to each element of an Iterable, so in those
|
// Feign applies the Param.Expander to each element of an Iterable, so in those
|
||||||
// cases we need to provide a TypeDescriptor of the element.
|
// cases we need to provide a TypeDescriptor of the element.
|
||||||
if (typeDescriptor.isAssignableTo(ITERABLE_TYPE_DESCRIPTOR)) {
|
if (typeDescriptor.isAssignableTo(ITERABLE_TYPE_DESCRIPTOR)) {
|
||||||
TypeDescriptor elementTypeDescriptor = getElementTypeDescriptor(
|
TypeDescriptor elementTypeDescriptor = getElementTypeDescriptor(typeDescriptor);
|
||||||
typeDescriptor);
|
|
||||||
|
|
||||||
checkState(elementTypeDescriptor != null,
|
checkState(elementTypeDescriptor != null,
|
||||||
"Could not resolve element type of Iterable type %s. Not declared?",
|
"Could not resolve element type of Iterable type %s. Not declared?", typeDescriptor);
|
||||||
typeDescriptor);
|
|
||||||
|
|
||||||
typeDescriptor = elementTypeDescriptor;
|
typeDescriptor = elementTypeDescriptor;
|
||||||
}
|
}
|
||||||
return typeDescriptor;
|
return typeDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static TypeDescriptor getElementTypeDescriptor(
|
private static TypeDescriptor getElementTypeDescriptor(TypeDescriptor typeDescriptor) {
|
||||||
TypeDescriptor typeDescriptor) {
|
|
||||||
TypeDescriptor elementTypeDescriptor = typeDescriptor.getElementTypeDescriptor();
|
TypeDescriptor elementTypeDescriptor = typeDescriptor.getElementTypeDescriptor();
|
||||||
// that means it's not a collection but it is iterable, gh-135
|
// that means it's not a collection but it is iterable, gh-135
|
||||||
if (elementTypeDescriptor == null
|
if (elementTypeDescriptor == null && Iterable.class.isAssignableFrom(typeDescriptor.getType())) {
|
||||||
&& Iterable.class.isAssignableFrom(typeDescriptor.getType())) {
|
ResolvableType type = typeDescriptor.getResolvableType().as(Iterable.class).getGeneric(0);
|
||||||
ResolvableType type = typeDescriptor.getResolvableType().as(Iterable.class)
|
|
||||||
.getGeneric(0);
|
|
||||||
if (type.resolve() == null) {
|
if (type.resolve() == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -182,8 +170,7 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
@Override
|
@Override
|
||||||
protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
|
protected void processAnnotationOnClass(MethodMetadata data, Class<?> clz) {
|
||||||
if (clz.getInterfaces().length == 0) {
|
if (clz.getInterfaces().length == 0) {
|
||||||
RequestMapping classAnnotation = findMergedAnnotation(clz,
|
RequestMapping classAnnotation = findMergedAnnotation(clz, RequestMapping.class);
|
||||||
RequestMapping.class);
|
|
||||||
if (classAnnotation != null) {
|
if (classAnnotation != null) {
|
||||||
// Prepend path from class annotation if specified
|
// Prepend path from class annotation if specified
|
||||||
if (classAnnotation.value().length > 0) {
|
if (classAnnotation.value().length > 0) {
|
||||||
@@ -206,8 +193,7 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
processedMethods.put(Feign.configKey(targetType, method), method);
|
processedMethods.put(Feign.configKey(targetType, method), method);
|
||||||
MethodMetadata md = super.parseAndValidateMetadata(targetType, method);
|
MethodMetadata md = super.parseAndValidateMetadata(targetType, method);
|
||||||
|
|
||||||
RequestMapping classAnnotation = findMergedAnnotation(targetType,
|
RequestMapping classAnnotation = findMergedAnnotation(targetType, RequestMapping.class);
|
||||||
RequestMapping.class);
|
|
||||||
if (classAnnotation != null) {
|
if (classAnnotation != null) {
|
||||||
// produces - use from class annotation only if method has not specified this
|
// produces - use from class annotation only if method has not specified this
|
||||||
if (!md.template().headers().containsKey(ACCEPT)) {
|
if (!md.template().headers().containsKey(ACCEPT)) {
|
||||||
@@ -227,16 +213,14 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void processAnnotationOnMethod(MethodMetadata data,
|
protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation, Method method) {
|
||||||
Annotation methodAnnotation, Method method) {
|
|
||||||
if (CollectionFormat.class.isInstance(methodAnnotation)) {
|
if (CollectionFormat.class.isInstance(methodAnnotation)) {
|
||||||
CollectionFormat collectionFormat = findMergedAnnotation(method,
|
CollectionFormat collectionFormat = findMergedAnnotation(method, CollectionFormat.class);
|
||||||
CollectionFormat.class);
|
|
||||||
data.template().collectionFormat(collectionFormat.value());
|
data.template().collectionFormat(collectionFormat.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!RequestMapping.class.isInstance(methodAnnotation) && !methodAnnotation
|
if (!RequestMapping.class.isInstance(methodAnnotation)
|
||||||
.annotationType().isAnnotationPresent(RequestMapping.class)) {
|
&& !methodAnnotation.annotationType().isAnnotationPresent(RequestMapping.class)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,34 +263,29 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
}
|
}
|
||||||
|
|
||||||
private String resolve(String value) {
|
private String resolve(String value) {
|
||||||
if (StringUtils.hasText(value)
|
if (StringUtils.hasText(value) && resourceLoader instanceof ConfigurableApplicationContext) {
|
||||||
&& resourceLoader instanceof ConfigurableApplicationContext) {
|
return ((ConfigurableApplicationContext) resourceLoader).getEnvironment().resolvePlaceholders(value);
|
||||||
return ((ConfigurableApplicationContext) resourceLoader).getEnvironment()
|
|
||||||
.resolvePlaceholders(value);
|
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkAtMostOne(Method method, Object[] values, String fieldName) {
|
private void checkAtMostOne(Method method, Object[] values, String fieldName) {
|
||||||
checkState(values != null && (values.length == 0 || values.length == 1),
|
checkState(values != null && (values.length == 0 || values.length == 1),
|
||||||
"Method %s can only contain at most 1 %s field. Found: %s",
|
"Method %s can only contain at most 1 %s field. Found: %s", method.getName(), fieldName,
|
||||||
method.getName(), fieldName,
|
|
||||||
values == null ? null : Arrays.asList(values));
|
values == null ? null : Arrays.asList(values));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkOne(Method method, Object[] values, String fieldName) {
|
private void checkOne(Method method, Object[] values, String fieldName) {
|
||||||
checkState(values != null && values.length == 1,
|
checkState(values != null && values.length == 1, "Method %s can only contain 1 %s field. Found: %s",
|
||||||
"Method %s can only contain 1 %s field. Found: %s", method.getName(),
|
method.getName(), fieldName, values == null ? null : Arrays.asList(values));
|
||||||
fieldName, values == null ? null : Arrays.asList(values));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean processAnnotationsOnParameter(MethodMetadata data,
|
protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {
|
||||||
Annotation[] annotations, int paramIndex) {
|
|
||||||
boolean isHttpAnnotation = false;
|
boolean isHttpAnnotation = false;
|
||||||
|
|
||||||
AnnotatedParameterProcessor.AnnotatedParameterContext context = new SimpleAnnotatedParameterContext(
|
AnnotatedParameterProcessor.AnnotatedParameterContext context = new SimpleAnnotatedParameterContext(data,
|
||||||
data, paramIndex);
|
paramIndex);
|
||||||
Method method = processedMethods.get(data.configKey());
|
Method method = processedMethods.get(data.configKey());
|
||||||
for (Annotation parameterAnnotation : annotations) {
|
for (Annotation parameterAnnotation : annotations) {
|
||||||
AnnotatedParameterProcessor processor = annotatedArgumentProcessors
|
AnnotatedParameterProcessor processor = annotatedArgumentProcessors
|
||||||
@@ -315,19 +294,16 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
Annotation processParameterAnnotation;
|
Annotation processParameterAnnotation;
|
||||||
// synthesize, handling @AliasFor, while falling back to parameter name on
|
// synthesize, handling @AliasFor, while falling back to parameter name on
|
||||||
// missing String #value():
|
// missing String #value():
|
||||||
processParameterAnnotation = synthesizeWithMethodParameterNameAsFallbackValue(
|
processParameterAnnotation = synthesizeWithMethodParameterNameAsFallbackValue(parameterAnnotation,
|
||||||
parameterAnnotation, method, paramIndex);
|
method, paramIndex);
|
||||||
isHttpAnnotation |= processor.processArgument(context,
|
isHttpAnnotation |= processor.processArgument(context, processParameterAnnotation, method);
|
||||||
processParameterAnnotation, method);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isMultipartFormData(data) && isHttpAnnotation
|
if (!isMultipartFormData(data) && isHttpAnnotation && data.indexToExpander().get(paramIndex) == null) {
|
||||||
&& data.indexToExpander().get(paramIndex) == null) {
|
|
||||||
TypeDescriptor typeDescriptor = createTypeDescriptor(method, paramIndex);
|
TypeDescriptor typeDescriptor = createTypeDescriptor(method, paramIndex);
|
||||||
if (conversionService.canConvert(typeDescriptor, STRING_TYPE_DESCRIPTOR)) {
|
if (conversionService.canConvert(typeDescriptor, STRING_TYPE_DESCRIPTOR)) {
|
||||||
Param.Expander expander = convertingExpanderFactory
|
Param.Expander expander = convertingExpanderFactory.getExpander(typeDescriptor);
|
||||||
.getExpander(typeDescriptor);
|
|
||||||
if (expander != null) {
|
if (expander != null) {
|
||||||
data.indexToExpander().put(paramIndex, expander);
|
data.indexToExpander().put(paramIndex, expander);
|
||||||
}
|
}
|
||||||
@@ -336,28 +312,23 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
return isHttpAnnotation;
|
return isHttpAnnotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseProduces(MethodMetadata md, Method method,
|
private void parseProduces(MethodMetadata md, Method method, RequestMapping annotation) {
|
||||||
RequestMapping annotation) {
|
|
||||||
String[] serverProduces = annotation.produces();
|
String[] serverProduces = annotation.produces();
|
||||||
String clientAccepts = serverProduces.length == 0 ? null
|
String clientAccepts = serverProduces.length == 0 ? null : emptyToNull(serverProduces[0]);
|
||||||
: emptyToNull(serverProduces[0]);
|
|
||||||
if (clientAccepts != null) {
|
if (clientAccepts != null) {
|
||||||
md.template().header(ACCEPT, clientAccepts);
|
md.template().header(ACCEPT, clientAccepts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseConsumes(MethodMetadata md, Method method,
|
private void parseConsumes(MethodMetadata md, Method method, RequestMapping annotation) {
|
||||||
RequestMapping annotation) {
|
|
||||||
String[] serverConsumes = annotation.consumes();
|
String[] serverConsumes = annotation.consumes();
|
||||||
String clientProduces = serverConsumes.length == 0 ? null
|
String clientProduces = serverConsumes.length == 0 ? null : emptyToNull(serverConsumes[0]);
|
||||||
: emptyToNull(serverConsumes[0]);
|
|
||||||
if (clientProduces != null) {
|
if (clientProduces != null) {
|
||||||
md.template().header(CONTENT_TYPE, clientProduces);
|
md.template().header(CONTENT_TYPE, clientProduces);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void parseHeaders(MethodMetadata md, Method method,
|
private void parseHeaders(MethodMetadata md, Method method, RequestMapping annotation) {
|
||||||
RequestMapping annotation) {
|
|
||||||
// TODO: only supports one header value per key
|
// TODO: only supports one header value per key
|
||||||
if (annotation.headers() != null && annotation.headers().length > 0) {
|
if (annotation.headers() != null && annotation.headers().length > 0) {
|
||||||
for (String header : annotation.headers()) {
|
for (String header : annotation.headers()) {
|
||||||
@@ -393,26 +364,21 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
return annotatedArgumentResolvers;
|
return annotatedArgumentResolvers;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Annotation synthesizeWithMethodParameterNameAsFallbackValue(
|
private Annotation synthesizeWithMethodParameterNameAsFallbackValue(Annotation parameterAnnotation, Method method,
|
||||||
Annotation parameterAnnotation, Method method, int parameterIndex) {
|
int parameterIndex) {
|
||||||
Map<String, Object> annotationAttributes = AnnotationUtils
|
Map<String, Object> annotationAttributes = AnnotationUtils.getAnnotationAttributes(parameterAnnotation);
|
||||||
.getAnnotationAttributes(parameterAnnotation);
|
|
||||||
Object defaultValue = AnnotationUtils.getDefaultValue(parameterAnnotation);
|
Object defaultValue = AnnotationUtils.getDefaultValue(parameterAnnotation);
|
||||||
if (defaultValue instanceof String
|
if (defaultValue instanceof String && defaultValue.equals(annotationAttributes.get(AnnotationUtils.VALUE))) {
|
||||||
&& defaultValue.equals(annotationAttributes.get(AnnotationUtils.VALUE))) {
|
|
||||||
Type[] parameterTypes = method.getGenericParameterTypes();
|
Type[] parameterTypes = method.getGenericParameterTypes();
|
||||||
String[] parameterNames = PARAMETER_NAME_DISCOVERER.getParameterNames(method);
|
String[] parameterNames = PARAMETER_NAME_DISCOVERER.getParameterNames(method);
|
||||||
if (shouldAddParameterName(parameterIndex, parameterTypes, parameterNames)) {
|
if (shouldAddParameterName(parameterIndex, parameterTypes, parameterNames)) {
|
||||||
annotationAttributes.put(AnnotationUtils.VALUE,
|
annotationAttributes.put(AnnotationUtils.VALUE, parameterNames[parameterIndex]);
|
||||||
parameterNames[parameterIndex]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return AnnotationUtils.synthesizeAnnotation(annotationAttributes,
|
return AnnotationUtils.synthesizeAnnotation(annotationAttributes, parameterAnnotation.annotationType(), null);
|
||||||
parameterAnnotation.annotationType(), null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldAddParameterName(int parameterIndex, Type[] parameterTypes,
|
private boolean shouldAddParameterName(int parameterIndex, Type[] parameterTypes, String[] parameterNames) {
|
||||||
String[] parameterNames) {
|
|
||||||
// has a parameter name
|
// has a parameter name
|
||||||
return parameterNames != null && parameterNames.length > parameterIndex
|
return parameterNames != null && parameterNames.length > parameterIndex
|
||||||
// has a type
|
// has a type
|
||||||
@@ -420,14 +386,12 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean isMultipartFormData(MethodMetadata data) {
|
private boolean isMultipartFormData(MethodMetadata data) {
|
||||||
Collection<String> contentTypes = data.template().headers()
|
Collection<String> contentTypes = data.template().headers().get(HttpEncoding.CONTENT_TYPE);
|
||||||
.get(HttpEncoding.CONTENT_TYPE);
|
|
||||||
|
|
||||||
if (contentTypes != null && !contentTypes.isEmpty()) {
|
if (contentTypes != null && !contentTypes.isEmpty()) {
|
||||||
String type = contentTypes.iterator().next();
|
String type = contentTypes.iterator().next();
|
||||||
try {
|
try {
|
||||||
return Objects.equals(MediaType.valueOf(type),
|
return Objects.equals(MediaType.valueOf(type), MediaType.MULTIPART_FORM_DATA);
|
||||||
MediaType.MULTIPART_FORM_DATA);
|
|
||||||
}
|
}
|
||||||
catch (InvalidMediaTypeException ignored) {
|
catch (InvalidMediaTypeException ignored) {
|
||||||
return false;
|
return false;
|
||||||
@@ -437,25 +401,6 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated Not used internally anymore. Will be removed in the future.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public static class ConvertingExpander implements Param.Expander {
|
|
||||||
|
|
||||||
private final ConversionService conversionService;
|
|
||||||
|
|
||||||
public ConvertingExpander(ConversionService conversionService) {
|
|
||||||
this.conversionService = conversionService;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String expand(Object value) {
|
|
||||||
return conversionService.convert(value, String.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class ConvertingExpanderFactory {
|
private static class ConvertingExpanderFactory {
|
||||||
|
|
||||||
private final ConversionService conversionService;
|
private final ConversionService conversionService;
|
||||||
@@ -466,23 +411,20 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
|
|
||||||
Param.Expander getExpander(TypeDescriptor typeDescriptor) {
|
Param.Expander getExpander(TypeDescriptor typeDescriptor) {
|
||||||
return value -> {
|
return value -> {
|
||||||
Object converted = conversionService.convert(value, typeDescriptor,
|
Object converted = conversionService.convert(value, typeDescriptor, STRING_TYPE_DESCRIPTOR);
|
||||||
STRING_TYPE_DESCRIPTOR);
|
|
||||||
return (String) converted;
|
return (String) converted;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SimpleAnnotatedParameterContext
|
private class SimpleAnnotatedParameterContext implements AnnotatedParameterProcessor.AnnotatedParameterContext {
|
||||||
implements AnnotatedParameterProcessor.AnnotatedParameterContext {
|
|
||||||
|
|
||||||
private final MethodMetadata methodMetadata;
|
private final MethodMetadata methodMetadata;
|
||||||
|
|
||||||
private final int parameterIndex;
|
private final int parameterIndex;
|
||||||
|
|
||||||
SimpleAnnotatedParameterContext(MethodMetadata methodMetadata,
|
SimpleAnnotatedParameterContext(MethodMetadata methodMetadata, int parameterIndex) {
|
||||||
int parameterIndex) {
|
|
||||||
this.methodMetadata = methodMetadata;
|
this.methodMetadata = methodMetadata;
|
||||||
this.parameterIndex = parameterIndex;
|
this.parameterIndex = parameterIndex;
|
||||||
}
|
}
|
||||||
@@ -503,8 +445,7 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<String> setTemplateParameter(String name,
|
public Collection<String> setTemplateParameter(String name, Collection<String> rest) {
|
||||||
Collection<String> rest) {
|
|
||||||
return addTemplateParameter(rest, name);
|
return addTemplateParameter(rest, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration,\
|
|
||||||
org.springframework.cloud.openfeign.hateoas.FeignHalAutoConfiguration,\
|
org.springframework.cloud.openfeign.hateoas.FeignHalAutoConfiguration,\
|
||||||
org.springframework.cloud.openfeign.FeignAutoConfiguration,\
|
org.springframework.cloud.openfeign.FeignAutoConfiguration,\
|
||||||
org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\
|
org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration,\
|
||||||
|
|||||||
+7
-14
@@ -43,10 +43,8 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
@RunWith(SpringJUnit4ClassRunner.class)
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
@SpringBootTest(classes = DefaultGzipDecoderTests.Application.class,
|
@SpringBootTest(classes = DefaultGzipDecoderTests.Application.class,
|
||||||
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||||
value = { "spring.application.name=defaultGzipDecoderTests",
|
value = { "spring.application.name=defaultGzipDecoderTests", "feign.compression.response.enabled=true",
|
||||||
"feign.compression.response.enabled=true",
|
"feign.compression.response.useGzipDecoder=true", "feign.client.config.default.loggerLevel=full",
|
||||||
"feign.compression.response.useGzipDecoder=true",
|
|
||||||
"feign.client.config.default.loggerLevel=full",
|
|
||||||
"logging.level.org.springframework.cloud.openfeign=DEBUG" })
|
"logging.level.org.springframework.cloud.openfeign=DEBUG" })
|
||||||
@DirtiesContext
|
@DirtiesContext
|
||||||
public class DefaultGzipDecoderTests extends FeignClientFactoryBean {
|
public class DefaultGzipDecoderTests extends FeignClientFactoryBean {
|
||||||
@@ -71,20 +69,17 @@ public class DefaultGzipDecoderTests extends FeignClientFactoryBean {
|
|||||||
public void testBodyDecompress() {
|
public void testBodyDecompress() {
|
||||||
ResponseEntity<Hello> response = testClient().getGzipResponse();
|
ResponseEntity<Hello> response = testClient().getGzipResponse();
|
||||||
assertThat(response).as("response was null").isNotNull();
|
assertThat(response).as("response was null").isNotNull();
|
||||||
assertThat(response.getStatusCode()).as("wrong status code")
|
assertThat(response.getStatusCode()).as("wrong status code").isEqualTo(HttpStatus.OK);
|
||||||
.isEqualTo(HttpStatus.OK);
|
|
||||||
Hello hello = response.getBody();
|
Hello hello = response.getBody();
|
||||||
assertThat(hello).as("hello was null").isNotNull();
|
assertThat(hello).as("hello was null").isNotNull();
|
||||||
assertThat(hello).as("first hello didn't match")
|
assertThat(hello).as("first hello didn't match").isEqualTo(new Hello("hello world via response"));
|
||||||
.isEqualTo(new Hello("hello world via response"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNullBodyDecompress() {
|
public void testNullBodyDecompress() {
|
||||||
ResponseEntity<Hello> response = testClient().getNullResponse();
|
ResponseEntity<Hello> response = testClient().getNullResponse();
|
||||||
assertThat(response).as("response was null").isNotNull();
|
assertThat(response).as("response was null").isNotNull();
|
||||||
assertThat(response.getStatusCode()).as("wrong status code")
|
assertThat(response.getStatusCode()).as("wrong status code").isEqualTo(HttpStatus.OK);
|
||||||
.isEqualTo(HttpStatus.OK);
|
|
||||||
Hello hello = response.getBody();
|
Hello hello = response.getBody();
|
||||||
assertThat(hello).as("hello was not null").isNull();
|
assertThat(hello).as("hello was not null").isNull();
|
||||||
assertThat(hello).as("null hello didn't match").isEqualTo(null);
|
assertThat(hello).as("null hello didn't match").isEqualTo(null);
|
||||||
@@ -94,12 +89,10 @@ public class DefaultGzipDecoderTests extends FeignClientFactoryBean {
|
|||||||
public void testCharsetDecompress() {
|
public void testCharsetDecompress() {
|
||||||
ResponseEntity<Hello> response = testClient().getUtf8Response();
|
ResponseEntity<Hello> response = testClient().getUtf8Response();
|
||||||
assertThat(response).as("response was null").isNotNull();
|
assertThat(response).as("response was null").isNotNull();
|
||||||
assertThat(response.getStatusCode()).as("wrong status code")
|
assertThat(response.getStatusCode()).as("wrong status code").isEqualTo(HttpStatus.OK);
|
||||||
.isEqualTo(HttpStatus.OK);
|
|
||||||
Hello hello = response.getBody();
|
Hello hello = response.getBody();
|
||||||
assertThat(hello).as("hello was null").isNotNull();
|
assertThat(hello).as("hello was null").isNotNull();
|
||||||
assertThat(hello).as("utf8 hello didn't match")
|
assertThat(hello).as("utf8 hello didn't match").isEqualTo(new Hello("안녕하세요 means Hello in Korean"));
|
||||||
.isEqualTo(new Hello("안녕하세요 means Hello in Korean"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class Hello {
|
private static class Hello {
|
||||||
|
|||||||
+3
-7
@@ -21,12 +21,10 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
|
||||||
import org.springframework.cloud.openfeign.support.PageableSpringEncoder;
|
import org.springframework.cloud.openfeign.support.PageableSpringEncoder;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
|
||||||
import org.springframework.test.annotation.DirtiesContext;
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
|
||||||
@@ -44,13 +42,11 @@ public class EnableFeignClientsSpringDataTests {
|
|||||||
@Test
|
@Test
|
||||||
public void encoderDefaultCorrect() {
|
public void encoderDefaultCorrect() {
|
||||||
|
|
||||||
PageableSpringEncoder.class
|
PageableSpringEncoder.class.cast(this.feignContext.getInstance("foo", Encoder.class));
|
||||||
.cast(this.feignContext.getInstance("foo", Encoder.class));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@Import({ PropertyPlaceholderAutoConfiguration.class, ArchaiusAutoConfiguration.class,
|
@EnableAutoConfiguration
|
||||||
FeignAutoConfiguration.class })
|
|
||||||
protected static class PlainConfiguration {
|
protected static class PlainConfiguration {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -30,7 +30,6 @@ import org.junit.runner.RunWith;
|
|||||||
|
|
||||||
import org.springframework.boot.WebApplicationType;
|
import org.springframework.boot.WebApplicationType;
|
||||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
|
||||||
import org.springframework.cloud.openfeign.support.SpringEncoder;
|
import org.springframework.cloud.openfeign.support.SpringEncoder;
|
||||||
import org.springframework.cloud.openfeign.support.SpringMvcContract;
|
import org.springframework.cloud.openfeign.support.SpringMvcContract;
|
||||||
import org.springframework.cloud.test.ClassPathExclusions;
|
import org.springframework.cloud.test.ClassPathExclusions;
|
||||||
@@ -88,7 +87,7 @@ public class EnableFeignClientsTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@Import({ ArchaiusAutoConfiguration.class, FeignAutoConfiguration.class })
|
@Import({ FeignAutoConfiguration.class })
|
||||||
protected static class PlainConfiguration {
|
protected static class PlainConfiguration {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-9
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2019 the original author or authors.
|
* Copyright 2013-2020 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -41,8 +41,7 @@ public class FeignBuilderCustomizerTests {
|
|||||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||||
FeignBuilderCustomizerTests.SampleConfiguration2.class);
|
FeignBuilderCustomizerTests.SampleConfiguration2.class);
|
||||||
|
|
||||||
FeignClientFactoryBean clientFactoryBean = context
|
FeignClientFactoryBean clientFactoryBean = context.getBean(FeignClientFactoryBean.class);
|
||||||
.getBean(FeignClientFactoryBean.class);
|
|
||||||
FeignContext feignContext = context.getBean(FeignContext.class);
|
FeignContext feignContext = context.getBean(FeignContext.class);
|
||||||
|
|
||||||
Feign.Builder builder = clientFactoryBean.feign(feignContext);
|
Feign.Builder builder = clientFactoryBean.feign(feignContext);
|
||||||
@@ -52,14 +51,12 @@ public class FeignBuilderCustomizerTests {
|
|||||||
context.close();
|
context.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertFeignBuilderField(Feign.Builder builder, String fieldName,
|
private void assertFeignBuilderField(Feign.Builder builder, String fieldName, Object expectedValue) {
|
||||||
Object expectedValue) {
|
|
||||||
Field builderField = ReflectionUtils.findField(Feign.Builder.class, fieldName);
|
Field builderField = ReflectionUtils.findField(Feign.Builder.class, fieldName);
|
||||||
ReflectionUtils.makeAccessible(builderField);
|
ReflectionUtils.makeAccessible(builderField);
|
||||||
|
|
||||||
Object value = ReflectionUtils.getField(builderField, builder);
|
Object value = ReflectionUtils.getField(builderField, builder);
|
||||||
assertThat(value).as("Expected value for the field '" + fieldName + "':")
|
assertThat(value).as("Expected value for the field '" + fieldName + "':").isEqualTo(expectedValue);
|
||||||
.isEqualTo(expectedValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -67,8 +64,7 @@ public class FeignBuilderCustomizerTests {
|
|||||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||||
FeignBuilderCustomizerTests.SampleConfiguration3.class);
|
FeignBuilderCustomizerTests.SampleConfiguration3.class);
|
||||||
|
|
||||||
FeignClientFactoryBean clientFactoryBean = context
|
FeignClientFactoryBean clientFactoryBean = context.getBean(FeignClientFactoryBean.class);
|
||||||
.getBean(FeignClientFactoryBean.class);
|
|
||||||
FeignContext feignContext = context.getBean(FeignContext.class);
|
FeignContext feignContext = context.getBean(FeignContext.class);
|
||||||
|
|
||||||
Feign.Builder builder = clientFactoryBean.feign(feignContext);
|
Feign.Builder builder = clientFactoryBean.feign(feignContext);
|
||||||
|
|||||||
+48
-59
@@ -24,7 +24,6 @@ import java.util.Arrays;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import feign.hystrix.FallbackFactory;
|
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
@@ -50,26 +49,23 @@ public class FeignClientBuilderTests {
|
|||||||
|
|
||||||
private ApplicationContext applicationContext;
|
private ApplicationContext applicationContext;
|
||||||
|
|
||||||
private static Object getDefaultValueFromFeignClientAnnotation(
|
private static Object getDefaultValueFromFeignClientAnnotation(final String methodName) {
|
||||||
final String methodName) {
|
|
||||||
final Method method = ReflectionUtils.findMethod(FeignClient.class, methodName);
|
final Method method = ReflectionUtils.findMethod(FeignClient.class, methodName);
|
||||||
return method.getDefaultValue();
|
return method.getDefaultValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assertFactoryBeanField(final FeignClientBuilder.Builder builder,
|
private static void assertFactoryBeanField(final FeignClientBuilder.Builder builder, final String fieldName,
|
||||||
final String fieldName, final Object expectedValue) {
|
final Object expectedValue) {
|
||||||
final Field factoryBeanField = ReflectionUtils
|
final Field factoryBeanField = ReflectionUtils.findField(FeignClientBuilder.Builder.class,
|
||||||
.findField(FeignClientBuilder.Builder.class, "feignClientFactoryBean");
|
"feignClientFactoryBean");
|
||||||
ReflectionUtils.makeAccessible(factoryBeanField);
|
ReflectionUtils.makeAccessible(factoryBeanField);
|
||||||
final FeignClientFactoryBean factoryBean = (FeignClientFactoryBean) ReflectionUtils
|
final FeignClientFactoryBean factoryBean = (FeignClientFactoryBean) ReflectionUtils.getField(factoryBeanField,
|
||||||
.getField(factoryBeanField, builder);
|
builder);
|
||||||
|
|
||||||
final Field field = ReflectionUtils.findField(FeignClientFactoryBean.class,
|
final Field field = ReflectionUtils.findField(FeignClientFactoryBean.class, fieldName);
|
||||||
fieldName);
|
|
||||||
ReflectionUtils.makeAccessible(field);
|
ReflectionUtils.makeAccessible(field);
|
||||||
final Object value = ReflectionUtils.getField(field, factoryBean);
|
final Object value = ReflectionUtils.getField(field, factoryBean);
|
||||||
assertThat(value).as("Expected value for the field '" + fieldName + "':")
|
assertThat(value).as("Expected value for the field '" + fieldName + "':").isEqualTo(expectedValue);
|
||||||
.isEqualTo(expectedValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
@@ -84,9 +80,8 @@ public class FeignClientBuilderTests {
|
|||||||
for (final Method method : FeignClient.class.getMethods()) {
|
for (final Method method : FeignClient.class.getMethods()) {
|
||||||
methodNames.add(method.getName());
|
methodNames.add(method.getName());
|
||||||
}
|
}
|
||||||
methodNames.removeAll(
|
methodNames.removeAll(Arrays.asList("annotationType", "value", "serviceId", "qualifier", "configuration",
|
||||||
Arrays.asList("annotationType", "value", "serviceId", "qualifier",
|
"primary", "equals", "hashCode", "toString"));
|
||||||
"configuration", "primary", "equals", "hashCode", "toString"));
|
|
||||||
Collections.sort(methodNames);
|
Collections.sort(methodNames);
|
||||||
// If this safety check fails the Builder has to be updated.
|
// If this safety check fails the Builder has to be updated.
|
||||||
// (1) Either a field was removed from the FeignClient annotation and so it has to
|
// (1) Either a field was removed from the FeignClient annotation and so it has to
|
||||||
@@ -94,44 +89,54 @@ public class FeignClientBuilderTests {
|
|||||||
// on this builder class.
|
// on this builder class.
|
||||||
// (2) Or a new field was added and the builder class has to be extended with this
|
// (2) Or a new field was added and the builder class has to be extended with this
|
||||||
// new field.
|
// new field.
|
||||||
assertThat(methodNames).containsExactly("contextId", "decode404", "fallback",
|
assertThat(methodNames).containsExactly("contextId", "decode404", "fallback", "fallbackFactory", "name", "path",
|
||||||
"fallbackFactory", "name", "path", "url");
|
"url");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void forType_preinitializedBuilder() {
|
public void forType_preinitializedBuilder() {
|
||||||
// when:
|
// when:
|
||||||
final FeignClientBuilder.Builder builder = this.feignClientBuilder
|
final FeignClientBuilder.Builder builder = this.feignClientBuilder.forType(TestFeignClient.class, "TestClient");
|
||||||
.forType(TestFeignClient.class, "TestClient");
|
|
||||||
|
|
||||||
// then:
|
// then:
|
||||||
assertFactoryBeanField(builder, "applicationContext", this.applicationContext);
|
assertFactoryBeanField(builder, "applicationContext", this.applicationContext);
|
||||||
assertFactoryBeanField(builder, "type", TestFeignClient.class);
|
assertFactoryBeanField(builder, "type", TestFeignClient.class);
|
||||||
assertFactoryBeanField(builder, "name", "TestClient");
|
assertFactoryBeanField(builder, "name", "TestClient");
|
||||||
assertFactoryBeanField(builder, "contextId", "TestClient");
|
assertFactoryBeanField(builder, "contextId", "TestClient");
|
||||||
assertFactoryBeanField(builder, "inheritParentContext", true);
|
|
||||||
|
|
||||||
// and:
|
// and:
|
||||||
assertFactoryBeanField(builder, "url",
|
assertFactoryBeanField(builder, "url", getDefaultValueFromFeignClientAnnotation("url"));
|
||||||
getDefaultValueFromFeignClientAnnotation("url"));
|
assertFactoryBeanField(builder, "path", getDefaultValueFromFeignClientAnnotation("path"));
|
||||||
assertFactoryBeanField(builder, "path",
|
assertFactoryBeanField(builder, "decode404", getDefaultValueFromFeignClientAnnotation("decode404"));
|
||||||
getDefaultValueFromFeignClientAnnotation("path"));
|
assertFactoryBeanField(builder, "fallback", getDefaultValueFromFeignClientAnnotation("fallback"));
|
||||||
assertFactoryBeanField(builder, "decode404",
|
assertFactoryBeanField(builder, "fallbackFactory", getDefaultValueFromFeignClientAnnotation("fallbackFactory"));
|
||||||
getDefaultValueFromFeignClientAnnotation("decode404"));
|
|
||||||
assertFactoryBeanField(builder, "fallback",
|
|
||||||
getDefaultValueFromFeignClientAnnotation("fallback"));
|
|
||||||
assertFactoryBeanField(builder, "fallbackFactory",
|
|
||||||
getDefaultValueFromFeignClientAnnotation("fallbackFactory"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void forType_allFieldsSetOnBuilder() {
|
public void forType_allFieldsSetOnBuilder() {
|
||||||
// when:
|
// when:
|
||||||
|
final FeignClientBuilder.Builder builder = this.feignClientBuilder.forType(TestFeignClient.class, "TestClient")
|
||||||
|
.decode404(true).url("Url/").path("/Path").contextId("TestContext");
|
||||||
|
|
||||||
|
// then:
|
||||||
|
assertFactoryBeanField(builder, "applicationContext", this.applicationContext);
|
||||||
|
assertFactoryBeanField(builder, "type", TestFeignClient.class);
|
||||||
|
assertFactoryBeanField(builder, "name", "TestClient");
|
||||||
|
assertFactoryBeanField(builder, "contextId", "TestContext");
|
||||||
|
|
||||||
|
// and:
|
||||||
|
assertFactoryBeanField(builder, "url", "http://Url/");
|
||||||
|
assertFactoryBeanField(builder, "path", "/Path");
|
||||||
|
assertFactoryBeanField(builder, "decode404", true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void forType_clientFactoryBeanProvided() {
|
||||||
|
// when:
|
||||||
final FeignClientBuilder.Builder builder = this.feignClientBuilder
|
final FeignClientBuilder.Builder builder = this.feignClientBuilder
|
||||||
.forType(TestFeignClient.class, "TestClient").inheritParentContext(false)
|
.forType(TestFeignClient.class, new FeignClientFactoryBean(), "TestClient").decode404(true)
|
||||||
.fallback(TestFeignClientFallback.class)
|
.path("Path/").url("Url/").contextId("TestContext");
|
||||||
.fallbackFactory(TestFeignClientFallbackFactory.class).decode404(true)
|
|
||||||
.url("Url/").path("/Path").contextId("TestContext");
|
|
||||||
|
|
||||||
// then:
|
// then:
|
||||||
assertFactoryBeanField(builder, "applicationContext", this.applicationContext);
|
assertFactoryBeanField(builder, "applicationContext", this.applicationContext);
|
||||||
@@ -143,21 +148,19 @@ public class FeignClientBuilderTests {
|
|||||||
assertFactoryBeanField(builder, "url", "http://Url/");
|
assertFactoryBeanField(builder, "url", "http://Url/");
|
||||||
assertFactoryBeanField(builder, "path", "/Path");
|
assertFactoryBeanField(builder, "path", "/Path");
|
||||||
assertFactoryBeanField(builder, "decode404", true);
|
assertFactoryBeanField(builder, "decode404", true);
|
||||||
assertFactoryBeanField(builder, "inheritParentContext", false);
|
|
||||||
assertFactoryBeanField(builder, "fallback", TestFeignClientFallback.class);
|
|
||||||
assertFactoryBeanField(builder, "fallbackFactory",
|
|
||||||
TestFeignClientFallbackFactory.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void forType_build() {
|
public void forType_build() {
|
||||||
// given:
|
// given:
|
||||||
Mockito.when(this.applicationContext.getBean(FeignContext.class))
|
Mockito.when(this.applicationContext.getBean(FeignContext.class)).thenThrow(new ClosedFileSystemException()); // throw
|
||||||
.thenThrow(new ClosedFileSystemException()); // throw an unusual exception
|
// an
|
||||||
// in the
|
// unusual
|
||||||
// FeignClientFactoryBean
|
// exception
|
||||||
final FeignClientBuilder.Builder builder = this.feignClientBuilder
|
// in
|
||||||
.forType(TestClient.class, "TestClient");
|
// the
|
||||||
|
// FeignClientFactoryBean
|
||||||
|
final FeignClientBuilder.Builder builder = this.feignClientBuilder.forType(TestClient.class, "TestClient");
|
||||||
|
|
||||||
// expect: 'the build will fail right after calling build() with the mocked
|
// expect: 'the build will fail right after calling build() with the mocked
|
||||||
// unusual exception'
|
// unusual exception'
|
||||||
@@ -169,18 +172,4 @@ public class FeignClientBuilderTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TestFeignClientFallback implements TestFeignClient {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TestFeignClientFallbackFactory
|
|
||||||
implements FallbackFactory<TestFeignClient> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TestFeignClientFallback create(Throwable throwable) {
|
|
||||||
return new TestFeignClientFallback();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-4
@@ -67,8 +67,7 @@ public class FeignClientErrorDecoderTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void errorDecoderInConfiguration() {
|
public void errorDecoderInConfiguration() {
|
||||||
assertThat(this.context.getInstance("foo", ErrorDecoder.class))
|
assertThat(this.context.getInstance("foo", ErrorDecoder.class)).isInstanceOf(ErrorDecoder.Default.class);
|
||||||
.isInstanceOf(ErrorDecoder.Default.class);
|
|
||||||
assertThat(this.context.getInstance("bar", ErrorDecoder.class)).isNull();
|
assertThat(this.context.getInstance("bar", ErrorDecoder.class)).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,8 +89,7 @@ public class FeignClientErrorDecoderTests {
|
|||||||
Map<Method, InvocationHandlerFactory.MethodHandler> dispatch = (Map<Method, InvocationHandlerFactory.MethodHandler>) ReflectionTestUtils
|
Map<Method, InvocationHandlerFactory.MethodHandler> dispatch = (Map<Method, InvocationHandlerFactory.MethodHandler>) ReflectionTestUtils
|
||||||
.getField(invocationHandler, "dispatch");
|
.getField(invocationHandler, "dispatch");
|
||||||
Method key = new ArrayList<>(dispatch.keySet()).get(0);
|
Method key = new ArrayList<>(dispatch.keySet()).get(0);
|
||||||
return ReflectionTestUtils.getField(
|
return ReflectionTestUtils.getField(ReflectionTestUtils.getField(dispatch.get(key), "asyncResponseHandler"),
|
||||||
ReflectionTestUtils.getField(dispatch.get(key), "asyncResponseHandler"),
|
|
||||||
"errorDecoder");
|
"errorDecoder");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-8
@@ -29,6 +29,7 @@ import org.junit.Test;
|
|||||||
|
|
||||||
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
|
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
|
||||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||||
|
import org.springframework.cloud.client.loadbalancer.LoadBalancerProperties;
|
||||||
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
|
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
|
||||||
import org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration;
|
import org.springframework.cloud.loadbalancer.config.LoadBalancerAutoConfiguration;
|
||||||
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
|
||||||
@@ -52,8 +53,7 @@ public class FeignClientFactoryTests {
|
|||||||
parent.refresh();
|
parent.refresh();
|
||||||
FeignContext context = new FeignContext();
|
FeignContext context = new FeignContext();
|
||||||
context.setApplicationContext(parent);
|
context.setApplicationContext(parent);
|
||||||
context.setConfigurations(Arrays.asList(getSpec("foo", FooConfig.class),
|
context.setConfigurations(Arrays.asList(getSpec("foo", FooConfig.class), getSpec("bar", BarConfig.class)));
|
||||||
getSpec("bar", BarConfig.class)));
|
|
||||||
|
|
||||||
Foo foo = context.getInstance("foo", Foo.class);
|
Foo foo = context.getInstance("foo", Foo.class);
|
||||||
assertThat(foo).as("foo was null").isNotNull();
|
assertThat(foo).as("foo was null").isNotNull();
|
||||||
@@ -67,8 +67,7 @@ public class FeignClientFactoryTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldRedirectToDelegateWhenUrlSet() {
|
public void shouldRedirectToDelegateWhenUrlSet() {
|
||||||
new ApplicationContextRunner().withUserConfiguration(TestConfig.class)
|
new ApplicationContextRunner().withUserConfiguration(TestConfig.class).run(this::defaultClientUsed);
|
||||||
.run(this::defaultClientUsed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked", "ConstantConditions" })
|
@SuppressWarnings({ "unchecked", "ConstantConditions" })
|
||||||
@@ -98,15 +97,14 @@ public class FeignClientFactoryTests {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
BlockingLoadBalancerClient loadBalancerClient() {
|
BlockingLoadBalancerClient loadBalancerClient() {
|
||||||
return new BlockingLoadBalancerClient(new LoadBalancerClientFactory());
|
return new BlockingLoadBalancerClient(new LoadBalancerClientFactory(), new LoadBalancerProperties());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
FeignContext feignContext() {
|
FeignContext feignContext() {
|
||||||
FeignContext feignContext = new FeignContext();
|
FeignContext feignContext = new FeignContext();
|
||||||
feignContext.setConfigurations(
|
feignContext.setConfigurations(Collections.singletonList(
|
||||||
Collections.singletonList(new FeignClientSpecification("test",
|
new FeignClientSpecification("test", new Class[] { LoadBalancerAutoConfiguration.class })));
|
||||||
new Class[] { LoadBalancerAutoConfiguration.class })));
|
|
||||||
return feignContext;
|
return feignContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+10
-34
@@ -18,7 +18,6 @@ package org.springframework.cloud.openfeign;
|
|||||||
|
|
||||||
import feign.Contract;
|
import feign.Contract;
|
||||||
import feign.ExceptionPropagationPolicy;
|
import feign.ExceptionPropagationPolicy;
|
||||||
import feign.Feign;
|
|
||||||
import feign.Logger;
|
import feign.Logger;
|
||||||
import feign.QueryMapEncoder;
|
import feign.QueryMapEncoder;
|
||||||
import feign.Request;
|
import feign.Request;
|
||||||
@@ -29,21 +28,18 @@ import feign.auth.BasicAuthRequestInterceptor;
|
|||||||
import feign.codec.Decoder;
|
import feign.codec.Decoder;
|
||||||
import feign.codec.Encoder;
|
import feign.codec.Encoder;
|
||||||
import feign.codec.ErrorDecoder;
|
import feign.codec.ErrorDecoder;
|
||||||
import feign.hystrix.HystrixFeign;
|
|
||||||
import feign.optionals.OptionalDecoder;
|
import feign.optionals.OptionalDecoder;
|
||||||
import feign.querymap.BeanQueryMapEncoder;
|
import feign.querymap.BeanQueryMapEncoder;
|
||||||
import feign.slf4j.Slf4jLogger;
|
import feign.slf4j.Slf4jLogger;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
|
||||||
import org.springframework.cloud.openfeign.support.PageableSpringEncoder;
|
import org.springframework.cloud.openfeign.support.PageableSpringEncoder;
|
||||||
import org.springframework.cloud.openfeign.support.SpringMvcContract;
|
import org.springframework.cloud.openfeign.support.SpringMvcContract;
|
||||||
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.test.annotation.DirtiesContext;
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
@@ -99,14 +95,12 @@ class FeignClientOverrideDefaultsTests {
|
|||||||
@Test
|
@Test
|
||||||
void overrideLoggerLevel() {
|
void overrideLoggerLevel() {
|
||||||
assertThat(context.getInstance("foo", Logger.Level.class)).isNull();
|
assertThat(context.getInstance("foo", Logger.Level.class)).isNull();
|
||||||
assertThat(context.getInstance("bar", Logger.Level.class))
|
assertThat(context.getInstance("bar", Logger.Level.class)).isEqualTo(Logger.Level.HEADERS);
|
||||||
.isEqualTo(Logger.Level.HEADERS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void overrideRetryer() {
|
void overrideRetryer() {
|
||||||
assertThat(context.getInstance("foo", Retryer.class))
|
assertThat(context.getInstance("foo", Retryer.class)).isEqualTo(Retryer.NEVER_RETRY);
|
||||||
.isEqualTo(Retryer.NEVER_RETRY);
|
|
||||||
Retryer.Default.class.cast(context.getInstance("bar", Retryer.class));
|
Retryer.Default.class.cast(context.getInstance("bar", Retryer.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,12 +110,6 @@ class FeignClientOverrideDefaultsTests {
|
|||||||
ErrorDecoder.Default.class.cast(context.getInstance("bar", ErrorDecoder.class));
|
ErrorDecoder.Default.class.cast(context.getInstance("bar", ErrorDecoder.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
void overrideBuilder() {
|
|
||||||
HystrixFeign.Builder.class.cast(context.getInstance("foo", Feign.Builder.class));
|
|
||||||
Feign.Builder.class.cast(context.getInstance("bar", Feign.Builder.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void overrideRequestOptions() {
|
void overrideRequestOptions() {
|
||||||
assertThat(context.getInstance("foo", Request.Options.class)).isNull();
|
assertThat(context.getInstance("foo", Request.Options.class)).isNull();
|
||||||
@@ -132,29 +120,24 @@ class FeignClientOverrideDefaultsTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
void overrideQueryMapEncoder() {
|
void overrideQueryMapEncoder() {
|
||||||
QueryMapEncoder.Default.class
|
QueryMapEncoder.Default.class.cast(context.getInstance("foo", QueryMapEncoder.class));
|
||||||
.cast(context.getInstance("foo", QueryMapEncoder.class));
|
|
||||||
BeanQueryMapEncoder.class.cast(context.getInstance("bar", QueryMapEncoder.class));
|
BeanQueryMapEncoder.class.cast(context.getInstance("bar", QueryMapEncoder.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void addRequestInterceptor() {
|
void addRequestInterceptor() {
|
||||||
assertThat(context.getInstances("foo", RequestInterceptor.class).size())
|
assertThat(context.getInstances("foo", RequestInterceptor.class).size()).isEqualTo(1);
|
||||||
.isEqualTo(1);
|
assertThat(context.getInstances("bar", RequestInterceptor.class).size()).isEqualTo(2);
|
||||||
assertThat(context.getInstances("bar", RequestInterceptor.class).size())
|
|
||||||
.isEqualTo(2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void exceptionPropagationPolicy() {
|
void exceptionPropagationPolicy() {
|
||||||
assertThat(context.getInstances("foo", ExceptionPropagationPolicy.class))
|
assertThat(context.getInstances("foo", ExceptionPropagationPolicy.class)).isEmpty();
|
||||||
.isEmpty();
|
|
||||||
assertThat(context.getInstances("bar", ExceptionPropagationPolicy.class))
|
assertThat(context.getInstances("bar", ExceptionPropagationPolicy.class))
|
||||||
.containsValues(ExceptionPropagationPolicy.UNWRAP);
|
.containsValues(ExceptionPropagationPolicy.UNWRAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
@FeignClient(name = "foo", url = "https://foo",
|
@FeignClient(name = "foo", url = "https://foo", configuration = FooConfiguration.class)
|
||||||
configuration = FooConfiguration.class)
|
|
||||||
interface FooClient {
|
interface FooClient {
|
||||||
|
|
||||||
@RequestLine("GET /")
|
@RequestLine("GET /")
|
||||||
@@ -162,8 +145,7 @@ class FeignClientOverrideDefaultsTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FeignClient(name = "bar", url = "https://bar",
|
@FeignClient(name = "bar", url = "https://bar", configuration = BarConfiguration.class)
|
||||||
configuration = BarConfiguration.class)
|
|
||||||
interface BarClient {
|
interface BarClient {
|
||||||
|
|
||||||
@RequestMapping(value = "/", method = RequestMethod.GET)
|
@RequestMapping(value = "/", method = RequestMethod.GET)
|
||||||
@@ -173,8 +155,7 @@ class FeignClientOverrideDefaultsTests {
|
|||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@EnableFeignClients(clients = { FooClient.class, BarClient.class })
|
@EnableFeignClients(clients = { FooClient.class, BarClient.class })
|
||||||
@Import({ PropertyPlaceholderAutoConfiguration.class, ArchaiusAutoConfiguration.class,
|
@EnableAutoConfiguration
|
||||||
FeignAutoConfiguration.class })
|
|
||||||
protected static class TestConfiguration {
|
protected static class TestConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@@ -207,11 +188,6 @@ class FeignClientOverrideDefaultsTests {
|
|||||||
return new Contract.Default();
|
return new Contract.Default();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
|
||||||
public Feign.Builder feignBuilder() {
|
|
||||||
return HystrixFeign.builder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public QueryMapEncoder queryMapEncoder() {
|
public QueryMapEncoder queryMapEncoder() {
|
||||||
return new feign.QueryMapEncoder.Default();
|
return new feign.QueryMapEncoder.Default();
|
||||||
|
|||||||
+6
-12
@@ -63,11 +63,9 @@ public class FeignClientUsingConfigurerTest {
|
|||||||
.getBean(BEAN_NAME_PREFIX + "TestFeignClient");
|
.getBean(BEAN_NAME_PREFIX + "TestFeignClient");
|
||||||
Feign.Builder builder = factoryBean.feign(context);
|
Feign.Builder builder = factoryBean.feign(context);
|
||||||
|
|
||||||
List<RequestInterceptor> interceptors = (List) getBuilderValue(builder,
|
List<RequestInterceptor> interceptors = (List) getBuilderValue(builder, "requestInterceptors");
|
||||||
"requestInterceptors");
|
|
||||||
assertThat(interceptors.size()).as("interceptors not set").isEqualTo(3);
|
assertThat(interceptors.size()).as("interceptors not set").isEqualTo(3);
|
||||||
assertThat(getBuilderValue(builder, "logLevel")).as("log level not set")
|
assertThat(getBuilderValue(builder, "logLevel")).as("log level not set").isEqualTo(Logger.Level.FULL);
|
||||||
.isEqualTo(Logger.Level.FULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object getBuilderValue(Feign.Builder builder, String member) {
|
private Object getBuilderValue(Feign.Builder builder, String member) {
|
||||||
@@ -83,12 +81,10 @@ public class FeignClientUsingConfigurerTest {
|
|||||||
.getBean(BEAN_NAME_PREFIX + "NoInheritFeignClient");
|
.getBean(BEAN_NAME_PREFIX + "NoInheritFeignClient");
|
||||||
Feign.Builder builder = factoryBean.feign(context);
|
Feign.Builder builder = factoryBean.feign(context);
|
||||||
|
|
||||||
List<RequestInterceptor> interceptors = (List) getBuilderValue(builder,
|
List<RequestInterceptor> interceptors = (List) getBuilderValue(builder, "requestInterceptors");
|
||||||
"requestInterceptors");
|
|
||||||
|
|
||||||
assertThat(interceptors).as("interceptors not set").isEmpty();
|
assertThat(interceptors).as("interceptors not set").isEmpty();
|
||||||
assertThat(factoryBean.isInheritParentContext())
|
assertThat(factoryBean.isInheritParentContext()).as("is inheriting from parent configuration").isFalse();
|
||||||
.as("is inheriting from parent configuration").isFalse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -97,8 +93,7 @@ public class FeignClientUsingConfigurerTest {
|
|||||||
.getBean(BEAN_NAME_PREFIX + "NoInheritFeignClient");
|
.getBean(BEAN_NAME_PREFIX + "NoInheritFeignClient");
|
||||||
Feign.Builder builder = factoryBean.feign(context);
|
Feign.Builder builder = factoryBean.feign(context);
|
||||||
|
|
||||||
assertThat(getBuilderValue(builder, "logLevel")).as("log level not set")
|
assertThat(getBuilderValue(builder, "logLevel")).as("log level not set").isEqualTo(Logger.Level.HEADERS);
|
||||||
.isEqualTo(Logger.Level.HEADERS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
@@ -140,8 +135,7 @@ public class FeignClientUsingConfigurerTest {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FeignClient(name = "noInheritFeignClient",
|
@FeignClient(name = "noInheritFeignClient", configuration = NoInheritConfiguration.class)
|
||||||
configuration = NoInheritConfiguration.class)
|
|
||||||
interface NoInheritFeignClient {
|
interface NoInheritFeignClient {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-48
@@ -79,8 +79,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("FieldMayBeFinal")
|
@SuppressWarnings("FieldMayBeFinal")
|
||||||
@RunWith(SpringJUnit4ClassRunner.class)
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
@SpringBootTest(classes = FeignClientUsingPropertiesTests.Application.class,
|
@SpringBootTest(classes = FeignClientUsingPropertiesTests.Application.class, webEnvironment = RANDOM_PORT)
|
||||||
webEnvironment = RANDOM_PORT)
|
|
||||||
@TestPropertySource("classpath:feign-properties.properties")
|
@TestPropertySource("classpath:feign-properties.properties")
|
||||||
@DirtiesContext
|
@DirtiesContext
|
||||||
public class FeignClientUsingPropertiesTests {
|
public class FeignClientUsingPropertiesTests {
|
||||||
@@ -124,40 +123,32 @@ public class FeignClientUsingPropertiesTests {
|
|||||||
formFactoryBean.setType(FeignClientFactoryBean.class);
|
formFactoryBean.setType(FeignClientFactoryBean.class);
|
||||||
|
|
||||||
this.defaultHeadersAndQuerySingleParamsFeignClientFactoryBean = new FeignClientFactoryBean();
|
this.defaultHeadersAndQuerySingleParamsFeignClientFactoryBean = new FeignClientFactoryBean();
|
||||||
this.defaultHeadersAndQuerySingleParamsFeignClientFactoryBean
|
this.defaultHeadersAndQuerySingleParamsFeignClientFactoryBean.setContextId("singleValue");
|
||||||
.setContextId("singleValue");
|
this.defaultHeadersAndQuerySingleParamsFeignClientFactoryBean.setType(FeignClientFactoryBean.class);
|
||||||
this.defaultHeadersAndQuerySingleParamsFeignClientFactoryBean
|
|
||||||
.setType(FeignClientFactoryBean.class);
|
|
||||||
|
|
||||||
this.defaultHeadersAndQueryMultipleParamsFeignClientFactoryBean = new FeignClientFactoryBean();
|
this.defaultHeadersAndQueryMultipleParamsFeignClientFactoryBean = new FeignClientFactoryBean();
|
||||||
this.defaultHeadersAndQueryMultipleParamsFeignClientFactoryBean
|
this.defaultHeadersAndQueryMultipleParamsFeignClientFactoryBean.setContextId("multipleValue");
|
||||||
.setContextId("multipleValue");
|
this.defaultHeadersAndQueryMultipleParamsFeignClientFactoryBean.setType(FeignClientFactoryBean.class);
|
||||||
this.defaultHeadersAndQueryMultipleParamsFeignClientFactoryBean
|
|
||||||
.setType(FeignClientFactoryBean.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public FooClient fooClient() {
|
public FooClient fooClient() {
|
||||||
fooFactoryBean.setApplicationContext(applicationContext);
|
fooFactoryBean.setApplicationContext(applicationContext);
|
||||||
return fooFactoryBean.feign(context).target(FooClient.class,
|
return fooFactoryBean.feign(context).target(FooClient.class, "http://localhost:" + port);
|
||||||
"http://localhost:" + port);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public BarClient barClient() {
|
public BarClient barClient() {
|
||||||
barFactoryBean.setApplicationContext(applicationContext);
|
barFactoryBean.setApplicationContext(applicationContext);
|
||||||
return barFactoryBean.feign(context).target(BarClient.class,
|
return barFactoryBean.feign(context).target(BarClient.class, "http://localhost:" + port);
|
||||||
"http://localhost:" + port);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnwrapClient unwrapClient() {
|
public UnwrapClient unwrapClient() {
|
||||||
unwrapFactoryBean.setApplicationContext(applicationContext);
|
unwrapFactoryBean.setApplicationContext(applicationContext);
|
||||||
return unwrapFactoryBean.feign(context).target(UnwrapClient.class,
|
return unwrapFactoryBean.feign(context).target(UnwrapClient.class, "http://localhost:" + port);
|
||||||
"http://localhost:" + port);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public FormClient formClient() {
|
public FormClient formClient() {
|
||||||
formFactoryBean.setApplicationContext(applicationContext);
|
formFactoryBean.setApplicationContext(applicationContext);
|
||||||
return formFactoryBean.feign(context).target(FormClient.class,
|
return formFactoryBean.feign(context).target(FormClient.class, "http://localhost:" + port);
|
||||||
"http://localhost:" + port);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -194,23 +185,18 @@ public class FeignClientUsingPropertiesTests {
|
|||||||
@Test
|
@Test
|
||||||
public void testMultipleValue() {
|
public void testMultipleValue() {
|
||||||
List<String> response = multipleValueClient().multipleValue();
|
List<String> response = multipleValueClient().multipleValue();
|
||||||
assertThat(response).isEqualTo(
|
assertThat(response).isEqualTo(Arrays.asList("header1", "header2", "parameter1", "parameter2"));
|
||||||
Arrays.asList("header1", "header2", "parameter1", "parameter2"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SingleValueClient singleValueClient() {
|
public SingleValueClient singleValueClient() {
|
||||||
this.defaultHeadersAndQuerySingleParamsFeignClientFactoryBean
|
this.defaultHeadersAndQuerySingleParamsFeignClientFactoryBean.setApplicationContext(this.applicationContext);
|
||||||
.setApplicationContext(this.applicationContext);
|
return this.defaultHeadersAndQuerySingleParamsFeignClientFactoryBean.feign(this.context)
|
||||||
return this.defaultHeadersAndQuerySingleParamsFeignClientFactoryBean
|
|
||||||
.feign(this.context)
|
|
||||||
.target(SingleValueClient.class, "http://localhost:" + this.port);
|
.target(SingleValueClient.class, "http://localhost:" + this.port);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MultipleValueClient multipleValueClient() {
|
public MultipleValueClient multipleValueClient() {
|
||||||
this.defaultHeadersAndQueryMultipleParamsFeignClientFactoryBean
|
this.defaultHeadersAndQueryMultipleParamsFeignClientFactoryBean.setApplicationContext(this.applicationContext);
|
||||||
.setApplicationContext(this.applicationContext);
|
return this.defaultHeadersAndQueryMultipleParamsFeignClientFactoryBean.feign(this.context)
|
||||||
return this.defaultHeadersAndQueryMultipleParamsFeignClientFactoryBean
|
|
||||||
.feign(this.context)
|
|
||||||
.target(MultipleValueClient.class, "http://localhost:" + this.port);
|
.target(MultipleValueClient.class, "http://localhost:" + this.port);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,8 +207,8 @@ public class FeignClientUsingPropertiesTests {
|
|||||||
readTimeoutFactoryBean.setType(FeignClientFactoryBean.class);
|
readTimeoutFactoryBean.setType(FeignClientFactoryBean.class);
|
||||||
readTimeoutFactoryBean.setApplicationContext(applicationContext);
|
readTimeoutFactoryBean.setApplicationContext(applicationContext);
|
||||||
|
|
||||||
TimeoutClient client = readTimeoutFactoryBean.feign(context)
|
TimeoutClient client = readTimeoutFactoryBean.feign(context).target(TimeoutClient.class,
|
||||||
.target(TimeoutClient.class, "http://localhost:" + port);
|
"http://localhost:" + port);
|
||||||
|
|
||||||
Request.Options options = getRequestOptions((Proxy) client);
|
Request.Options options = getRequestOptions((Proxy) client);
|
||||||
|
|
||||||
@@ -237,8 +223,8 @@ public class FeignClientUsingPropertiesTests {
|
|||||||
readTimeoutFactoryBean.setType(FeignClientFactoryBean.class);
|
readTimeoutFactoryBean.setType(FeignClientFactoryBean.class);
|
||||||
readTimeoutFactoryBean.setApplicationContext(applicationContext);
|
readTimeoutFactoryBean.setApplicationContext(applicationContext);
|
||||||
|
|
||||||
TimeoutClient client = readTimeoutFactoryBean.feign(context)
|
TimeoutClient client = readTimeoutFactoryBean.feign(context).target(TimeoutClient.class,
|
||||||
.target(TimeoutClient.class, "http://localhost:" + port);
|
"http://localhost:" + port);
|
||||||
|
|
||||||
Request.Options options = getRequestOptions((Proxy) client);
|
Request.Options options = getRequestOptions((Proxy) client);
|
||||||
|
|
||||||
@@ -251,8 +237,7 @@ public class FeignClientUsingPropertiesTests {
|
|||||||
Map<Method, InvocationHandlerFactory.MethodHandler> dispatch = (Map<Method, InvocationHandlerFactory.MethodHandler>) ReflectionTestUtils
|
Map<Method, InvocationHandlerFactory.MethodHandler> dispatch = (Map<Method, InvocationHandlerFactory.MethodHandler>) ReflectionTestUtils
|
||||||
.getField(Objects.requireNonNull(invocationHandler), "dispatch");
|
.getField(Objects.requireNonNull(invocationHandler), "dispatch");
|
||||||
Method key = new ArrayList<>(dispatch.keySet()).get(0);
|
Method key = new ArrayList<>(dispatch.keySet()).get(0);
|
||||||
return (Request.Options) ReflectionTestUtils.getField(dispatch.get(key),
|
return (Request.Options) ReflectionTestUtils.getField(dispatch.get(key), "options");
|
||||||
"options");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected interface FooClient {
|
protected interface FooClient {
|
||||||
@@ -313,8 +298,7 @@ public class FeignClientUsingPropertiesTests {
|
|||||||
|
|
||||||
@RequestMapping(method = RequestMethod.GET, value = "/foo")
|
@RequestMapping(method = RequestMethod.GET, value = "/foo")
|
||||||
public String foo(HttpServletRequest request) throws IllegalAccessException {
|
public String foo(HttpServletRequest request) throws IllegalAccessException {
|
||||||
if ("Foo".equals(request.getHeader("Foo"))
|
if ("Foo".equals(request.getHeader("Foo")) && "Bar".equals(request.getHeader("Bar"))) {
|
||||||
&& "Bar".equals(request.getHeader("Bar"))) {
|
|
||||||
return "OK";
|
return "OK";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -328,8 +312,7 @@ public class FeignClientUsingPropertiesTests {
|
|||||||
return "OK";
|
return "OK";
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping(path = "/form",
|
@PostMapping(path = "/form", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
||||||
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
|
|
||||||
public String form(HttpServletRequest request) {
|
public String form(HttpServletRequest request) {
|
||||||
return request.getParameter("form");
|
return request.getParameter("form");
|
||||||
}
|
}
|
||||||
@@ -337,16 +320,15 @@ public class FeignClientUsingPropertiesTests {
|
|||||||
@GetMapping(path = "/singleValue")
|
@GetMapping(path = "/singleValue")
|
||||||
public List<String> singleValue(@RequestHeader List<String> singleValueHeaders,
|
public List<String> singleValue(@RequestHeader List<String> singleValueHeaders,
|
||||||
@RequestParam List<String> singleValueParameters) {
|
@RequestParam List<String> singleValueParameters) {
|
||||||
return Stream.of(singleValueHeaders, singleValueParameters)
|
return Stream.of(singleValueHeaders, singleValueParameters).flatMap(Collection::stream)
|
||||||
.flatMap(Collection::stream).collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(path = "/multipleValue")
|
@GetMapping(path = "/multipleValue")
|
||||||
public List<String> multipleValue(
|
public List<String> multipleValue(@RequestHeader List<String> multipleValueHeaders,
|
||||||
@RequestHeader List<String> multipleValueHeaders,
|
|
||||||
@RequestParam List<String> multipleValueParameters) {
|
@RequestParam List<String> multipleValueParameters) {
|
||||||
return Stream.of(multipleValueHeaders, multipleValueParameters)
|
return Stream.of(multipleValueHeaders, multipleValueParameters).flatMap(Collection::stream)
|
||||||
.flatMap(Collection::stream).collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -390,16 +372,14 @@ public class FeignClientUsingPropertiesTests {
|
|||||||
public static class FormEncoder implements Encoder {
|
public static class FormEncoder implements Encoder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void encode(Object o, Type type, RequestTemplate requestTemplate)
|
public void encode(Object o, Type type, RequestTemplate requestTemplate) throws EncodeException {
|
||||||
throws EncodeException {
|
|
||||||
Map<String, String> form = (Map<String, String>) o;
|
Map<String, String> form = (Map<String, String>) o;
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
form.forEach((key, value) -> {
|
form.forEach((key, value) -> {
|
||||||
builder.append(key + "=" + value + "&");
|
builder.append(key + "=" + value + "&");
|
||||||
});
|
});
|
||||||
|
|
||||||
requestTemplate.header(HttpHeaders.CONTENT_TYPE,
|
requestTemplate.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
|
||||||
MediaType.APPLICATION_FORM_URLENCODED_VALUE);
|
|
||||||
requestTemplate.body(builder.toString());
|
requestTemplate.body(builder.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-12
@@ -22,7 +22,6 @@ import org.junit.Test;
|
|||||||
|
|
||||||
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.cloud.openfeign.test.TestAutoConfiguration;
|
|
||||||
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;
|
||||||
@@ -96,17 +95,15 @@ public class FeignClientsRegistrarTests {
|
|||||||
@Test
|
@Test
|
||||||
public void shouldPassSubLevelFeignClient() {
|
public void shouldPassSubLevelFeignClient() {
|
||||||
AnnotationConfigApplicationContext config = new AnnotationConfigApplicationContext();
|
AnnotationConfigApplicationContext config = new AnnotationConfigApplicationContext();
|
||||||
((DefaultListableBeanFactory) config.getBeanFactory())
|
((DefaultListableBeanFactory) config.getBeanFactory()).setAllowBeanDefinitionOverriding(false);
|
||||||
.setAllowBeanDefinitionOverriding(false);
|
|
||||||
config.register(TopLevelSubLevelTestConfig.class);
|
config.register(TopLevelSubLevelTestConfig.class);
|
||||||
assertThatCode(() -> config.refresh()).as(
|
assertThatCode(() -> config.refresh())
|
||||||
"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();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FeignClient(name = "fallbackTestClient", url = "http://localhost:8080/",
|
@FeignClient(name = "fallbackTestClient", url = "http://localhost:8080/", fallback = FallbackClient.class)
|
||||||
fallback = FallbackClient.class)
|
|
||||||
protected interface FallbackClient {
|
protected interface FallbackClient {
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
||||||
@@ -132,16 +129,13 @@ public class FeignClientsRegistrarTests {
|
|||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
@EnableFeignClients(
|
@EnableFeignClients(clients = { FeignClientsRegistrarTests.FallbackFactoryClient.class })
|
||||||
clients = { FeignClientsRegistrarTests.FallbackFactoryClient.class })
|
|
||||||
protected static class FallbackFactoryTestConfig {
|
protected static class FallbackFactoryTestConfig {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EnableFeignClients(clients = {
|
@EnableFeignClients(clients = { org.springframework.cloud.openfeign.feignclientsregistrar.TopLevelClient.class,
|
||||||
org.springframework.cloud.openfeign.feignclientsregistrar.TopLevelClient.class,
|
|
||||||
org.springframework.cloud.openfeign.feignclientsregistrar.sub.SubLevelClient.class })
|
org.springframework.cloud.openfeign.feignclientsregistrar.sub.SubLevelClient.class })
|
||||||
@EnableAutoConfiguration(exclude = TestAutoConfiguration.class)
|
|
||||||
protected static class TopLevelSubLevelTestConfig {
|
protected static class TopLevelSubLevelTestConfig {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-9
@@ -28,7 +28,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||||
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
|
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
|
||||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
|
||||||
import org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration;
|
import org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration;
|
||||||
import org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingInterceptor;
|
import org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingInterceptor;
|
||||||
import org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration;
|
import org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration;
|
||||||
@@ -51,18 +50,15 @@ public class FeignCompressionTests {
|
|||||||
@Test
|
@Test
|
||||||
public void testInterceptors() {
|
public void testInterceptors() {
|
||||||
new ApplicationContextRunner()
|
new ApplicationContextRunner()
|
||||||
.withPropertyValues("feign.compression.response.enabled=true",
|
.withPropertyValues("feign.compression.response.enabled=true", "feign.compression.request.enabled=true",
|
||||||
"feign.compression.request.enabled=true",
|
|
||||||
"feign.okhttp.enabled=false")
|
"feign.okhttp.enabled=false")
|
||||||
.withConfiguration(AutoConfigurations.of(ArchaiusAutoConfiguration.class,
|
.withConfiguration(AutoConfigurations.of(FeignAutoConfiguration.class,
|
||||||
FeignAutoConfiguration.class,
|
FeignContentGzipEncodingAutoConfiguration.class, FeignAcceptGzipEncodingAutoConfiguration.class,
|
||||||
FeignContentGzipEncodingAutoConfiguration.class,
|
|
||||||
FeignAcceptGzipEncodingAutoConfiguration.class,
|
|
||||||
HttpClientConfiguration.class, PlainConfig.class))
|
HttpClientConfiguration.class, PlainConfig.class))
|
||||||
.run(context -> {
|
.run(context -> {
|
||||||
FeignContext feignContext = context.getBean(FeignContext.class);
|
FeignContext feignContext = context.getBean(FeignContext.class);
|
||||||
Map<String, RequestInterceptor> interceptors = feignContext
|
Map<String, RequestInterceptor> interceptors = feignContext.getInstances("foo",
|
||||||
.getInstances("foo", RequestInterceptor.class);
|
RequestInterceptor.class);
|
||||||
assertThat(interceptors.size()).isEqualTo(2);
|
assertThat(interceptors.size()).isEqualTo(2);
|
||||||
assertThat(interceptors.get("feignAcceptGzipEncodingInterceptor"))
|
assertThat(interceptors.get("feignAcceptGzipEncodingInterceptor"))
|
||||||
.isInstanceOf(FeignAcceptGzipEncodingInterceptor.class);
|
.isInstanceOf(FeignAcceptGzipEncodingInterceptor.class);
|
||||||
|
|||||||
+6
-12
@@ -39,11 +39,9 @@ public class FeignContextTest {
|
|||||||
|
|
||||||
FeignContext feignContext = new FeignContext();
|
FeignContext feignContext = new FeignContext();
|
||||||
feignContext.setApplicationContext(parent);
|
feignContext.setApplicationContext(parent);
|
||||||
feignContext.setConfigurations(
|
feignContext.setConfigurations(Lists.newArrayList(getSpec("empty", EmptyConfiguration.class)));
|
||||||
Lists.newArrayList(getSpec("empty", EmptyConfiguration.class)));
|
|
||||||
|
|
||||||
Logger.Level level = feignContext.getInstanceWithoutAncestors("empty",
|
Logger.Level level = feignContext.getInstanceWithoutAncestors("empty", Logger.Level.class);
|
||||||
Logger.Level.class);
|
|
||||||
|
|
||||||
assertThat(level).as("Logger was not null").isNull();
|
assertThat(level).as("Logger was not null").isNull();
|
||||||
}
|
}
|
||||||
@@ -59,8 +57,7 @@ public class FeignContextTest {
|
|||||||
|
|
||||||
FeignContext feignContext = new FeignContext();
|
FeignContext feignContext = new FeignContext();
|
||||||
feignContext.setApplicationContext(parent);
|
feignContext.setApplicationContext(parent);
|
||||||
feignContext.setConfigurations(
|
feignContext.setConfigurations(Lists.newArrayList(getSpec("empty", EmptyConfiguration.class)));
|
||||||
Lists.newArrayList(getSpec("empty", EmptyConfiguration.class)));
|
|
||||||
|
|
||||||
Collection<RequestInterceptor> interceptors = feignContext
|
Collection<RequestInterceptor> interceptors = feignContext
|
||||||
.getInstancesWithoutAncestors("empty", RequestInterceptor.class).values();
|
.getInstancesWithoutAncestors("empty", RequestInterceptor.class).values();
|
||||||
@@ -75,11 +72,9 @@ public class FeignContextTest {
|
|||||||
|
|
||||||
FeignContext feignContext = new FeignContext();
|
FeignContext feignContext = new FeignContext();
|
||||||
feignContext.setApplicationContext(parent);
|
feignContext.setApplicationContext(parent);
|
||||||
feignContext.setConfigurations(
|
feignContext.setConfigurations(Lists.newArrayList(getSpec("demo", DemoConfiguration.class)));
|
||||||
Lists.newArrayList(getSpec("demo", DemoConfiguration.class)));
|
|
||||||
|
|
||||||
Logger.Level level = feignContext.getInstanceWithoutAncestors("demo",
|
Logger.Level level = feignContext.getInstanceWithoutAncestors("demo", Logger.Level.class);
|
||||||
Logger.Level.class);
|
|
||||||
|
|
||||||
assertThat(level).isEqualTo(Logger.Level.FULL);
|
assertThat(level).isEqualTo(Logger.Level.FULL);
|
||||||
}
|
}
|
||||||
@@ -91,8 +86,7 @@ public class FeignContextTest {
|
|||||||
|
|
||||||
FeignContext feignContext = new FeignContext();
|
FeignContext feignContext = new FeignContext();
|
||||||
feignContext.setApplicationContext(parent);
|
feignContext.setApplicationContext(parent);
|
||||||
feignContext.setConfigurations(
|
feignContext.setConfigurations(Lists.newArrayList(getSpec("demo", DemoConfiguration.class)));
|
||||||
Lists.newArrayList(getSpec("demo", DemoConfiguration.class)));
|
|
||||||
|
|
||||||
Collection<RequestInterceptor> interceptors = feignContext
|
Collection<RequestInterceptor> interceptors = feignContext
|
||||||
.getInstancesWithoutAncestors("demo", RequestInterceptor.class).values();
|
.getInstancesWithoutAncestors("demo", RequestInterceptor.class).values();
|
||||||
|
|||||||
+6
-12
@@ -34,20 +34,16 @@ public class FeignErrorDecoderFactoryTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testNoDefaultFactory() {
|
public void testNoDefaultFactory() {
|
||||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SampleConfiguration1.class);
|
||||||
SampleConfiguration1.class);
|
String[] beanNamesForType = context.getBeanNamesForType(FeignErrorDecoderFactory.class);
|
||||||
String[] beanNamesForType = context
|
|
||||||
.getBeanNamesForType(FeignErrorDecoderFactory.class);
|
|
||||||
assertThat(beanNamesForType).isEmpty();
|
assertThat(beanNamesForType).isEmpty();
|
||||||
context.close();
|
context.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCustomErrorDecoderFactory() {
|
public void testCustomErrorDecoderFactory() {
|
||||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SampleConfiguration2.class);
|
||||||
SampleConfiguration2.class);
|
FeignErrorDecoderFactory errorDecoderFactory = context.getBean(FeignErrorDecoderFactory.class);
|
||||||
FeignErrorDecoderFactory errorDecoderFactory = context
|
|
||||||
.getBean(FeignErrorDecoderFactory.class);
|
|
||||||
assertThat(errorDecoderFactory).isNotNull();
|
assertThat(errorDecoderFactory).isNotNull();
|
||||||
ErrorDecoder errorDecoder = errorDecoderFactory.create(Object.class);
|
ErrorDecoder errorDecoder = errorDecoderFactory.create(Object.class);
|
||||||
assertThat(errorDecoder).isNotNull();
|
assertThat(errorDecoder).isNotNull();
|
||||||
@@ -57,10 +53,8 @@ public class FeignErrorDecoderFactoryTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCustomErrorDecoderFactoryNotOverwritingErrorDecoder() {
|
public void testCustomErrorDecoderFactoryNotOverwritingErrorDecoder() {
|
||||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SampleConfiguration3.class);
|
||||||
SampleConfiguration3.class);
|
FeignErrorDecoderFactory errorDecoderFactory = context.getBean(FeignErrorDecoderFactory.class);
|
||||||
FeignErrorDecoderFactory errorDecoderFactory = context
|
|
||||||
.getBean(FeignErrorDecoderFactory.class);
|
|
||||||
assertThat(errorDecoderFactory).isNotNull();
|
assertThat(errorDecoderFactory).isNotNull();
|
||||||
ErrorDecoder errorDecoderFromFactory = errorDecoderFactory.create(Object.class);
|
ErrorDecoder errorDecoderFromFactory = errorDecoderFactory.create(Object.class);
|
||||||
assertThat(errorDecoderFromFactory).isNotNull();
|
assertThat(errorDecoderFromFactory).isNotNull();
|
||||||
|
|||||||
+8
-16
@@ -53,10 +53,8 @@ public class FeignHttpClientConfigurationTests {
|
|||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
this.context = new SpringApplicationBuilder()
|
this.context = new SpringApplicationBuilder()
|
||||||
.properties("debug=true", "feign.httpclient.disableSslValidation=true")
|
.properties("debug=true", "feign.httpclient.disableSslValidation=true").web(WebApplicationType.NONE)
|
||||||
.web(WebApplicationType.NONE)
|
.sources(HttpClientConfiguration.class, FeignAutoConfiguration.class).run();
|
||||||
.sources(HttpClientConfiguration.class, FeignAutoConfiguration.class)
|
|
||||||
.run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@@ -68,13 +66,10 @@ public class FeignHttpClientConfigurationTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void disableSslTest() throws Exception {
|
public void disableSslTest() throws Exception {
|
||||||
HttpClientConnectionManager connectionManager = this.context
|
HttpClientConnectionManager connectionManager = this.context.getBean(HttpClientConnectionManager.class);
|
||||||
.getBean(HttpClientConnectionManager.class);
|
Lookup<ConnectionSocketFactory> socketFactoryRegistry = getConnectionSocketFactoryLookup(connectionManager);
|
||||||
Lookup<ConnectionSocketFactory> socketFactoryRegistry = getConnectionSocketFactoryLookup(
|
|
||||||
connectionManager);
|
|
||||||
assertThat(socketFactoryRegistry.lookup("https")).isNotNull();
|
assertThat(socketFactoryRegistry.lookup("https")).isNotNull();
|
||||||
assertThat(this.getX509TrustManager(socketFactoryRegistry).getAcceptedIssuers())
|
assertThat(this.getX509TrustManager(socketFactoryRegistry).getAcceptedIssuers()).isNull();
|
||||||
.isNull();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Lookup<ConnectionSocketFactory> getConnectionSocketFactoryLookup(
|
private Lookup<ConnectionSocketFactory> getConnectionSocketFactoryLookup(
|
||||||
@@ -84,14 +79,11 @@ public class FeignHttpClientConfigurationTests {
|
|||||||
return (Lookup) this.getField(connectionOperator, "socketFactoryRegistry");
|
return (Lookup) this.getField(connectionOperator, "socketFactoryRegistry");
|
||||||
}
|
}
|
||||||
|
|
||||||
private X509TrustManager getX509TrustManager(
|
private X509TrustManager getX509TrustManager(Lookup<ConnectionSocketFactory> socketFactoryRegistry) {
|
||||||
Lookup<ConnectionSocketFactory> socketFactoryRegistry) {
|
|
||||||
ConnectionSocketFactory connectionSocketFactory = (ConnectionSocketFactory) socketFactoryRegistry
|
ConnectionSocketFactory connectionSocketFactory = (ConnectionSocketFactory) socketFactoryRegistry
|
||||||
.lookup("https");
|
.lookup("https");
|
||||||
SSLSocketFactory sslSocketFactory = (SSLSocketFactory) this
|
SSLSocketFactory sslSocketFactory = (SSLSocketFactory) this.getField(connectionSocketFactory, "socketfactory");
|
||||||
.getField(connectionSocketFactory, "socketfactory");
|
SSLContextSpi sslContext = (SSLContextSpi) this.getField(sslSocketFactory, "context");
|
||||||
SSLContextSpi sslContext = (SSLContextSpi) this.getField(sslSocketFactory,
|
|
||||||
"context");
|
|
||||||
return (X509TrustManager) this.getField(sslContext, "trustManager");
|
return (X509TrustManager) this.getField(sslContext, "trustManager");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+28
-38
@@ -23,10 +23,9 @@ import feign.Client;
|
|||||||
import feign.Feign;
|
import feign.Feign;
|
||||||
import feign.Target;
|
import feign.Target;
|
||||||
import feign.httpclient.ApacheHttpClient;
|
import feign.httpclient.ApacheHttpClient;
|
||||||
import org.junit.AfterClass;
|
import org.junit.jupiter.api.AfterAll;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
@@ -36,7 +35,6 @@ 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.test.annotation.DirtiesContext;
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
import org.springframework.test.context.junit4.SpringRunner;
|
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
import org.springframework.util.SocketUtils;
|
import org.springframework.util.SocketUtils;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
@@ -49,13 +47,11 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
|
|||||||
/**
|
/**
|
||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
*/
|
*/
|
||||||
@RunWith(SpringRunner.class)
|
@SpringBootTest(classes = FeignHttpClientUrlTests.TestConfig.class, webEnvironment = DEFINED_PORT,
|
||||||
@SpringBootTest(classes = FeignHttpClientUrlTests.TestConfig.class,
|
value = { "spring.application.name=feignclienturltest", "feign.hystrix.enabled=false",
|
||||||
webEnvironment = DEFINED_PORT,
|
"feign.okhttp.enabled=false", "spring.cloud.loadbalancer.retry.enabled=false" })
|
||||||
value = { "spring.application.name=feignclienturltest",
|
|
||||||
"feign.hystrix.enabled=false", "feign.okhttp.enabled=false" })
|
|
||||||
@DirtiesContext
|
@DirtiesContext
|
||||||
public class FeignHttpClientUrlTests {
|
class FeignHttpClientUrlTests {
|
||||||
|
|
||||||
static int port;
|
static int port;
|
||||||
|
|
||||||
@@ -68,40 +64,37 @@ public class FeignHttpClientUrlTests {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private BeanUrlClient beanClient;
|
private BeanUrlClient beanClient;
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeAll
|
||||||
public static void beforeClass() {
|
public static void beforeClass() {
|
||||||
port = SocketUtils.findAvailableTcpPort();
|
port = SocketUtils.findAvailableTcpPort();
|
||||||
System.setProperty("server.port", String.valueOf(port));
|
System.setProperty("server.port", String.valueOf(port));
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterAll
|
||||||
public static void afterClass() {
|
public static void afterClass() {
|
||||||
System.clearProperty("server.port");
|
System.clearProperty("server.port");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUrlHttpClient() {
|
void testUrlHttpClient() {
|
||||||
assertThat(this.urlClient).as("UrlClient was null").isNotNull();
|
assertThat(urlClient).as("UrlClient was null").isNotNull();
|
||||||
Hello hello = this.urlClient.getHello();
|
Hello hello = urlClient.getHello();
|
||||||
assertThat(hello).as("hello was null").isNotNull();
|
assertThat(hello).as("hello was null").isNotNull();
|
||||||
assertThat(hello).as("first hello didn't match")
|
assertThat(hello).as("first hello didn't match").isEqualTo(new Hello("hello world 1"));
|
||||||
.isEqualTo(new Hello("hello world 1"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBeanUrl() {
|
void testBeanUrl() {
|
||||||
Hello hello = this.beanClient.getHello();
|
Hello hello = beanClient.getHello();
|
||||||
assertThat(hello).as("hello was null").isNotNull();
|
assertThat(hello).as("hello was null").isNotNull();
|
||||||
assertThat(hello).as("first hello didn't match")
|
assertThat(hello).as("first hello didn't match").isEqualTo(new Hello("hello world 1"));
|
||||||
.isEqualTo(new Hello("hello world 1"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testBeanUrlNoProtocol() {
|
public void testBeanUrlNoProtocol() {
|
||||||
Hello hello = this.beanClientNoProtocol.getHello();
|
Hello hello = beanClientNoProtocol.getHello();
|
||||||
assertThat(hello).as("hello was null").isNotNull();
|
assertThat(hello).as("hello was null").isNotNull();
|
||||||
assertThat(hello).as("first hello didn't match")
|
assertThat(hello).as("first hello didn't match").isEqualTo(new Hello("hello world 1"));
|
||||||
.isEqualTo(new Hello("hello world 1"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this tests that
|
// this tests that
|
||||||
@@ -132,8 +125,7 @@ public class FeignHttpClientUrlTests {
|
|||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
@RestController
|
@RestController
|
||||||
@EnableFeignClients(clients = { UrlClient.class, BeanUrlClient.class,
|
@EnableFeignClients(clients = { UrlClient.class, BeanUrlClient.class, BeanUrlClientNoProtocol.class })
|
||||||
BeanUrlClientNoProtocol.class })
|
|
||||||
@Import(NoSecurityConfiguration.class)
|
@Import(NoSecurityConfiguration.class)
|
||||||
protected static class TestConfig {
|
protected static class TestConfig {
|
||||||
|
|
||||||
@@ -161,15 +153,13 @@ public class FeignHttpClientUrlTests {
|
|||||||
public Targeter feignTargeter() {
|
public Targeter feignTargeter() {
|
||||||
return new Targeter() {
|
return new Targeter() {
|
||||||
@Override
|
@Override
|
||||||
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
|
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
|
||||||
FeignContext context, Target.HardCodedTarget<T> target) {
|
Target.HardCodedTarget<T> target) {
|
||||||
Field field = ReflectionUtils.findField(Feign.Builder.class,
|
Field field = ReflectionUtils.findField(Feign.Builder.class, "client");
|
||||||
"client");
|
|
||||||
ReflectionUtils.makeAccessible(field);
|
ReflectionUtils.makeAccessible(field);
|
||||||
Client client = (Client) ReflectionUtils.getField(field, feign);
|
Client client = (Client) ReflectionUtils.getField(field, feign);
|
||||||
if (target.name().equals("localappurl")) {
|
if (target.name().equals("localappurl")) {
|
||||||
assertThat(client).isInstanceOf(ApacheHttpClient.class)
|
assertThat(client).isInstanceOf(ApacheHttpClient.class).as("client was wrong type");
|
||||||
.as("client was wrong type");
|
|
||||||
}
|
}
|
||||||
return feign.target(target);
|
return feign.target(target);
|
||||||
}
|
}
|
||||||
@@ -182,15 +172,15 @@ public class FeignHttpClientUrlTests {
|
|||||||
|
|
||||||
private String message;
|
private String message;
|
||||||
|
|
||||||
public Hello() {
|
Hello() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Hello(String message) {
|
Hello(String message) {
|
||||||
this.message = message;
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
return this.message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMessage(String message) {
|
public void setMessage(String message) {
|
||||||
@@ -206,12 +196,12 @@ public class FeignHttpClientUrlTests {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Hello that = (Hello) o;
|
Hello that = (Hello) o;
|
||||||
return Objects.equals(this.message, that.message);
|
return Objects.equals(message, that.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(this.message);
|
return Objects.hash(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-6
@@ -34,8 +34,7 @@ public class FeignLoggerFactoryTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultLogger() {
|
public void testDefaultLogger() {
|
||||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SampleConfiguration1.class);
|
||||||
SampleConfiguration1.class);
|
|
||||||
FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class);
|
FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class);
|
||||||
assertThat(loggerFactory).isNotNull();
|
assertThat(loggerFactory).isNotNull();
|
||||||
Logger logger = loggerFactory.create(Object.class);
|
Logger logger = loggerFactory.create(Object.class);
|
||||||
@@ -46,8 +45,7 @@ public class FeignLoggerFactoryTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCustomLogger() {
|
public void testCustomLogger() {
|
||||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SampleConfiguration2.class);
|
||||||
SampleConfiguration2.class);
|
|
||||||
FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class);
|
FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class);
|
||||||
assertThat(loggerFactory).isNotNull();
|
assertThat(loggerFactory).isNotNull();
|
||||||
Logger logger = loggerFactory.create(Object.class);
|
Logger logger = loggerFactory.create(Object.class);
|
||||||
@@ -58,8 +56,7 @@ public class FeignLoggerFactoryTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCustomLoggerFactory() {
|
public void testCustomLoggerFactory() {
|
||||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SampleConfiguration3.class);
|
||||||
SampleConfiguration3.class);
|
|
||||||
FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class);
|
FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class);
|
||||||
assertThat(loggerFactory).isNotNull();
|
assertThat(loggerFactory).isNotNull();
|
||||||
assertThat(loggerFactory instanceof LoggerFactoryImpl).isTrue();
|
assertThat(loggerFactory instanceof LoggerFactoryImpl).isTrue();
|
||||||
|
|||||||
+5
-9
@@ -49,10 +49,9 @@ public class FeignOkHttpConfigurationTests {
|
|||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
this.context = new SpringApplicationBuilder()
|
this.context = new SpringApplicationBuilder()
|
||||||
.properties("debug=true", "feign.httpclient.disableSslValidation=true",
|
.properties("debug=true", "feign.httpclient.disableSslValidation=true", "feign.okhttp.enabled=true",
|
||||||
"feign.okhttp.enabled=true", "feign.httpclient.enabled=false")
|
"feign.httpclient.enabled=false")
|
||||||
.web(WebApplicationType.NONE)
|
.web(WebApplicationType.NONE).sources(HttpClientConfiguration.class, FeignAutoConfiguration.class)
|
||||||
.sources(HttpClientConfiguration.class, FeignAutoConfiguration.class)
|
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,11 +65,8 @@ public class FeignOkHttpConfigurationTests {
|
|||||||
@Test
|
@Test
|
||||||
public void disableSslTest() throws Exception {
|
public void disableSslTest() throws Exception {
|
||||||
OkHttpClient httpClient = this.context.getBean(OkHttpClient.class);
|
OkHttpClient httpClient = this.context.getBean(OkHttpClient.class);
|
||||||
HostnameVerifier hostnameVerifier = (HostnameVerifier) this.getField(httpClient,
|
HostnameVerifier hostnameVerifier = (HostnameVerifier) this.getField(httpClient, "hostnameVerifier");
|
||||||
"hostnameVerifier");
|
assertThat(OkHttpClientFactory.TrustAllHostnames.class.isInstance(hostnameVerifier)).isTrue();
|
||||||
assertThat(
|
|
||||||
OkHttpClientFactory.TrustAllHostnames.class.isInstance(hostnameVerifier))
|
|
||||||
.isTrue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> Object getField(Object target, String name) {
|
protected <T> Object getField(Object target, String name) {
|
||||||
|
|||||||
+14
-24
@@ -25,10 +25,10 @@ import org.junit.Test;
|
|||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||||
|
import org.springframework.boot.web.server.LocalServerPort;
|
||||||
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
|
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
@@ -47,16 +47,15 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
*/
|
*/
|
||||||
@RunWith(SpringJUnit4ClassRunner.class)
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
@SpringBootTest(classes = SpringDecoderTests.Application.class,
|
@SpringBootTest(classes = SpringDecoderTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||||
webEnvironment = WebEnvironment.RANDOM_PORT, value = {
|
value = { "spring.application.name=springdecodertest", "spring.jmx.enabled=false" })
|
||||||
"spring.application.name=springdecodertest", "spring.jmx.enabled=false" })
|
|
||||||
@DirtiesContext
|
@DirtiesContext
|
||||||
public class SpringDecoderTests extends FeignClientFactoryBean {
|
public class SpringDecoderTests extends FeignClientFactoryBean {
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
FeignContext context;
|
FeignContext context;
|
||||||
|
|
||||||
@Value("${local.server.port}")
|
@LocalServerPort
|
||||||
private int port = 0;
|
private int port = 0;
|
||||||
|
|
||||||
public SpringDecoderTests() {
|
public SpringDecoderTests() {
|
||||||
@@ -71,28 +70,24 @@ public class SpringDecoderTests extends FeignClientFactoryBean {
|
|||||||
public TestClient testClient(boolean decode404) {
|
public TestClient testClient(boolean decode404) {
|
||||||
setType(this.getClass());
|
setType(this.getClass());
|
||||||
setDecode404(decode404);
|
setDecode404(decode404);
|
||||||
return feign(this.context).target(TestClient.class,
|
return feign(this.context).target(TestClient.class, "http://localhost:" + this.port);
|
||||||
"http://localhost:" + this.port);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResponseEntity() {
|
public void testResponseEntity() {
|
||||||
ResponseEntity<Hello> response = testClient().getHelloResponse();
|
ResponseEntity<Hello> response = testClient().getHelloResponse();
|
||||||
assertThat(response).as("response was null").isNotNull();
|
assertThat(response).as("response was null").isNotNull();
|
||||||
assertThat(response.getStatusCode()).as("wrong status code")
|
assertThat(response.getStatusCode()).as("wrong status code").isEqualTo(HttpStatus.OK);
|
||||||
.isEqualTo(HttpStatus.OK);
|
|
||||||
Hello hello = response.getBody();
|
Hello hello = response.getBody();
|
||||||
assertThat(hello).as("hello was null").isNotNull();
|
assertThat(hello).as("hello was null").isNotNull();
|
||||||
assertThat(hello).as("first hello didn't match")
|
assertThat(hello).as("first hello didn't match").isEqualTo(new Hello("hello world via response"));
|
||||||
.isEqualTo(new Hello("hello world via response"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleType() {
|
public void testSimpleType() {
|
||||||
Hello hello = testClient().getHello();
|
Hello hello = testClient().getHello();
|
||||||
assertThat(hello).as("hello was null").isNotNull();
|
assertThat(hello).as("hello was null").isNotNull();
|
||||||
assertThat(hello).as("first hello didn't match")
|
assertThat(hello).as("first hello didn't match").isEqualTo(new Hello("hello world 1"));
|
||||||
.isEqualTo(new Hello("hello world 1"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -100,8 +95,7 @@ public class SpringDecoderTests extends FeignClientFactoryBean {
|
|||||||
List<Hello> hellos = testClient().getHellos();
|
List<Hello> hellos = testClient().getHellos();
|
||||||
assertThat(hellos).as("hellos was null").isNotNull();
|
assertThat(hellos).as("hellos was null").isNotNull();
|
||||||
assertThat(hellos.size()).as("hellos was not the right size").isEqualTo(2);
|
assertThat(hellos.size()).as("hellos was not the right size").isEqualTo(2);
|
||||||
assertThat(hellos.get(0)).as("first hello didn't match")
|
assertThat(hellos.get(0)).as("first hello didn't match").isEqualTo(new Hello("hello world 1"));
|
||||||
.isEqualTo(new Hello("hello world 1"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -109,8 +103,7 @@ public class SpringDecoderTests extends FeignClientFactoryBean {
|
|||||||
List<String> hellos = testClient().getHelloStrings();
|
List<String> hellos = testClient().getHelloStrings();
|
||||||
assertThat(hellos).as("hellos was null").isNotNull();
|
assertThat(hellos).as("hellos was null").isNotNull();
|
||||||
assertThat(hellos.size()).as("hellos was not the right size").isEqualTo(2);
|
assertThat(hellos.size()).as("hellos was not the right size").isEqualTo(2);
|
||||||
assertThat(hellos.get(0)).as("first hello didn't match")
|
assertThat(hellos.get(0)).as("first hello didn't match").isEqualTo("hello world 1");
|
||||||
.isEqualTo("hello world 1");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -118,22 +111,19 @@ public class SpringDecoderTests extends FeignClientFactoryBean {
|
|||||||
public void testWildcardTypeDecode() {
|
public void testWildcardTypeDecode() {
|
||||||
ResponseEntity<?> wildcard = testClient().getWildcard();
|
ResponseEntity<?> wildcard = testClient().getWildcard();
|
||||||
assertThat(wildcard).as("wildcard was null").isNotNull();
|
assertThat(wildcard).as("wildcard was null").isNotNull();
|
||||||
assertThat(wildcard.getStatusCode()).as("wrong status code")
|
assertThat(wildcard.getStatusCode()).as("wrong status code").isEqualTo(HttpStatus.OK);
|
||||||
.isEqualTo(HttpStatus.OK);
|
|
||||||
Object wildcardBody = wildcard.getBody();
|
Object wildcardBody = wildcard.getBody();
|
||||||
assertThat(wildcardBody).as("wildcardBody was null").isNotNull();
|
assertThat(wildcardBody).as("wildcardBody was null").isNotNull();
|
||||||
assertThat(wildcardBody instanceof Map).as("wildcard not an instance of Map")
|
assertThat(wildcardBody instanceof Map).as("wildcard not an instance of Map").isTrue();
|
||||||
.isTrue();
|
|
||||||
Map<String, String> hello = (Map<String, String>) wildcardBody;
|
Map<String, String> hello = (Map<String, String>) wildcardBody;
|
||||||
assertThat(hello.get("message")).as("first hello didn't match")
|
assertThat(hello.get("message")).as("first hello didn't match").isEqualTo("wildcard");
|
||||||
.isEqualTo("wildcard");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResponseEntityVoid() {
|
public void testResponseEntityVoid() {
|
||||||
ResponseEntity<Void> response = testClient().getHelloVoid();
|
ResponseEntity<Void> response = testClient().getHelloVoid();
|
||||||
assertThat(response).as("response was null").isNotNull();
|
assertThat(response).as("response was null").isNotNull();
|
||||||
List<String> headerVals = response.getHeaders().get("X-test-header");
|
List<String> headerVals = response.getHeaders().get("x-test-header");
|
||||||
assertThat(headerVals).as("headerVals was null").isNotNull();
|
assertThat(headerVals).as("headerVals was null").isNotNull();
|
||||||
assertThat(headerVals.size()).as("headerVals size was wrong").isEqualTo(1);
|
assertThat(headerVals.size()).as("headerVals size was wrong").isEqualTo(1);
|
||||||
String header = headerVals.get(0);
|
String header = headerVals.get(0);
|
||||||
|
|||||||
-77
@@ -1,77 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2020 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
import org.springframework.boot.WebApplicationType;
|
|
||||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
|
||||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
|
|
||||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
|
||||||
import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration;
|
|
||||||
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
|
|
||||||
import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer;
|
|
||||||
import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration;
|
|
||||||
import org.springframework.cloud.openfeign.ribbon.RetryableFeignLoadBalancer;
|
|
||||||
import org.springframework.cloud.test.ClassPathExclusions;
|
|
||||||
import org.springframework.cloud.test.ModifiedClassPathRunner;
|
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Ryan Baxter
|
|
||||||
*/
|
|
||||||
@RunWith(ModifiedClassPathRunner.class)
|
|
||||||
@ClassPathExclusions({ "spring-retry-*.jar", "spring-boot-starter-aop-*.jar" })
|
|
||||||
public class SpringRetryDisabledTests {
|
|
||||||
|
|
||||||
private ConfigurableApplicationContext context;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setUp() {
|
|
||||||
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
|
|
||||||
.sources(RibbonAutoConfiguration.class,
|
|
||||||
LoadBalancerAutoConfiguration.class,
|
|
||||||
RibbonClientConfiguration.class,
|
|
||||||
FeignRibbonClientAutoConfiguration.class)
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void tearDown() {
|
|
||||||
if (this.context != null) {
|
|
||||||
this.context.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testLoadBalancedRetryFactoryBean() throws Exception {
|
|
||||||
Map<String, CachingSpringLoadBalancerFactory> lbFactorys = this.context
|
|
||||||
.getBeansOfType(CachingSpringLoadBalancerFactory.class);
|
|
||||||
assertThat(lbFactorys.values()).hasSize(1);
|
|
||||||
FeignLoadBalancer lb = lbFactorys.values().iterator().next().create("foo");
|
|
||||||
assertThat(lb).isInstanceOf(FeignLoadBalancer.class);
|
|
||||||
assertThat(lb).isNotInstanceOf(RetryableFeignLoadBalancer.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
-65
@@ -1,65 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013-2020 the original author or authors.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
import org.springframework.beans.BeansException;
|
|
||||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
|
|
||||||
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
|
|
||||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
|
||||||
import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration;
|
|
||||||
import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;
|
|
||||||
import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer;
|
|
||||||
import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration;
|
|
||||||
import org.springframework.cloud.openfeign.ribbon.RetryableFeignLoadBalancer;
|
|
||||||
import org.springframework.context.ApplicationContext;
|
|
||||||
import org.springframework.context.ApplicationContextAware;
|
|
||||||
import org.springframework.test.context.ContextConfiguration;
|
|
||||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Ryan Baxter
|
|
||||||
*/
|
|
||||||
@RunWith(SpringJUnit4ClassRunner.class)
|
|
||||||
@ContextConfiguration(classes = { RibbonAutoConfiguration.class,
|
|
||||||
RibbonClientConfiguration.class, LoadBalancerAutoConfiguration.class,
|
|
||||||
FeignRibbonClientAutoConfiguration.class, HttpClientConfiguration.class })
|
|
||||||
public class SpringRetryEnabledTests implements ApplicationContextAware {
|
|
||||||
|
|
||||||
private ApplicationContext context;
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testLoadBalancedRetryFactoryBean() throws Exception {
|
|
||||||
Map<String, CachingSpringLoadBalancerFactory> lbFactorys = this.context
|
|
||||||
.getBeansOfType(CachingSpringLoadBalancerFactory.class);
|
|
||||||
assertThat(lbFactorys.values()).hasSize(1);
|
|
||||||
FeignLoadBalancer lb = lbFactorys.values().iterator().next().create("foo");
|
|
||||||
assertThat(lb).isInstanceOf(RetryableFeignLoadBalancer.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
+17
-19
@@ -47,15 +47,19 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
|
* @author Spencer Gibb
|
||||||
|
* @author Jakub Narloch
|
||||||
|
* @author Erik Kringen
|
||||||
|
* @author Halvdan Hoem Grelland
|
||||||
|
* @author Aaron Whiteside
|
||||||
*/
|
*/
|
||||||
@RunWith(SpringJUnit4ClassRunner.class)
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
@SpringBootTest(classes = FeignClientTests.Application.class,
|
@SpringBootTest(classes = BeansFeignClientTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT,
|
||||||
webEnvironment = WebEnvironment.RANDOM_PORT,
|
|
||||||
value = { "spring.application.name=feignclienttest",
|
value = { "spring.application.name=feignclienttest",
|
||||||
"logging.level.org.springframework.cloud.openfeign.valid=DEBUG",
|
"logging.level.org.springframework.cloud.openfeign.valid=DEBUG", "feign.httpclient.enabled=false",
|
||||||
"feign.httpclient.enabled=false", "feign.okhttp.enabled=false" })
|
"feign.okhttp.enabled=false" })
|
||||||
@DirtiesContext
|
@DirtiesContext
|
||||||
public class FeignClientTests {
|
public class BeansFeignClientTests {
|
||||||
|
|
||||||
@Value("${local.server.port}")
|
@Value("${local.server.port}")
|
||||||
private int port = 0;
|
private int port = 0;
|
||||||
@@ -76,18 +80,15 @@ public class FeignClientTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAnnotations() {
|
public void testAnnotations() {
|
||||||
Map<String, Object> beans = this.context
|
Map<String, Object> beans = this.context.getBeansWithAnnotation(FeignClient.class);
|
||||||
.getBeansWithAnnotation(FeignClient.class);
|
assertThat(beans.containsKey(TestClient.class.getName())).as("Wrong clients: " + beans).isTrue();
|
||||||
assertThat(beans.containsKey(TestClient.class.getName()))
|
|
||||||
.as("Wrong clients: " + beans).isTrue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testClient() {
|
public void testClient() {
|
||||||
assertThat(this.testClient).as("testClient was null").isNotNull();
|
assertThat(this.testClient).as("testClient was null").isNotNull();
|
||||||
assertThat(this.extraClient).as("extraClient was null").isNotNull();
|
assertThat(this.extraClient).as("extraClient was null").isNotNull();
|
||||||
assertThat(Proxy.isProxyClass(this.testClient.getClass()))
|
assertThat(Proxy.isProxyClass(this.testClient.getClass())).as("testClient is not a java Proxy").isTrue();
|
||||||
.as("testClient is not a java Proxy").isTrue();
|
|
||||||
InvocationHandler invocationHandler = Proxy.getInvocationHandler(this.testClient);
|
InvocationHandler invocationHandler = Proxy.getInvocationHandler(this.testClient);
|
||||||
assertThat(invocationHandler).as("invocationHandler was null").isNotNull();
|
assertThat(invocationHandler).as("invocationHandler was null").isNotNull();
|
||||||
}
|
}
|
||||||
@@ -95,20 +96,17 @@ public class FeignClientTests {
|
|||||||
@Test
|
@Test
|
||||||
public void extraClient() {
|
public void extraClient() {
|
||||||
assertThat(this.extraClient).as("extraClient was null").isNotNull();
|
assertThat(this.extraClient).as("extraClient was null").isNotNull();
|
||||||
assertThat(Proxy.isProxyClass(this.extraClient.getClass()))
|
assertThat(Proxy.isProxyClass(this.extraClient.getClass())).as("extraClient is not a java Proxy").isTrue();
|
||||||
.as("extraClient is not a java Proxy").isTrue();
|
InvocationHandler invocationHandler = Proxy.getInvocationHandler(this.extraClient);
|
||||||
InvocationHandler invocationHandler = Proxy
|
|
||||||
.getInvocationHandler(this.extraClient);
|
|
||||||
assertThat(invocationHandler).as("invocationHandler was null").isNotNull();
|
assertThat(invocationHandler).as("invocationHandler was null").isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void buildByBuilder() {
|
public void buildByBuilder() {
|
||||||
assertThat(this.buildByBuilder).as("buildByBuilder was null").isNotNull();
|
assertThat(this.buildByBuilder).as("buildByBuilder was null").isNotNull();
|
||||||
assertThat(Proxy.isProxyClass(this.buildByBuilder.getClass()))
|
assertThat(Proxy.isProxyClass(this.buildByBuilder.getClass())).as("buildByBuilder is not a java Proxy")
|
||||||
.as("buildByBuilder is not a java Proxy").isTrue();
|
.isTrue();
|
||||||
InvocationHandler invocationHandler = Proxy
|
InvocationHandler invocationHandler = Proxy.getInvocationHandler(this.buildByBuilder);
|
||||||
.getInvocationHandler(this.buildByBuilder);
|
|
||||||
assertThat(invocationHandler).as("invocationHandler was null").isNotNull();
|
assertThat(invocationHandler).as("invocationHandler was null").isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
+1
-1
@@ -17,7 +17,7 @@
|
|||||||
package org.springframework.cloud.openfeign.beans;
|
package org.springframework.cloud.openfeign.beans;
|
||||||
|
|
||||||
import org.springframework.cloud.openfeign.FeignClient;
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
import org.springframework.cloud.openfeign.beans.FeignClientTests.Hello;
|
import org.springframework.cloud.openfeign.beans.BeansFeignClientTests.Hello;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@
|
|||||||
package org.springframework.cloud.openfeign.beans.extra;
|
package org.springframework.cloud.openfeign.beans.extra;
|
||||||
|
|
||||||
import org.springframework.cloud.openfeign.FeignClient;
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
import org.springframework.cloud.openfeign.beans.FeignClientTests.Hello;
|
import org.springframework.cloud.openfeign.beans.BeansFeignClientTests.Hello;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
|
||||||
|
|||||||
+13
-18
@@ -16,20 +16,19 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.openfeign.encoding;
|
package org.springframework.cloud.openfeign.encoding;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
|
||||||
import com.netflix.loadbalancer.ILoadBalancer;
|
|
||||||
import com.netflix.loadbalancer.Server;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
import org.springframework.boot.web.server.LocalServerPort;
|
||||||
|
import org.springframework.cloud.client.DefaultServiceInstance;
|
||||||
|
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
|
||||||
|
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
|
||||||
|
import org.springframework.cloud.loadbalancer.support.ServiceInstanceListSuppliers;
|
||||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||||
import org.springframework.cloud.openfeign.encoding.app.client.InvoiceClient;
|
import org.springframework.cloud.openfeign.encoding.app.client.InvoiceClient;
|
||||||
import org.springframework.cloud.openfeign.encoding.app.domain.Invoice;
|
import org.springframework.cloud.openfeign.encoding.app.domain.Invoice;
|
||||||
@@ -50,8 +49,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
|
|||||||
*
|
*
|
||||||
* @author Jakub Narloch
|
* @author Jakub Narloch
|
||||||
*/
|
*/
|
||||||
@SpringBootTest(classes = FeignAcceptEncodingTests.Application.class,
|
@SpringBootTest(classes = FeignAcceptEncodingTests.Application.class, webEnvironment = RANDOM_PORT,
|
||||||
webEnvironment = RANDOM_PORT,
|
|
||||||
value = { "feign.compression.response.enabled=true" })
|
value = { "feign.compression.response.enabled=true" })
|
||||||
@RunWith(SpringRunner.class)
|
@RunWith(SpringRunner.class)
|
||||||
@DirtiesContext
|
@DirtiesContext
|
||||||
@@ -75,26 +73,23 @@ public class FeignAcceptEncodingTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@EnableFeignClients(clients = InvoiceClient.class)
|
@EnableFeignClients(clients = InvoiceClient.class)
|
||||||
@RibbonClient(name = "local", configuration = LocalRibbonClientConfiguration.class)
|
@LoadBalancerClient(name = "local", configuration = LocalClientConfiguration.class)
|
||||||
@SpringBootApplication(
|
@SpringBootApplication(scanBasePackages = "org.springframework.cloud.openfeign.encoding.app")
|
||||||
scanBasePackages = "org.springframework.cloud.openfeign.encoding.app")
|
|
||||||
@Import(NoSecurityConfiguration.class)
|
@Import(NoSecurityConfiguration.class)
|
||||||
public static class Application {
|
public static class Application {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
static class LocalRibbonClientConfiguration {
|
static class LocalClientConfiguration {
|
||||||
|
|
||||||
@Value("${local.server.port}")
|
@LocalServerPort
|
||||||
private int port = 0;
|
private int port = 0;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ILoadBalancer ribbonLoadBalancer() {
|
public ServiceInstanceListSupplier staticServiceInstanceListSupplier() {
|
||||||
BaseLoadBalancer balancer = new BaseLoadBalancer();
|
return ServiceInstanceListSuppliers.from("local",
|
||||||
balancer.setServersList(
|
new DefaultServiceInstance("local-1", "local", "localhost", port, false));
|
||||||
Collections.singletonList(new Server("localhost", this.port)));
|
|
||||||
return balancer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-20
@@ -16,20 +16,19 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.openfeign.encoding;
|
package org.springframework.cloud.openfeign.encoding;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
|
||||||
import com.netflix.loadbalancer.ILoadBalancer;
|
|
||||||
import com.netflix.loadbalancer.Server;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
import org.springframework.boot.web.server.LocalServerPort;
|
||||||
|
import org.springframework.cloud.client.DefaultServiceInstance;
|
||||||
|
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
|
||||||
|
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
|
||||||
|
import org.springframework.cloud.loadbalancer.support.ServiceInstanceListSuppliers;
|
||||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||||
import org.springframework.cloud.openfeign.encoding.app.client.InvoiceClient;
|
import org.springframework.cloud.openfeign.encoding.app.client.InvoiceClient;
|
||||||
import org.springframework.cloud.openfeign.encoding.app.domain.Invoice;
|
import org.springframework.cloud.openfeign.encoding.app.domain.Invoice;
|
||||||
@@ -49,8 +48,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
|
|||||||
*
|
*
|
||||||
* @author Jakub Narloch
|
* @author Jakub Narloch
|
||||||
*/
|
*/
|
||||||
@SpringBootTest(classes = FeignContentEncodingTests.Application.class,
|
@SpringBootTest(classes = FeignContentEncodingTests.Application.class, webEnvironment = RANDOM_PORT,
|
||||||
webEnvironment = RANDOM_PORT,
|
|
||||||
value = { "feign.compression.request.enabled=true",
|
value = { "feign.compression.request.enabled=true",
|
||||||
"hystrix.command.default.execution.isolation.strategy=SEMAPHORE",
|
"hystrix.command.default.execution.isolation.strategy=SEMAPHORE",
|
||||||
"ribbon.OkToRetryOnAllOperations=false" })
|
"ribbon.OkToRetryOnAllOperations=false" })
|
||||||
@@ -67,8 +65,7 @@ public class FeignContentEncodingTests {
|
|||||||
final List<Invoice> invoices = Invoices.createInvoiceList(50);
|
final List<Invoice> invoices = Invoices.createInvoiceList(50);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final ResponseEntity<List<Invoice>> response = this.invoiceClient
|
final ResponseEntity<List<Invoice>> response = this.invoiceClient.saveInvoices(invoices);
|
||||||
.saveInvoices(invoices);
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(response).isNotNull();
|
assertThat(response).isNotNull();
|
||||||
@@ -79,26 +76,23 @@ public class FeignContentEncodingTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@EnableFeignClients(clients = InvoiceClient.class)
|
@EnableFeignClients(clients = InvoiceClient.class)
|
||||||
@RibbonClient(name = "local", configuration = LocalRibbonClientConfiguration.class)
|
@LoadBalancerClient(name = "local", configuration = LocalClientConfiguration.class)
|
||||||
@SpringBootApplication(
|
@SpringBootApplication(scanBasePackages = "org.springframework.cloud.openfeign.encoding.app")
|
||||||
scanBasePackages = "org.springframework.cloud.openfeign.encoding.app")
|
|
||||||
@Import(NoSecurityConfiguration.class)
|
@Import(NoSecurityConfiguration.class)
|
||||||
public static class Application {
|
public static class Application {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
static class LocalRibbonClientConfiguration {
|
static class LocalClientConfiguration {
|
||||||
|
|
||||||
@Value("${local.server.port}")
|
@LocalServerPort
|
||||||
private int port = 0;
|
private int port = 0;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ILoadBalancer ribbonLoadBalancer() {
|
public ServiceInstanceListSupplier staticServiceInstanceListSupplier() {
|
||||||
BaseLoadBalancer balancer = new BaseLoadBalancer();
|
return ServiceInstanceListSuppliers.from("local",
|
||||||
balancer.setServersList(
|
new DefaultServiceInstance("local-1", "local", "localhost", port, false));
|
||||||
Collections.singletonList(new Server("localhost", this.port)));
|
|
||||||
return balancer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-22
@@ -16,21 +16,20 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.openfeign.encoding;
|
package org.springframework.cloud.openfeign.encoding;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import com.netflix.loadbalancer.BaseLoadBalancer;
|
|
||||||
import com.netflix.loadbalancer.ILoadBalancer;
|
|
||||||
import com.netflix.loadbalancer.Server;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
|
import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.cloud.netflix.ribbon.RibbonClient;
|
import org.springframework.boot.web.server.LocalServerPort;
|
||||||
|
import org.springframework.cloud.client.DefaultServiceInstance;
|
||||||
|
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
|
||||||
|
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
|
||||||
|
import org.springframework.cloud.loadbalancer.support.ServiceInstanceListSuppliers;
|
||||||
import org.springframework.cloud.openfeign.EnableFeignClients;
|
import org.springframework.cloud.openfeign.EnableFeignClients;
|
||||||
import org.springframework.cloud.openfeign.FeignClientsConfiguration;
|
import org.springframework.cloud.openfeign.FeignClientsConfiguration;
|
||||||
import org.springframework.cloud.openfeign.encoding.app.client.InvoiceClient;
|
import org.springframework.cloud.openfeign.encoding.app.client.InvoiceClient;
|
||||||
@@ -56,8 +55,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
|
|||||||
*
|
*
|
||||||
* @author Charlie Mordant.
|
* @author Charlie Mordant.
|
||||||
*/
|
*/
|
||||||
@SpringBootTest(classes = FeignPageableEncodingTests.Application.class,
|
@SpringBootTest(classes = FeignPageableEncodingTests.Application.class, webEnvironment = RANDOM_PORT,
|
||||||
webEnvironment = RANDOM_PORT,
|
|
||||||
value = { "feign.compression.request.enabled=true",
|
value = { "feign.compression.request.enabled=true",
|
||||||
"hystrix.command.default.execution.isolation.strategy=SEMAPHORE",
|
"hystrix.command.default.execution.isolation.strategy=SEMAPHORE",
|
||||||
"ribbon.OkToRetryOnAllOperations=false" })
|
"ribbon.OkToRetryOnAllOperations=false" })
|
||||||
@@ -74,8 +72,7 @@ public class FeignPageableEncodingTests {
|
|||||||
Pageable pageable = PageRequest.of(0, 10, Sort.Direction.ASC, "sortProperty");
|
Pageable pageable = PageRequest.of(0, 10, Sort.Direction.ASC, "sortProperty");
|
||||||
|
|
||||||
// when
|
// when
|
||||||
final ResponseEntity<Page<Invoice>> response = this.invoiceClient
|
final ResponseEntity<Page<Invoice>> response = this.invoiceClient.getInvoicesPaged(pageable);
|
||||||
.getInvoicesPaged(pageable);
|
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assertThat(response).isNotNull();
|
assertThat(response).isNotNull();
|
||||||
@@ -83,8 +80,7 @@ public class FeignPageableEncodingTests {
|
|||||||
assertThat(response.getBody()).isNotNull();
|
assertThat(response.getBody()).isNotNull();
|
||||||
assertThat(pageable.getPageSize()).isEqualTo(response.getBody().getSize());
|
assertThat(pageable.getPageSize()).isEqualTo(response.getBody().getSize());
|
||||||
assertThat(response.getBody().getPageable().getSort()).hasSize(1);
|
assertThat(response.getBody().getPageable().getSort()).hasSize(1);
|
||||||
Optional<Sort.Order> optionalOrder = response.getBody().getPageable().getSort()
|
Optional<Sort.Order> optionalOrder = response.getBody().getPageable().getSort().get().findFirst();
|
||||||
.get().findFirst();
|
|
||||||
if (optionalOrder.isPresent()) {
|
if (optionalOrder.isPresent()) {
|
||||||
Sort.Order order = optionalOrder.get();
|
Sort.Order order = optionalOrder.get();
|
||||||
assertThat(order.getDirection()).isEqualTo(Sort.Direction.ASC);
|
assertThat(order.getDirection()).isEqualTo(Sort.Direction.ASC);
|
||||||
@@ -94,9 +90,8 @@ public class FeignPageableEncodingTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@EnableFeignClients(clients = InvoiceClient.class)
|
@EnableFeignClients(clients = InvoiceClient.class)
|
||||||
@RibbonClient(name = "local", configuration = LocalRibbonClientConfiguration.class)
|
@LoadBalancerClient(name = "local", configuration = LocalClientConfiguration.class)
|
||||||
@SpringBootApplication(
|
@SpringBootApplication(scanBasePackages = "org.springframework.cloud.openfeign.encoding.app",
|
||||||
scanBasePackages = "org.springframework.cloud.openfeign.encoding.app",
|
|
||||||
exclude = { RepositoryRestMvcAutoConfiguration.class })
|
exclude = { RepositoryRestMvcAutoConfiguration.class })
|
||||||
@EnableSpringDataWebSupport
|
@EnableSpringDataWebSupport
|
||||||
@Import({ NoSecurityConfiguration.class, FeignClientsConfiguration.class })
|
@Import({ NoSecurityConfiguration.class, FeignClientsConfiguration.class })
|
||||||
@@ -105,17 +100,15 @@ public class FeignPageableEncodingTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
static class LocalRibbonClientConfiguration {
|
static class LocalClientConfiguration {
|
||||||
|
|
||||||
@Value("${local.server.port}")
|
@LocalServerPort
|
||||||
private int port = 0;
|
private int port = 0;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public ILoadBalancer ribbonLoadBalancer() {
|
public ServiceInstanceListSupplier staticServiceInstanceListSupplier() {
|
||||||
BaseLoadBalancer balancer = new BaseLoadBalancer();
|
return ServiceInstanceListSuppliers.from("local",
|
||||||
balancer.setServersList(
|
new DefaultServiceInstance("local-1", "local", "localhost", port, false));
|
||||||
Collections.singletonList(new Server("localhost", this.port)));
|
|
||||||
return balancer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -35,8 +35,7 @@ final class Invoices {
|
|||||||
for (int ind = 0; ind < count; ind++) {
|
for (int ind = 0; ind < count; ind++) {
|
||||||
final Invoice invoice = new Invoice();
|
final Invoice invoice = new Invoice();
|
||||||
invoice.setTitle("Invoice " + (ind + 1));
|
invoice.setTitle("Invoice " + (ind + 1));
|
||||||
invoice.setAmount(new BigDecimal(
|
invoice.setAmount(new BigDecimal(String.format(Locale.US, "%.2f", Math.random() * 1000)));
|
||||||
String.format(Locale.US, "%.2f", Math.random() * 1000)));
|
|
||||||
invoices.add(invoice);
|
invoices.add(invoice);
|
||||||
}
|
}
|
||||||
return invoices;
|
return invoices;
|
||||||
|
|||||||
+4
-8
@@ -34,17 +34,13 @@ import org.springframework.web.bind.annotation.RequestMethod;
|
|||||||
@FeignClient("local")
|
@FeignClient("local")
|
||||||
public interface InvoiceClient {
|
public interface InvoiceClient {
|
||||||
|
|
||||||
@RequestMapping(value = "invoicesPaged", method = RequestMethod.GET,
|
@RequestMapping(value = "invoicesPaged", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
ResponseEntity<Page<Invoice>> getInvoicesPaged(org.springframework.data.domain.Pageable pageable);
|
||||||
ResponseEntity<Page<Invoice>> getInvoicesPaged(
|
|
||||||
org.springframework.data.domain.Pageable pageable);
|
|
||||||
|
|
||||||
@RequestMapping(value = "invoices", method = RequestMethod.GET,
|
@RequestMapping(value = "invoices", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
|
||||||
ResponseEntity<List<Invoice>> getInvoices();
|
ResponseEntity<List<Invoice>> getInvoices();
|
||||||
|
|
||||||
@RequestMapping(value = "invoices", method = RequestMethod.POST,
|
@RequestMapping(value = "invoices", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE,
|
||||||
consumes = MediaType.APPLICATION_JSON_VALUE,
|
|
||||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
ResponseEntity<List<Invoice>> saveInvoices(List<Invoice> invoices);
|
ResponseEntity<List<Invoice>> saveInvoices(List<Invoice> invoices);
|
||||||
|
|
||||||
|
|||||||
+6
-12
@@ -39,27 +39,22 @@ import org.springframework.web.bind.annotation.RestController;
|
|||||||
@RestController
|
@RestController
|
||||||
public class InvoiceResource {
|
public class InvoiceResource {
|
||||||
|
|
||||||
@RequestMapping(value = "invoices", method = RequestMethod.GET,
|
@RequestMapping(value = "invoices", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
|
||||||
public ResponseEntity<List<Invoice>> getInvoices() {
|
public ResponseEntity<List<Invoice>> getInvoices() {
|
||||||
|
|
||||||
return ResponseEntity.ok(createInvoiceList(100));
|
return ResponseEntity.ok(createInvoiceList(100));
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "invoices", method = RequestMethod.POST,
|
@RequestMapping(value = "invoices", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE,
|
||||||
consumes = MediaType.APPLICATION_JSON_VALUE,
|
|
||||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
ResponseEntity<List<Invoice>> saveInvoices(@RequestBody List<Invoice> invoices) {
|
ResponseEntity<List<Invoice>> saveInvoices(@RequestBody List<Invoice> invoices) {
|
||||||
|
|
||||||
return ResponseEntity.ok(invoices);
|
return ResponseEntity.ok(invoices);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(value = "invoicesPaged", method = RequestMethod.GET,
|
@RequestMapping(value = "invoicesPaged", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||||
produces = MediaType.APPLICATION_JSON_VALUE)
|
public ResponseEntity<Page<Invoice>> getInvoicesPaged(org.springframework.data.domain.Pageable pageable) {
|
||||||
public ResponseEntity<Page<Invoice>> getInvoicesPaged(
|
Page<Invoice> page = new PageImpl<>(createInvoiceList(pageable.getPageSize()), pageable, 100);
|
||||||
org.springframework.data.domain.Pageable pageable) {
|
|
||||||
Page<Invoice> page = new PageImpl<>(createInvoiceList(pageable.getPageSize()),
|
|
||||||
pageable, 100);
|
|
||||||
return ResponseEntity.ok(page);
|
return ResponseEntity.ok(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,8 +63,7 @@ public class InvoiceResource {
|
|||||||
for (int ind = 0; ind < count; ind++) {
|
for (int ind = 0; ind < count; ind++) {
|
||||||
final Invoice invoice = new Invoice();
|
final Invoice invoice = new Invoice();
|
||||||
invoice.setTitle("Invoice " + (ind + 1));
|
invoice.setTitle("Invoice " + (ind + 1));
|
||||||
invoice.setAmount(new BigDecimal(
|
invoice.setAmount(new BigDecimal(String.format(Locale.US, "%.2f", Math.random() * 1000)));
|
||||||
String.format(Locale.US, "%.2f", Math.random() * 1000)));
|
|
||||||
invoices.add(invoice);
|
invoices.add(invoice);
|
||||||
}
|
}
|
||||||
return invoices;
|
return invoices;
|
||||||
|
|||||||
+7
-13
@@ -68,9 +68,8 @@ public class ProtobufSpringEncoderTest {
|
|||||||
|
|
||||||
// a protobuf object with some content
|
// a protobuf object with some content
|
||||||
private org.springframework.cloud.openfeign.encoding.proto.Request request = org.springframework.cloud.openfeign.encoding.proto.Request
|
private org.springframework.cloud.openfeign.encoding.proto.Request request = org.springframework.cloud.openfeign.encoding.proto.Request
|
||||||
.newBuilder().setId(1000000).setMsg("Erlang/OTP 最初是爱立信为开发电信设备系统设计的编程语言平台,"
|
.newBuilder().setId(1000000)
|
||||||
+ "电信设备(路由器、接入网关、…)典型设计是通过背板连接主控板卡与多块业务板卡的分布式系统。")
|
.setMsg("Erlang/OTP 最初是爱立信为开发电信设备系统设计的编程语言平台," + "电信设备(路由器、接入网关、…)典型设计是通过背板连接主控板卡与多块业务板卡的分布式系统。").build();
|
||||||
.build();
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testProtobuf() throws IOException, URISyntaxException {
|
public void testProtobuf() throws IOException, URISyntaxException {
|
||||||
@@ -92,8 +91,7 @@ public class ProtobufSpringEncoderTest {
|
|||||||
RequestTemplate requestTemplate = newRequestTemplate();
|
RequestTemplate requestTemplate = newRequestTemplate();
|
||||||
newEncoder().encode(this.request, Request.class, requestTemplate);
|
newEncoder().encode(this.request, Request.class, requestTemplate);
|
||||||
// set a charset
|
// set a charset
|
||||||
requestTemplate.body(
|
requestTemplate.body(encoded(requestTemplate.requestBody().asBytes(), StandardCharsets.UTF_8));
|
||||||
encoded(requestTemplate.requestBody().asBytes(), StandardCharsets.UTF_8));
|
|
||||||
HttpEntity entity = toApacheHttpEntity(requestTemplate);
|
HttpEntity entity = toApacheHttpEntity(requestTemplate);
|
||||||
byte[] bytes = read(entity.getContent(), (int) entity.getContentLength());
|
byte[] bytes = read(entity.getContent(), (int) entity.getContentLength());
|
||||||
|
|
||||||
@@ -125,21 +123,17 @@ public class ProtobufSpringEncoderTest {
|
|||||||
return requestTemplate;
|
return requestTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
private HttpEntity toApacheHttpEntity(RequestTemplate requestTemplate)
|
private HttpEntity toApacheHttpEntity(RequestTemplate requestTemplate) throws IOException, URISyntaxException {
|
||||||
throws IOException, URISyntaxException {
|
|
||||||
final List<HttpUriRequest> request = new ArrayList<>(1);
|
final List<HttpUriRequest> request = new ArrayList<>(1);
|
||||||
BDDMockito.given(this.httpClient.execute(ArgumentMatchers.<HttpUriRequest>any()))
|
BDDMockito.given(this.httpClient.execute(ArgumentMatchers.<HttpUriRequest>any()))
|
||||||
.will(new Answer<HttpResponse>() {
|
.will(new Answer<HttpResponse>() {
|
||||||
@Override
|
@Override
|
||||||
public HttpResponse answer(InvocationOnMock invocationOnMock)
|
public HttpResponse answer(InvocationOnMock invocationOnMock) throws Throwable {
|
||||||
throws Throwable {
|
|
||||||
request.add((HttpUriRequest) invocationOnMock.getArguments()[0]);
|
request.add((HttpUriRequest) invocationOnMock.getArguments()[0]);
|
||||||
return new BasicHttpResponse(new BasicStatusLine(
|
return new BasicHttpResponse(new BasicStatusLine(new ProtocolVersion("http", 1, 1), 200, null));
|
||||||
new ProtocolVersion("http", 1, 1), 200, null));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
new ApacheHttpClient(this.httpClient).execute(
|
new ApacheHttpClient(this.httpClient).execute(requestTemplate.resolve(new HashMap<>()).request(),
|
||||||
requestTemplate.resolve(new HashMap<>()).request(),
|
|
||||||
new feign.Request.Options());
|
new feign.Request.Options());
|
||||||
HttpUriRequest httpUriRequest = request.get(0);
|
HttpUriRequest httpUriRequest = request.get(0);
|
||||||
return ((HttpEntityEnclosingRequestBase) httpUriRequest).getEntity();
|
return ((HttpEntityEnclosingRequestBase) httpUriRequest).getEntity();
|
||||||
|
|||||||
+6
-11
@@ -27,10 +27,8 @@ public final class ProtobufTest {
|
|||||||
private static com.google.protobuf.Descriptors.FileDescriptor descriptor;
|
private static com.google.protobuf.Descriptors.FileDescriptor descriptor;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
String[] descriptorData = {
|
String[] descriptorData = { "\n\023protobuf_test.proto\"\"\n\007Request\022\n\n\002id\030\001"
|
||||||
"\n\023protobuf_test.proto\"\"\n\007Request\022\n\n\002id\030\001"
|
+ " \001(\005\022\013\n\003msg\030\002 \001(\tB\024\n\020feign.httpclientP\001b" + "\006proto3" };
|
||||||
+ " \001(\005\022\013\n\003msg\030\002 \001(\tB\024\n\020feign.httpclientP\001b"
|
|
||||||
+ "\006proto3" };
|
|
||||||
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
|
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
|
||||||
public com.google.protobuf.ExtensionRegistry assignDescriptors(
|
public com.google.protobuf.ExtensionRegistry assignDescriptors(
|
||||||
com.google.protobuf.Descriptors.FileDescriptor root) {
|
com.google.protobuf.Descriptors.FileDescriptor root) {
|
||||||
@@ -38,9 +36,8 @@ public final class ProtobufTest {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom(
|
com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom(descriptorData,
|
||||||
descriptorData, new com.google.protobuf.Descriptors.FileDescriptor[] {},
|
new com.google.protobuf.Descriptors.FileDescriptor[] {}, assigner);
|
||||||
assigner);
|
|
||||||
internal_static_Request_descriptor = getDescriptor().getMessageTypes().get(0);
|
internal_static_Request_descriptor = getDescriptor().getMessageTypes().get(0);
|
||||||
internal_static_Request_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
|
internal_static_Request_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable(
|
||||||
internal_static_Request_descriptor, new String[] { "Id", "Msg", });
|
internal_static_Request_descriptor, new String[] { "Id", "Msg", });
|
||||||
@@ -49,12 +46,10 @@ public final class ProtobufTest {
|
|||||||
private ProtobufTest() {
|
private ProtobufTest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void registerAllExtensions(
|
public static void registerAllExtensions(com.google.protobuf.ExtensionRegistryLite registry) {
|
||||||
com.google.protobuf.ExtensionRegistryLite registry) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void registerAllExtensions(
|
public static void registerAllExtensions(com.google.protobuf.ExtensionRegistry registry) {
|
||||||
com.google.protobuf.ExtensionRegistry registry) {
|
|
||||||
registerAllExtensions((com.google.protobuf.ExtensionRegistryLite) registry);
|
registerAllExtensions((com.google.protobuf.ExtensionRegistryLite) registry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+27
-54
@@ -68,8 +68,7 @@ public final class Request extends com.google.protobuf.GeneratedMessageV3 implem
|
|||||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||||
this();
|
this();
|
||||||
int mutable_bitField0_ = 0;
|
int mutable_bitField0_ = 0;
|
||||||
com.google.protobuf.UnknownFieldSet.Builder unknownFields = com.google.protobuf.UnknownFieldSet
|
com.google.protobuf.UnknownFieldSet.Builder unknownFields = com.google.protobuf.UnknownFieldSet.newBuilder();
|
||||||
.newBuilder();
|
|
||||||
try {
|
try {
|
||||||
boolean done = false;
|
boolean done = false;
|
||||||
while (!done) {
|
while (!done) {
|
||||||
@@ -79,8 +78,7 @@ public final class Request extends com.google.protobuf.GeneratedMessageV3 implem
|
|||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (!parseUnknownFieldProto3(input, unknownFields, extensionRegistry,
|
if (!parseUnknownFieldProto3(input, unknownFields, extensionRegistry, tag)) {
|
||||||
tag)) {
|
|
||||||
done = true;
|
done = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -98,8 +96,7 @@ public final class Request extends com.google.protobuf.GeneratedMessageV3 implem
|
|||||||
throw e.setUnfinishedMessage(this);
|
throw e.setUnfinishedMessage(this);
|
||||||
}
|
}
|
||||||
catch (java.io.IOException e) {
|
catch (java.io.IOException e) {
|
||||||
throw new com.google.protobuf.InvalidProtocolBufferException(e)
|
throw new com.google.protobuf.InvalidProtocolBufferException(e).setUnfinishedMessage(this);
|
||||||
.setUnfinishedMessage(this);
|
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
this.unknownFields = unknownFields.build();
|
this.unknownFields = unknownFields.build();
|
||||||
@@ -133,52 +130,40 @@ public final class Request extends com.google.protobuf.GeneratedMessageV3 implem
|
|||||||
return PARSER.parseFrom(data, extensionRegistry);
|
return PARSER.parseFrom(data, extensionRegistry);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Request parseFrom(byte[] data)
|
public static Request parseFrom(byte[] data) throws com.google.protobuf.InvalidProtocolBufferException {
|
||||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
|
||||||
return PARSER.parseFrom(data);
|
return PARSER.parseFrom(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Request parseFrom(byte[] data,
|
public static Request parseFrom(byte[] data, com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
|
||||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||||
return PARSER.parseFrom(data, extensionRegistry);
|
return PARSER.parseFrom(data, extensionRegistry);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Request parseFrom(java.io.InputStream input)
|
public static Request parseFrom(java.io.InputStream input) throws java.io.IOException {
|
||||||
throws java.io.IOException {
|
|
||||||
return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input);
|
return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Request parseFrom(java.io.InputStream input,
|
public static Request parseFrom(java.io.InputStream input,
|
||||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException {
|
||||||
throws java.io.IOException {
|
return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry);
|
||||||
return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input,
|
|
||||||
extensionRegistry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Request parseDelimitedFrom(java.io.InputStream input)
|
public static Request parseDelimitedFrom(java.io.InputStream input) throws java.io.IOException {
|
||||||
throws java.io.IOException {
|
return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input);
|
||||||
return com.google.protobuf.GeneratedMessageV3
|
|
||||||
.parseDelimitedWithIOException(PARSER, input);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Request parseDelimitedFrom(java.io.InputStream input,
|
public static Request parseDelimitedFrom(java.io.InputStream input,
|
||||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException {
|
||||||
throws java.io.IOException {
|
return com.google.protobuf.GeneratedMessageV3.parseDelimitedWithIOException(PARSER, input, extensionRegistry);
|
||||||
return com.google.protobuf.GeneratedMessageV3
|
|
||||||
.parseDelimitedWithIOException(PARSER, input, extensionRegistry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Request parseFrom(com.google.protobuf.CodedInputStream input)
|
public static Request parseFrom(com.google.protobuf.CodedInputStream input) throws java.io.IOException {
|
||||||
throws java.io.IOException {
|
|
||||||
return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input);
|
return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Request parseFrom(com.google.protobuf.CodedInputStream input,
|
public static Request parseFrom(com.google.protobuf.CodedInputStream input,
|
||||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException {
|
||||||
throws java.io.IOException {
|
return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, extensionRegistry);
|
||||||
return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input,
|
|
||||||
extensionRegistry);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Builder newBuilder() {
|
public static Builder newBuilder() {
|
||||||
@@ -236,8 +221,7 @@ public final class Request extends com.google.protobuf.GeneratedMessageV3 implem
|
|||||||
public com.google.protobuf.ByteString getMsgBytes() {
|
public com.google.protobuf.ByteString getMsgBytes() {
|
||||||
Object ref = this.msg_;
|
Object ref = this.msg_;
|
||||||
if (ref instanceof String) {
|
if (ref instanceof String) {
|
||||||
com.google.protobuf.ByteString b = com.google.protobuf.ByteString
|
com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref);
|
||||||
.copyFromUtf8((String) ref);
|
|
||||||
this.msg_ = b;
|
this.msg_ = b;
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
@@ -258,8 +242,7 @@ public final class Request extends com.google.protobuf.GeneratedMessageV3 implem
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeTo(com.google.protobuf.CodedOutputStream output)
|
public void writeTo(com.google.protobuf.CodedOutputStream output) throws java.io.IOException {
|
||||||
throws java.io.IOException {
|
|
||||||
if (this.id_ != 0) {
|
if (this.id_ != 0) {
|
||||||
output.writeInt32(1, this.id_);
|
output.writeInt32(1, this.id_);
|
||||||
}
|
}
|
||||||
@@ -280,8 +263,7 @@ public final class Request extends com.google.protobuf.GeneratedMessageV3 implem
|
|||||||
size += com.google.protobuf.CodedOutputStream.computeInt32Size(1, this.id_);
|
size += com.google.protobuf.CodedOutputStream.computeInt32Size(1, this.id_);
|
||||||
}
|
}
|
||||||
if (!getMsgBytes().isEmpty()) {
|
if (!getMsgBytes().isEmpty()) {
|
||||||
size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2,
|
size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, this.msg_);
|
||||||
this.msg_);
|
|
||||||
}
|
}
|
||||||
size += this.unknownFields.getSerializedSize();
|
size += this.unknownFields.getSerializedSize();
|
||||||
this.memoizedSize = size;
|
this.memoizedSize = size;
|
||||||
@@ -347,8 +329,7 @@ public final class Request extends com.google.protobuf.GeneratedMessageV3 implem
|
|||||||
/**
|
/**
|
||||||
* Protobuf type {@code Request}
|
* Protobuf type {@code Request}
|
||||||
*/
|
*/
|
||||||
public static final class Builder
|
public static final class Builder extends com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
|
||||||
extends com.google.protobuf.GeneratedMessageV3.Builder<Builder> implements
|
|
||||||
// @@protoc_insertion_point(builder_implements:Request)
|
// @@protoc_insertion_point(builder_implements:Request)
|
||||||
org.springframework.cloud.openfeign.encoding.proto.RequestOrBuilder {
|
org.springframework.cloud.openfeign.encoding.proto.RequestOrBuilder {
|
||||||
|
|
||||||
@@ -373,8 +354,7 @@ public final class Request extends com.google.protobuf.GeneratedMessageV3 implem
|
|||||||
|
|
||||||
protected FieldAccessorTable internalGetFieldAccessorTable() {
|
protected FieldAccessorTable internalGetFieldAccessorTable() {
|
||||||
return org.springframework.cloud.openfeign.encoding.proto.ProtobufTest.internal_static_Request_fieldAccessorTable
|
return org.springframework.cloud.openfeign.encoding.proto.ProtobufTest.internal_static_Request_fieldAccessorTable
|
||||||
.ensureFieldAccessorsInitialized(Request.class,
|
.ensureFieldAccessorsInitialized(Request.class, Request.Builder.class);
|
||||||
Request.Builder.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void maybeForceBuilderInitialization() {
|
private void maybeForceBuilderInitialization() {
|
||||||
@@ -418,8 +398,7 @@ public final class Request extends com.google.protobuf.GeneratedMessageV3 implem
|
|||||||
return (Builder) super.clone();
|
return (Builder) super.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setField(com.google.protobuf.Descriptors.FieldDescriptor field,
|
public Builder setField(com.google.protobuf.Descriptors.FieldDescriptor field, Object value) {
|
||||||
Object value) {
|
|
||||||
return (Builder) super.setField(field, value);
|
return (Builder) super.setField(field, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,14 +410,12 @@ public final class Request extends com.google.protobuf.GeneratedMessageV3 implem
|
|||||||
return (Builder) super.clearOneof(oneof);
|
return (Builder) super.clearOneof(oneof);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setRepeatedField(
|
public Builder setRepeatedField(com.google.protobuf.Descriptors.FieldDescriptor field, int index,
|
||||||
com.google.protobuf.Descriptors.FieldDescriptor field, int index,
|
|
||||||
Object value) {
|
Object value) {
|
||||||
return (Builder) super.setRepeatedField(field, index, value);
|
return (Builder) super.setRepeatedField(field, index, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder addRepeatedField(
|
public Builder addRepeatedField(com.google.protobuf.Descriptors.FieldDescriptor field, Object value) {
|
||||||
com.google.protobuf.Descriptors.FieldDescriptor field, Object value) {
|
|
||||||
return (Builder) super.addRepeatedField(field, value);
|
return (Builder) super.addRepeatedField(field, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,8 +450,7 @@ public final class Request extends com.google.protobuf.GeneratedMessageV3 implem
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Builder mergeFrom(com.google.protobuf.CodedInputStream input,
|
public Builder mergeFrom(com.google.protobuf.CodedInputStream input,
|
||||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
com.google.protobuf.ExtensionRegistryLite extensionRegistry) throws java.io.IOException {
|
||||||
throws java.io.IOException {
|
|
||||||
Request parsedMessage = null;
|
Request parsedMessage = null;
|
||||||
try {
|
try {
|
||||||
parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
|
parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry);
|
||||||
@@ -553,8 +529,7 @@ public final class Request extends com.google.protobuf.GeneratedMessageV3 implem
|
|||||||
public com.google.protobuf.ByteString getMsgBytes() {
|
public com.google.protobuf.ByteString getMsgBytes() {
|
||||||
Object ref = this.msg_;
|
Object ref = this.msg_;
|
||||||
if (ref instanceof String) {
|
if (ref instanceof String) {
|
||||||
com.google.protobuf.ByteString b = com.google.protobuf.ByteString
|
com.google.protobuf.ByteString b = com.google.protobuf.ByteString.copyFromUtf8((String) ref);
|
||||||
.copyFromUtf8((String) ref);
|
|
||||||
this.msg_ = b;
|
this.msg_ = b;
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
@@ -587,13 +562,11 @@ public final class Request extends com.google.protobuf.GeneratedMessageV3 implem
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder setUnknownFields(
|
public Builder setUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) {
|
||||||
final com.google.protobuf.UnknownFieldSet unknownFields) {
|
|
||||||
return super.setUnknownFieldsProto3(unknownFields);
|
return super.setUnknownFieldsProto3(unknownFields);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder mergeUnknownFields(
|
public Builder mergeUnknownFields(final com.google.protobuf.UnknownFieldSet unknownFields) {
|
||||||
final com.google.protobuf.UnknownFieldSet unknownFields) {
|
|
||||||
return super.mergeUnknownFields(unknownFields);
|
return super.mergeUnknownFields(unknownFields);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+9
-14
@@ -42,34 +42,29 @@ public class FeignHalAutoConfigurationContextTests {
|
|||||||
public void setUp() {
|
public void setUp() {
|
||||||
contextRunner = new WebApplicationContextRunner()
|
contextRunner = new WebApplicationContextRunner()
|
||||||
.withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class,
|
.withConfiguration(AutoConfigurations.of(JacksonAutoConfiguration.class,
|
||||||
HttpMessageConvertersAutoConfiguration.class,
|
HttpMessageConvertersAutoConfiguration.class, HypermediaAutoConfiguration.class,
|
||||||
HypermediaAutoConfiguration.class,
|
RepositoryRestMvcAutoConfiguration.class, FeignHalAutoConfiguration.class))
|
||||||
RepositoryRestMvcAutoConfiguration.class,
|
|
||||||
FeignHalAutoConfiguration.class))
|
|
||||||
.withPropertyValues("debug=true");
|
.withPropertyValues("debug=true");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHalJacksonHttpMessageConverterIsNotLoaded() {
|
public void testHalJacksonHttpMessageConverterIsNotLoaded() {
|
||||||
FilteredClassLoader filteredClassLoader = new FilteredClassLoader(
|
FilteredClassLoader filteredClassLoader = new FilteredClassLoader(RepositoryRestMvcConfiguration.class,
|
||||||
RepositoryRestMvcConfiguration.class, RepresentationModel.class);
|
RepresentationModel.class);
|
||||||
contextRunner.withClassLoader(filteredClassLoader)
|
contextRunner.withClassLoader(filteredClassLoader)
|
||||||
.run(context -> assertThat(context)
|
.run(context -> assertThat(context).doesNotHaveBean("halJacksonHttpMessageConverter"));
|
||||||
.doesNotHaveBean("halJacksonHttpMessageConverter"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHalJacksonHttpMessageConverterIsLoaded() {
|
public void testHalJacksonHttpMessageConverterIsLoaded() {
|
||||||
FilteredClassLoader filteredClassLoader = new FilteredClassLoader(
|
FilteredClassLoader filteredClassLoader = new FilteredClassLoader(RepositoryRestMvcConfiguration.class);
|
||||||
RepositoryRestMvcConfiguration.class);
|
contextRunner.withClassLoader(filteredClassLoader)
|
||||||
contextRunner.withClassLoader(filteredClassLoader).run(
|
.run(context -> assertThat(context).hasBean("halJacksonHttpMessageConverter"));
|
||||||
context -> assertThat(context).hasBean("halJacksonHttpMessageConverter"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testHalJacksonHttpMessageConverterIsNotLoadedUseRestDataMessageConverterInstead() {
|
public void testHalJacksonHttpMessageConverterIsNotLoadedUseRestDataMessageConverterInstead() {
|
||||||
contextRunner.run(
|
contextRunner.run(context -> assertThat(context).hasBean("halJacksonHttpMessageConverter"));
|
||||||
context -> assertThat(context).hasBean("halJacksonHttpMessageConverter"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-6
@@ -68,22 +68,20 @@ public class FeignHalAutoConfigurationTests {
|
|||||||
ObjectMapper mapper = new ObjectMapper();
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
when(objectMapper.getIfAvailable(any())).thenReturn(mapper);
|
when(objectMapper.getIfAvailable(any())).thenReturn(mapper);
|
||||||
|
|
||||||
when(halConfiguration.getIfAvailable(any()))
|
when(halConfiguration.getIfAvailable(any())).thenReturn(mock(HalConfiguration.class));
|
||||||
.thenReturn(mock(HalConfiguration.class));
|
|
||||||
when(relProvider.getIfAvailable()).thenReturn(mock(LinkRelationProvider.class));
|
when(relProvider.getIfAvailable()).thenReturn(mock(LinkRelationProvider.class));
|
||||||
when(curieProvider.getIfAvailable(any())).thenReturn(mock(CurieProvider.class));
|
when(curieProvider.getIfAvailable(any())).thenReturn(mock(CurieProvider.class));
|
||||||
when(messageResolver.getIfAvailable()).thenReturn(mock(MessageResolver.class));
|
when(messageResolver.getIfAvailable()).thenReturn(mock(MessageResolver.class));
|
||||||
|
|
||||||
TypeConstrainedMappingJackson2HttpMessageConverter converter = feignHalAutoConfiguration
|
TypeConstrainedMappingJackson2HttpMessageConverter converter = feignHalAutoConfiguration
|
||||||
.halJacksonHttpMessageConverter(objectMapper, halConfiguration,
|
.halJacksonHttpMessageConverter(objectMapper, halConfiguration, messageResolver, curieProvider,
|
||||||
messageResolver, curieProvider, relProvider);
|
relProvider);
|
||||||
|
|
||||||
assertThat(converter).isNotNull();
|
assertThat(converter).isNotNull();
|
||||||
assertThat(converter.getObjectMapper()).isNotNull();
|
assertThat(converter.getObjectMapper()).isNotNull();
|
||||||
assertThat(converter.getSupportedMediaTypes()).isEqualTo(Arrays.asList(HAL_JSON));
|
assertThat(converter.getSupportedMediaTypes()).isEqualTo(Arrays.asList(HAL_JSON));
|
||||||
|
|
||||||
assertThat(Jackson2HalModule.isAlreadyRegisteredIn(converter.getObjectMapper()))
|
assertThat(Jackson2HalModule.isAlreadyRegisteredIn(converter.getObjectMapper())).isTrue();
|
||||||
.isTrue();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-4
@@ -41,8 +41,7 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
|
|||||||
*
|
*
|
||||||
* @author Hector Espert
|
* @author Hector Espert
|
||||||
*/
|
*/
|
||||||
@SpringBootTest(classes = FeignHalApplication.class, webEnvironment = RANDOM_PORT,
|
@SpringBootTest(classes = FeignHalApplication.class, webEnvironment = RANDOM_PORT, value = "debug=true")
|
||||||
value = "debug=true")
|
|
||||||
@RunWith(SpringRunner.class)
|
@RunWith(SpringRunner.class)
|
||||||
@DirtiesContext
|
@DirtiesContext
|
||||||
public class FeignHalTests {
|
public class FeignHalTests {
|
||||||
@@ -74,8 +73,7 @@ public class FeignHalTests {
|
|||||||
assertThat(collectionModel.hasLinks()).isTrue();
|
assertThat(collectionModel.hasLinks()).isTrue();
|
||||||
assertThat(collectionModel.hasLink("self")).isTrue();
|
assertThat(collectionModel.hasLink("self")).isTrue();
|
||||||
|
|
||||||
assertThat(collectionModel.getLink("self")).map(Link::getHref)
|
assertThat(collectionModel.getLink("self")).map(Link::getHref).contains("/collection");
|
||||||
.contains("/collection");
|
|
||||||
|
|
||||||
Collection<MarsRover> collection = collectionModel.getContent();
|
Collection<MarsRover> collection = collectionModel.getContent();
|
||||||
assertThat(collection).isNotEmpty();
|
assertThat(collection).isNotEmpty();
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user