Compare commits
131
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3046af2b42 | ||
|
|
8973f4286b | ||
|
|
ef5f9d6858 | ||
|
|
f661105e63 | ||
|
|
bc10583bf3 | ||
|
|
cb1ea20f3f | ||
|
|
6bfc684967 | ||
|
|
63a9245f13 | ||
|
|
64d63cbb39 | ||
|
|
d963ebd0f0 | ||
|
|
1e1562b24a | ||
|
|
7eb7067752 | ||
|
|
bc4bfb5b7a | ||
|
|
94977691b8 | ||
|
|
b2791605b7 | ||
|
|
3e5d7f84aa | ||
|
|
eecc51e695 | ||
|
|
9dfc820306 | ||
|
|
c3fc93268c | ||
|
|
ecfd1b4cee | ||
|
|
5ce9fcf9f8 | ||
|
|
4e6c1b2d04 | ||
|
|
d8cdade30f | ||
|
|
b16904f15e | ||
|
|
21a2a59461 | ||
|
|
39dfa0ef7f | ||
|
|
96ae8479aa | ||
|
|
37fd2d4448 | ||
|
|
6493eab98a | ||
|
|
caacee575c | ||
|
|
8abbf0a343 | ||
|
|
c32b8568cf | ||
|
|
654479a696 | ||
|
|
35b9de7f6e | ||
|
|
2294dee616 | ||
|
|
2cdc1aceeb | ||
|
|
dabbfb7220 | ||
|
|
3e069b41af | ||
|
|
9cf06153a8 | ||
|
|
ead4c47fcb | ||
|
|
eda905684d | ||
|
|
2c73498398 | ||
|
|
ebd80b5115 | ||
|
|
866d20d976 | ||
|
|
0d891d284a | ||
|
|
27380898a8 | ||
|
|
d5031f8618 | ||
|
|
b5539198e6 | ||
|
|
36d63a1b9f | ||
|
|
9b8a3c6c1b | ||
|
|
2e8a5833a9 | ||
|
|
c4284683ad | ||
|
|
21805a9989 | ||
|
|
b1a8917796 | ||
|
|
f76a81fe6b | ||
|
|
3a60921794 | ||
|
|
e531539e77 | ||
|
|
7afd62dd32 | ||
|
|
2e21175087 | ||
|
|
10dda1601e | ||
|
|
84b0d0ca40 | ||
|
|
15be7111ec | ||
|
|
50c799a4a0 | ||
|
|
aba2abba8a | ||
|
|
35ec2913d9 | ||
|
|
f6fee55411 | ||
|
|
4092cb10cc | ||
|
|
0169a3c3f3 | ||
|
|
e04056be09 | ||
|
|
05893e57e7 | ||
|
|
8e0fc92c48 | ||
|
|
09f9382a51 | ||
|
|
1347724cdf | ||
|
|
fff4975c1e | ||
|
|
ac45facf04 | ||
|
|
5a54a612cf | ||
|
|
75faca6c17 | ||
|
|
a81c53a55c | ||
|
|
43c53739cf | ||
|
|
115e87b6ce | ||
|
|
33fa5b1b6b | ||
|
|
2633db6bde | ||
|
|
2c824ff39c | ||
|
|
6f9e1f63dc | ||
|
|
7a6c0a42f6 | ||
|
|
d73c61f0f0 | ||
|
|
d01bcdf29c | ||
|
|
47f2850751 | ||
|
|
702e1c7340 | ||
|
|
4ac6429152 | ||
|
|
6e0e63644b | ||
|
|
f8352de786 | ||
|
|
c8c7452848 | ||
|
|
1f37891ffe | ||
|
|
505a83e6a4 | ||
|
|
76dbc7d21f | ||
|
|
b176e3a958 | ||
|
|
eb7e4d3082 | ||
|
|
c76b02b8a3 | ||
|
|
c1418bafee | ||
|
|
67fa03dcd9 | ||
|
|
5804d12511 | ||
|
|
23ec2a1c43 | ||
|
|
d148672e13 | ||
|
|
e227808b9d | ||
|
|
642d3e6961 | ||
|
|
3051c4c1fd | ||
|
|
fb6676d634 | ||
|
|
c1bbb9cbb3 | ||
|
|
e65f78f06b | ||
|
|
a1a45125ea | ||
|
|
b00abe7b68 | ||
|
|
b9cb792820 | ||
|
|
a38ebe40c8 | ||
|
|
a064530141 | ||
|
|
61afb5dcd6 | ||
|
|
20017afdca | ||
|
|
f267c2255b | ||
|
|
dd7b869a12 | ||
|
|
d46fb3a92e | ||
|
|
cc2fe823c3 | ||
|
|
f1975e5200 | ||
|
|
ca55e95c8f | ||
|
|
e85e74e311 | ||
|
|
71675455a3 | ||
|
|
a39e6316b5 | ||
|
|
3e3e486648 | ||
|
|
15bda96e91 | ||
|
|
52bea35160 | ||
|
|
7148cce790 | ||
|
|
88fae41f02 |
@@ -9,7 +9,6 @@ jobs:
|
|||||||
branches:
|
branches:
|
||||||
ignore:
|
ignore:
|
||||||
- gh-pages # list of branches to ignore
|
- gh-pages # list of branches to ignore
|
||||||
resource_class: large
|
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
@@ -36,4 +35,4 @@ jobs:
|
|||||||
destination: artifacts
|
destination: artifacts
|
||||||
- store_test_results:
|
- store_test_results:
|
||||||
path: /junit/
|
path: /junit/
|
||||||
destination: testartifacts
|
destination: testartifacts
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# EditorConfig is awesome: https://EditorConfig.org
|
||||||
|
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = 4
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.yml]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-build</artifactId>
|
||||||
|
<version>2.2.0.RC2</version>
|
||||||
|
<relativePath></relativePath>
|
||||||
|
</parent>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-openfeign</artifactId>
|
||||||
|
<version>2.2.0.RC2</version>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<name>Spring Cloud OpenFeign</name>
|
||||||
|
<description>Spring Cloud OpenFeign</description>
|
||||||
|
<url>https://spring.io/spring-cloud/spring-cloud-openfeign</url>
|
||||||
|
<organization>
|
||||||
|
<name>Pivotal Software, Inc.</name>
|
||||||
|
<url>https://www.spring.io</url>
|
||||||
|
</organization>
|
||||||
|
<licenses>
|
||||||
|
<license>
|
||||||
|
<name>Apache License, Version 2.0</name>
|
||||||
|
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
|
||||||
|
<comments>Copyright 2014-2015 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.</comments>
|
||||||
|
</license>
|
||||||
|
</licenses>
|
||||||
|
<developers>
|
||||||
|
<developer>
|
||||||
|
<id>dsyer</id>
|
||||||
|
<name>Dave Syer</name>
|
||||||
|
<email>dsyer at pivotal.io</email>
|
||||||
|
<organization>Pivotal Software, Inc.</organization>
|
||||||
|
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||||
|
<roles>
|
||||||
|
<role>lead</role>
|
||||||
|
</roles>
|
||||||
|
</developer>
|
||||||
|
<developer>
|
||||||
|
<id>sgibb</id>
|
||||||
|
<name>Spencer Gibb</name>
|
||||||
|
<email>sgibb at pivotal.io</email>
|
||||||
|
<organization>Pivotal Software, Inc.</organization>
|
||||||
|
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||||
|
<roles>
|
||||||
|
<role>lead</role>
|
||||||
|
</roles>
|
||||||
|
</developer>
|
||||||
|
<developer>
|
||||||
|
<id>mgrzejszczak</id>
|
||||||
|
<name>Marcin Grzejszczak</name>
|
||||||
|
<email>mgrzejszczak at pivotal.io</email>
|
||||||
|
<organization>Pivotal Software, Inc.</organization>
|
||||||
|
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||||
|
<roles>
|
||||||
|
<role>developer</role>
|
||||||
|
</roles>
|
||||||
|
</developer>
|
||||||
|
<developer>
|
||||||
|
<id>rbaxter</id>
|
||||||
|
<name>Ryan Baxter</name>
|
||||||
|
<email>rbaxter at pivotal.io</email>
|
||||||
|
<organization>Pivotal Software, Inc.</organization>
|
||||||
|
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||||
|
<roles>
|
||||||
|
<role>developer</role>
|
||||||
|
</roles>
|
||||||
|
</developer>
|
||||||
|
<developer>
|
||||||
|
<id>omaciaszeksharma</id>
|
||||||
|
<name>Olga Maciaszek-Sharma</name>
|
||||||
|
<email>omaciaszeksharma at pivotal.io</email>
|
||||||
|
<organization>Pivotal Software, Inc.</organization>
|
||||||
|
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||||
|
<roles>
|
||||||
|
<role>developer</role>
|
||||||
|
</roles>
|
||||||
|
</developer>
|
||||||
|
</developers>
|
||||||
|
<scm>
|
||||||
|
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-openfeign.git</connection>
|
||||||
|
<developerConnection>scm:git:ssh://git@github.com/spring-cloud/spring-cloud-openfeign.git</developerConnection>
|
||||||
|
<url>https://github.com/spring-cloud/spring-cloud-openfeign</url>
|
||||||
|
</scm>
|
||||||
|
<profiles>
|
||||||
|
<profile>
|
||||||
|
<id>spring</id>
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<releases>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</releases>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>true</enabled>
|
||||||
|
</snapshots>
|
||||||
|
<id>spring-snapshots</id>
|
||||||
|
<name>Spring Snapshots</name>
|
||||||
|
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
<id>spring-milestones</id>
|
||||||
|
<name>Spring Milestones</name>
|
||||||
|
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||||
|
</repository>
|
||||||
|
<repository>
|
||||||
|
<snapshots>
|
||||||
|
<enabled>false</enabled>
|
||||||
|
</snapshots>
|
||||||
|
<id>spring-releases</id>
|
||||||
|
<name>Spring Releases</name>
|
||||||
|
<url>https://repo.spring.io/release</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
|
</project>
|
||||||
@@ -28,7 +28,7 @@ added after the original pull request but before a merge.
|
|||||||
you can import formatter settings using the
|
you can import formatter settings using the
|
||||||
`eclipse-code-formatter.xml` file from the
|
`eclipse-code-formatter.xml` file from the
|
||||||
[Spring Cloud Build](https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-dependencies-parent/eclipse-code-formatter.xml) project. If using IntelliJ, you can use the
|
[Spring Cloud Build](https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-dependencies-parent/eclipse-code-formatter.xml) project. If using IntelliJ, you can use the
|
||||||
[Eclipse Code Formatter Plugin](http://plugins.jetbrains.com/plugin/6546) to import the same file.
|
[Eclipse Code Formatter Plugin](https://plugins.jetbrains.com/plugin/6546) to import the same file.
|
||||||
* Make sure all new `.java` files to have a simple Javadoc class comment with at least an
|
* Make sure all new `.java` files to have a simple Javadoc class comment with at least an
|
||||||
`@author` tag identifying you, and preferably at least a paragraph on what the class is
|
`@author` tag identifying you, and preferably at least a paragraph on what the class is
|
||||||
for.
|
for.
|
||||||
@@ -40,6 +40,6 @@ added after the original pull request but before a merge.
|
|||||||
* A few unit tests would help a lot as well -- someone has to do it.
|
* A few unit tests would help a lot as well -- someone has to do it.
|
||||||
* If no-one else is using your branch, please rebase it against the current master (or
|
* If no-one else is using your branch, please rebase it against the current master (or
|
||||||
other target branch in the main project).
|
other target branch in the main project).
|
||||||
* When writing a commit message please follow [these conventions](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html),
|
* When writing a commit message please follow [these conventions](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html),
|
||||||
if you are fixing an existing issue please add `Fixes gh-XXXX` at the end of the commit
|
if you are fixing an existing issue please add `Fixes gh-XXXX` at the end of the commit
|
||||||
message (where XXXX is the issue number).
|
message (where XXXX is the issue number).
|
||||||
|
|||||||
@@ -18,3 +18,4 @@ _site/
|
|||||||
.shelf
|
.shelf
|
||||||
*.swp
|
*.swp
|
||||||
*.swo
|
*.swo
|
||||||
|
.vscode/
|
||||||
+65
-63
@@ -1,66 +1,68 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<settings>
|
<settings>
|
||||||
<servers>
|
<servers>
|
||||||
<server>
|
<server>
|
||||||
<id>repo.spring.io</id>
|
<id>repo.spring.io</id>
|
||||||
<username>${env.CI_DEPLOY_USERNAME}</username>
|
<username>${env.CI_DEPLOY_USERNAME}</username>
|
||||||
<password>${env.CI_DEPLOY_PASSWORD}</password>
|
<password>${env.CI_DEPLOY_PASSWORD}</password>
|
||||||
</server>
|
</server>
|
||||||
</servers>
|
</servers>
|
||||||
<profiles>
|
<profiles>
|
||||||
<profile>
|
<profile>
|
||||||
<!--
|
<!--
|
||||||
N.B. this profile is only here to support users and IDEs that do not use Maven 3.3.
|
N.B. this profile is only here to support users and IDEs that do not use Maven 3.3.
|
||||||
It isn't needed on the command line if you use the wrapper script (mvnw) or if you use
|
It isn't needed on the command line if you use the wrapper script (mvnw) or if you use
|
||||||
a native Maven with the right version. Eclipse users should points their Maven tooling to
|
a native Maven with the right version. Eclipse users should points their Maven tooling to
|
||||||
this settings file, or copy the profile into their ~/.m2/settings.xml.
|
this settings file, or copy the profile into their ~/.m2/settings.xml.
|
||||||
-->
|
-->
|
||||||
<id>spring</id>
|
<id>spring</id>
|
||||||
<activation><activeByDefault>true</activeByDefault></activation>
|
<activation>
|
||||||
<repositories>
|
<activeByDefault>true</activeByDefault>
|
||||||
<repository>
|
</activation>
|
||||||
<id>spring-snapshots</id>
|
<repositories>
|
||||||
<name>Spring Snapshots</name>
|
<repository>
|
||||||
<url>http://repo.spring.io/libs-snapshot-local</url>
|
<id>spring-snapshots</id>
|
||||||
<snapshots>
|
<name>Spring Snapshots</name>
|
||||||
<enabled>true</enabled>
|
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||||
</snapshots>
|
<snapshots>
|
||||||
</repository>
|
<enabled>true</enabled>
|
||||||
<repository>
|
</snapshots>
|
||||||
<id>spring-milestones</id>
|
</repository>
|
||||||
<name>Spring Milestones</name>
|
<repository>
|
||||||
<url>http://repo.spring.io/libs-milestone-local</url>
|
<id>spring-milestones</id>
|
||||||
<snapshots>
|
<name>Spring Milestones</name>
|
||||||
<enabled>false</enabled>
|
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||||
</snapshots>
|
<snapshots>
|
||||||
</repository>
|
<enabled>false</enabled>
|
||||||
<repository>
|
</snapshots>
|
||||||
<id>spring-releases</id>
|
</repository>
|
||||||
<name>Spring Releases</name>
|
<repository>
|
||||||
<url>http://repo.spring.io/release</url>
|
<id>spring-releases</id>
|
||||||
<snapshots>
|
<name>Spring Releases</name>
|
||||||
<enabled>false</enabled>
|
<url>https://repo.spring.io/release</url>
|
||||||
</snapshots>
|
<snapshots>
|
||||||
</repository>
|
<enabled>false</enabled>
|
||||||
</repositories>
|
</snapshots>
|
||||||
<pluginRepositories>
|
</repository>
|
||||||
<pluginRepository>
|
</repositories>
|
||||||
<id>spring-snapshots</id>
|
<pluginRepositories>
|
||||||
<name>Spring Snapshots</name>
|
<pluginRepository>
|
||||||
<url>http://repo.spring.io/libs-snapshot-local</url>
|
<id>spring-snapshots</id>
|
||||||
<snapshots>
|
<name>Spring Snapshots</name>
|
||||||
<enabled>true</enabled>
|
<url>https://repo.spring.io/libs-snapshot-local</url>
|
||||||
</snapshots>
|
<snapshots>
|
||||||
</pluginRepository>
|
<enabled>true</enabled>
|
||||||
<pluginRepository>
|
</snapshots>
|
||||||
<id>spring-milestones</id>
|
</pluginRepository>
|
||||||
<name>Spring Milestones</name>
|
<pluginRepository>
|
||||||
<url>http://repo.spring.io/libs-milestone-local</url>
|
<id>spring-milestones</id>
|
||||||
<snapshots>
|
<name>Spring Milestones</name>
|
||||||
<enabled>false</enabled>
|
<url>https://repo.spring.io/libs-milestone-local</url>
|
||||||
</snapshots>
|
<snapshots>
|
||||||
</pluginRepository>
|
<enabled>false</enabled>
|
||||||
</pluginRepositories>
|
</snapshots>
|
||||||
</profile>
|
</pluginRepository>
|
||||||
</profiles>
|
</pluginRepositories>
|
||||||
|
</profile>
|
||||||
|
</profiles>
|
||||||
</settings>
|
</settings>
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
Apache License
|
Apache License
|
||||||
Version 2.0, January 2004
|
Version 2.0, January 2004
|
||||||
http://www.apache.org/licenses/
|
https://www.apache.org/licenses/
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
@@ -193,7 +193,7 @@
|
|||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
You may obtain a copy of the License at
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
Unless required by applicable law or agreed to in writing, software
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
|||||||
+165
-9
@@ -1,4 +1,8 @@
|
|||||||
// Do not edit this file (e.g. go instead to src/main/asciidoc)
|
////
|
||||||
|
DO NOT EDIT THIS FILE. IT WAS GENERATED.
|
||||||
|
Manual changes to this file will be lost when it is generated again.
|
||||||
|
Edit the files in the src/main/asciidoc/ directory instead.
|
||||||
|
////
|
||||||
|
|
||||||
image:https://circleci.com/gh/spring-cloud/spring-cloud-openfeign.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-openfeign"]
|
image:https://circleci.com/gh/spring-cloud/spring-cloud-openfeign.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-openfeign"]
|
||||||
|
|
||||||
@@ -6,6 +10,23 @@ image:https://codecov.io/gh/spring-cloud/spring-cloud-openfeign/branch/master/gr
|
|||||||
|
|
||||||
image:https://api.codacy.com/project/badge/Grade/97b04c4e609c4b4f86b415e4437a6484["Codacy code quality", link="https://www.codacy.com/app/Spring-Cloud/spring-cloud-openfeign?utm_source=github.com&utm_medium=referral&utm_content=spring-cloud/spring-cloud-openfeign&utm_campaign=Badge_Grade"]
|
image:https://api.codacy.com/project/badge/Grade/97b04c4e609c4b4f86b415e4437a6484["Codacy code quality", link="https://www.codacy.com/app/Spring-Cloud/spring-cloud-openfeign?utm_source=github.com&utm_medium=referral&utm_content=spring-cloud/spring-cloud-openfeign&utm_campaign=Badge_Grade"]
|
||||||
|
|
||||||
|
:doctype: book
|
||||||
|
:idprefix:
|
||||||
|
:idseparator: -
|
||||||
|
:toc: left
|
||||||
|
:toclevels: 4
|
||||||
|
:tabsize: 4
|
||||||
|
:numbered:
|
||||||
|
:sectanchors:
|
||||||
|
:sectnums:
|
||||||
|
:icons: font
|
||||||
|
:hide-uri-scheme:
|
||||||
|
:docinfo: shared,private
|
||||||
|
|
||||||
|
:sc-ext: java
|
||||||
|
:project-full-name: Spring Cloud OpenFeign
|
||||||
|
:all: {asterisk}{asterisk}
|
||||||
|
|
||||||
This project provides OpenFeign integrations for Spring Boot apps through autoconfiguration
|
This project provides OpenFeign integrations for Spring Boot apps through autoconfiguration
|
||||||
and binding to the Spring Environment and other Spring programming model idioms.
|
and binding to the Spring Environment and other Spring programming model idioms.
|
||||||
|
|
||||||
@@ -51,7 +72,7 @@ credentials and you already have those.
|
|||||||
|
|
||||||
The projects that require middleware generally include a
|
The projects that require middleware generally include a
|
||||||
`docker-compose.yml`, so consider using
|
`docker-compose.yml`, so consider using
|
||||||
http://compose.docker.io/[Docker Compose] to run the middeware servers
|
https://docs.docker.com/compose/[Docker Compose] to run the middeware servers
|
||||||
in Docker containers. See the README in the
|
in Docker containers. See the README in the
|
||||||
https://github.com/spring-cloud-samples/scripts[scripts demo
|
https://github.com/spring-cloud-samples/scripts[scripts demo
|
||||||
repository] for specific instructions about the common cases of mongo,
|
repository] for specific instructions about the common cases of mongo,
|
||||||
@@ -73,13 +94,13 @@ a modified file in the correct place. Just commit it and push the change.
|
|||||||
|
|
||||||
=== Working with the code
|
=== Working with the code
|
||||||
If you don't have an IDE preference we would recommend that you use
|
If you don't have an IDE preference we would recommend that you use
|
||||||
http://www.springsource.com/developer/sts[Spring Tools Suite] or
|
https://www.springsource.com/developer/sts[Spring Tools Suite] or
|
||||||
http://eclipse.org[Eclipse] when working with the code. We use the
|
https://eclipse.org[Eclipse] when working with the code. We use the
|
||||||
http://eclipse.org/m2e/[m2eclipse] eclipse plugin for maven support. Other IDEs and tools
|
https://eclipse.org/m2e/[m2eclipse] eclipse plugin for maven support. Other IDEs and tools
|
||||||
should also work without issue as long as they use Maven 3.3.3 or better.
|
should also work without issue as long as they use Maven 3.3.3 or better.
|
||||||
|
|
||||||
==== Importing into eclipse with m2eclipse
|
==== Importing into eclipse with m2eclipse
|
||||||
We recommend the http://eclipse.org/m2e/[m2eclipse] eclipse plugin when working with
|
We recommend the https://eclipse.org/m2e/[m2eclipse] eclipse plugin when working with
|
||||||
eclipse. If you don't already have m2eclipse installed it is available from the "eclipse
|
eclipse. If you don't already have m2eclipse installed it is available from the "eclipse
|
||||||
marketplace".
|
marketplace".
|
||||||
|
|
||||||
@@ -108,6 +129,8 @@ from the `file` menu.
|
|||||||
|
|
||||||
== Contributing
|
== Contributing
|
||||||
|
|
||||||
|
:spring-cloud-build-branch: master
|
||||||
|
|
||||||
Spring Cloud is released under the non-restrictive Apache 2.0 license,
|
Spring Cloud is released under the non-restrictive Apache 2.0 license,
|
||||||
and follows a very standard Github development process, using Github
|
and follows a very standard Github development process, using Github
|
||||||
tracker for issues and merging pull requests into master. If you want
|
tracker for issues and merging pull requests into master. If you want
|
||||||
@@ -136,7 +159,7 @@ added after the original pull request but before a merge.
|
|||||||
`eclipse-code-formatter.xml` file from the
|
`eclipse-code-formatter.xml` file from the
|
||||||
https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-dependencies-parent/eclipse-code-formatter.xml[Spring
|
https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-dependencies-parent/eclipse-code-formatter.xml[Spring
|
||||||
Cloud Build] project. If using IntelliJ, you can use the
|
Cloud Build] project. If using IntelliJ, you can use the
|
||||||
http://plugins.jetbrains.com/plugin/6546[Eclipse Code Formatter
|
https://plugins.jetbrains.com/plugin/6546[Eclipse Code Formatter
|
||||||
Plugin] to import the same file.
|
Plugin] to import the same file.
|
||||||
* Make sure all new `.java` files to have a simple Javadoc class comment with at least an
|
* Make sure all new `.java` files to have a simple Javadoc class comment with at least an
|
||||||
`@author` tag identifying you, and preferably at least a paragraph on what the class is
|
`@author` tag identifying you, and preferably at least a paragraph on what the class is
|
||||||
@@ -149,6 +172,139 @@ added after the original pull request but before a merge.
|
|||||||
* A few unit tests would help a lot as well -- someone has to do it.
|
* A few unit tests would help a lot as well -- someone has to do it.
|
||||||
* If no-one else is using your branch, please rebase it against the current master (or
|
* If no-one else is using your branch, please rebase it against the current master (or
|
||||||
other target branch in the main project).
|
other target branch in the main project).
|
||||||
* When writing a commit message please follow http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html[these conventions],
|
* When writing a commit message please follow https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html[these conventions],
|
||||||
if you are fixing an existing issue please add `Fixes gh-XXXX` at the end of the commit
|
if you are fixing an existing issue please add `Fixes gh-XXXX` at the end of the commit
|
||||||
message (where XXXX is the issue number).
|
message (where XXXX is the issue number).
|
||||||
|
|
||||||
|
=== Checkstyle
|
||||||
|
|
||||||
|
Spring Cloud Build comes with a set of checkstyle rules. You can find them in the `spring-cloud-build-tools` module. The most notable files under the module are:
|
||||||
|
|
||||||
|
.spring-cloud-build-tools/
|
||||||
|
----
|
||||||
|
└── src
|
||||||
|
├── checkstyle
|
||||||
|
│ └── checkstyle-suppressions.xml <3>
|
||||||
|
└── main
|
||||||
|
└── resources
|
||||||
|
├── checkstyle-header.txt <2>
|
||||||
|
└── checkstyle.xml <1>
|
||||||
|
----
|
||||||
|
<1> Default Checkstyle rules
|
||||||
|
<2> File header setup
|
||||||
|
<3> Default suppression rules
|
||||||
|
|
||||||
|
==== Checkstyle configuration
|
||||||
|
|
||||||
|
Checkstyle rules are *disabled by default*. To add checkstyle to your project just define the following properties and plugins.
|
||||||
|
|
||||||
|
.pom.xml
|
||||||
|
----
|
||||||
|
<properties>
|
||||||
|
<maven-checkstyle-plugin.failsOnError>true</maven-checkstyle-plugin.failsOnError> <1>
|
||||||
|
<maven-checkstyle-plugin.failsOnViolation>true
|
||||||
|
</maven-checkstyle-plugin.failsOnViolation> <2>
|
||||||
|
<maven-checkstyle-plugin.includeTestSourceDirectory>true
|
||||||
|
</maven-checkstyle-plugin.includeTestSourceDirectory> <3>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin> <4>
|
||||||
|
<groupId>io.spring.javaformat</groupId>
|
||||||
|
<artifactId>spring-javaformat-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin> <5>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
|
||||||
|
<reporting>
|
||||||
|
<plugins>
|
||||||
|
<plugin> <5>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</reporting>
|
||||||
|
</build>
|
||||||
|
----
|
||||||
|
<1> Fails the build upon Checkstyle errors
|
||||||
|
<2> Fails the build upon Checkstyle violations
|
||||||
|
<3> Checkstyle analyzes also the test sources
|
||||||
|
<4> Add the Spring Java Format plugin that will reformat your code to pass most of the Checkstyle formatting rules
|
||||||
|
<5> Add checkstyle plugin to your build and reporting phases
|
||||||
|
|
||||||
|
If you need to suppress some rules (e.g. line length needs to be longer), then it's enough for you to define a file under `${project.root}/src/checkstyle/checkstyle-suppressions.xml` with your suppressions. Example:
|
||||||
|
|
||||||
|
.projectRoot/src/checkstyle/checkstyle-suppresions.xml
|
||||||
|
----
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!DOCTYPE suppressions PUBLIC
|
||||||
|
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
|
||||||
|
"https://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
|
||||||
|
<suppressions>
|
||||||
|
<suppress files=".*ConfigServerApplication\.java" checks="HideUtilityClassConstructor"/>
|
||||||
|
<suppress files=".*ConfigClientWatch\.java" checks="LineLengthCheck"/>
|
||||||
|
</suppressions>
|
||||||
|
----
|
||||||
|
|
||||||
|
It's advisable to copy the `${spring-cloud-build.rootFolder}/.editorconfig` and `${spring-cloud-build.rootFolder}/.springformat` to your project. That way, some default formatting rules will be applied. You can do so by running this script:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ curl https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/.editorconfig -o .editorconfig
|
||||||
|
$ touch .springformat
|
||||||
|
```
|
||||||
|
|
||||||
|
=== IDE setup
|
||||||
|
|
||||||
|
==== Intellij IDEA
|
||||||
|
|
||||||
|
In order to setup Intellij you should import our coding conventions, inspection profiles and set up the checkstyle plugin.
|
||||||
|
The following files can be found in the https://github.com/spring-cloud/spring-cloud-build/tree/master/spring-cloud-build-tools[Spring Cloud Build] project.
|
||||||
|
|
||||||
|
.spring-cloud-build-tools/
|
||||||
|
----
|
||||||
|
└── src
|
||||||
|
├── checkstyle
|
||||||
|
│ └── checkstyle-suppressions.xml <3>
|
||||||
|
└── main
|
||||||
|
└── resources
|
||||||
|
├── checkstyle-header.txt <2>
|
||||||
|
├── checkstyle.xml <1>
|
||||||
|
└── intellij
|
||||||
|
├── Intellij_Project_Defaults.xml <4>
|
||||||
|
└── Intellij_Spring_Boot_Java_Conventions.xml <5>
|
||||||
|
----
|
||||||
|
<1> Default Checkstyle rules
|
||||||
|
<2> File header setup
|
||||||
|
<3> Default suppression rules
|
||||||
|
<4> Project defaults for Intellij that apply most of Checkstyle rules
|
||||||
|
<5> Project style conventions for Intellij that apply most of Checkstyle rules
|
||||||
|
|
||||||
|
.Code style
|
||||||
|
|
||||||
|
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/{spring-cloud-build-branch}/docs/src/main/asciidoc/images/intellij-code-style.png[Code style]
|
||||||
|
|
||||||
|
Go to `File` -> `Settings` -> `Editor` -> `Code style`. There click on the icon next to the `Scheme` section. There, click on the `Import Scheme` value and pick the `Intellij IDEA code style XML` option. Import the `spring-cloud-build-tools/src/main/resources/intellij/Intellij_Spring_Boot_Java_Conventions.xml` file.
|
||||||
|
|
||||||
|
.Inspection profiles
|
||||||
|
|
||||||
|
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/{spring-cloud-build-branch}/docs/src/main/asciidoc/images/intellij-inspections.png[Code style]
|
||||||
|
|
||||||
|
Go to `File` -> `Settings` -> `Editor` -> `Inspections`. There click on the icon next to the `Profile` section. There, click on the `Import Profile` and import the `spring-cloud-build-tools/src/main/resources/intellij/Intellij_Project_Defaults.xml` file.
|
||||||
|
|
||||||
|
.Checkstyle
|
||||||
|
|
||||||
|
To have Intellij work with Checkstyle, you have to install the `Checkstyle` plugin. It's advisable to also install the `Assertions2Assertj` to automatically convert the JUnit assertions
|
||||||
|
|
||||||
|
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/{spring-cloud-build-branch}/docs/src/main/asciidoc/images/intellij-checkstyle.png[Checkstyle]
|
||||||
|
|
||||||
|
Go to `File` -> `Settings` -> `Other settings` -> `Checkstyle`. There click on the `+` icon in the `Configuration file` section. There, you'll have to define where the checkstyle rules should be picked from. In the image above, we've picked the rules from the cloned Spring Cloud Build repository. However, you can point to the Spring Cloud Build's GitHub repository (e.g. for the `checkstyle.xml` : `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/main/resources/checkstyle.xml`). We need to provide the following variables:
|
||||||
|
|
||||||
|
- `checkstyle.header.file` - please point it to the Spring Cloud Build's, `spring-cloud-build-tools/src/main/resources/checkstyle-header.txt` file either in your cloned repo or via the `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/main/resources/checkstyle-header.txt` URL.
|
||||||
|
- `checkstyle.suppressions.file` - default suppressions. Please point it to the Spring Cloud Build's, `spring-cloud-build-tools/src/checkstyle/checkstyle-suppressions.xml` file either in your cloned repo or via the `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/checkstyle/checkstyle-suppressions.xml` URL.
|
||||||
|
- `checkstyle.additional.suppressions.file` - this variable corresponds to suppressions in your local project. E.g. you're working on `spring-cloud-contract`. Then point to the `project-root/src/checkstyle/checkstyle-suppressions.xml` folder. Example for `spring-cloud-contract` would be: `/home/username/spring-cloud-contract/src/checkstyle/checkstyle-suppressions.xml`.
|
||||||
|
|
||||||
|
IMPORTANT: Remember to set the `Scan Scope` to `All sources` since we apply checkstyle rules for production and test sources.
|
||||||
+2009
-397
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,107 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-openfeign</artifactId>
|
||||||
|
<version>2.2.0.RC2</version>
|
||||||
|
</parent>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-openfeign-docs</artifactId>
|
||||||
|
<version>2.2.0.RC2</version>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
<name>Spring Cloud OpenFeign Docs</name>
|
||||||
|
<description>Spring Cloud Docs</description>
|
||||||
|
<url>https://spring.io/spring-cloud/spring-cloud-openfeign/spring-cloud-openfeign-docs</url>
|
||||||
|
<organization>
|
||||||
|
<name>Pivotal Software, Inc.</name>
|
||||||
|
<url>https://www.spring.io</url>
|
||||||
|
</organization>
|
||||||
|
<licenses>
|
||||||
|
<license>
|
||||||
|
<name>Apache License, Version 2.0</name>
|
||||||
|
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
|
||||||
|
<comments>Copyright 2014-2015 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.</comments>
|
||||||
|
</license>
|
||||||
|
</licenses>
|
||||||
|
<developers>
|
||||||
|
<developer>
|
||||||
|
<id>dsyer</id>
|
||||||
|
<name>Dave Syer</name>
|
||||||
|
<email>dsyer at pivotal.io</email>
|
||||||
|
<organization>Pivotal Software, Inc.</organization>
|
||||||
|
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||||
|
<roles>
|
||||||
|
<role>lead</role>
|
||||||
|
</roles>
|
||||||
|
</developer>
|
||||||
|
<developer>
|
||||||
|
<id>sgibb</id>
|
||||||
|
<name>Spencer Gibb</name>
|
||||||
|
<email>sgibb at pivotal.io</email>
|
||||||
|
<organization>Pivotal Software, Inc.</organization>
|
||||||
|
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||||
|
<roles>
|
||||||
|
<role>lead</role>
|
||||||
|
</roles>
|
||||||
|
</developer>
|
||||||
|
<developer>
|
||||||
|
<id>mgrzejszczak</id>
|
||||||
|
<name>Marcin Grzejszczak</name>
|
||||||
|
<email>mgrzejszczak at pivotal.io</email>
|
||||||
|
<organization>Pivotal Software, Inc.</organization>
|
||||||
|
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||||
|
<roles>
|
||||||
|
<role>developer</role>
|
||||||
|
</roles>
|
||||||
|
</developer>
|
||||||
|
<developer>
|
||||||
|
<id>rbaxter</id>
|
||||||
|
<name>Ryan Baxter</name>
|
||||||
|
<email>rbaxter at pivotal.io</email>
|
||||||
|
<organization>Pivotal Software, Inc.</organization>
|
||||||
|
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||||
|
<roles>
|
||||||
|
<role>developer</role>
|
||||||
|
</roles>
|
||||||
|
</developer>
|
||||||
|
<developer>
|
||||||
|
<id>omaciaszeksharma</id>
|
||||||
|
<name>Olga Maciaszek-Sharma</name>
|
||||||
|
<email>omaciaszeksharma at pivotal.io</email>
|
||||||
|
<organization>Pivotal Software, Inc.</organization>
|
||||||
|
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||||
|
<roles>
|
||||||
|
<role>developer</role>
|
||||||
|
</roles>
|
||||||
|
</developer>
|
||||||
|
</developers>
|
||||||
|
<scm>
|
||||||
|
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-openfeign.git/spring-cloud-openfeign-docs</connection>
|
||||||
|
<developerConnection>scm:git:ssh://git@github.com/spring-cloud/spring-cloud-openfeign.git/spring-cloud-openfeign-docs</developerConnection>
|
||||||
|
<url>https://github.com/spring-cloud/spring-cloud-openfeign/spring-cloud-openfeign-docs</url>
|
||||||
|
</scm>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||||
|
<version>2.2.0.RC2</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
+24
-15
@@ -1,11 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
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>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-openfeign</artifactId>
|
<artifactId>spring-cloud-openfeign</artifactId>
|
||||||
<version>2.0.3.BUILD-SNAPSHOT</version>
|
<version>2.2.0.RC2</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>spring-cloud-openfeign-docs</artifactId>
|
<artifactId>spring-cloud-openfeign-docs</artifactId>
|
||||||
<packaging>pom</packaging>
|
<packaging>pom</packaging>
|
||||||
@@ -14,8 +15,15 @@
|
|||||||
<properties>
|
<properties>
|
||||||
<docs.main>spring-cloud-openfeign</docs.main>
|
<docs.main>spring-cloud-openfeign</docs.main>
|
||||||
<main.basedir>${basedir}/..</main.basedir>
|
<main.basedir>${basedir}/..</main.basedir>
|
||||||
<docs.whitelisted.branches>2.0.x</docs.whitelisted.branches>
|
<docs.whitelisted.branches>2.0.x,2.1.x</docs.whitelisted.branches>
|
||||||
|
<configprops.inclusionPattern>feign.*</configprops.inclusionPattern>
|
||||||
</properties>
|
</properties>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>${project.groupId}</groupId>
|
||||||
|
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
@@ -31,28 +39,29 @@
|
|||||||
<id>docs</id>
|
<id>docs</id>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>pl.project13.maven</groupId>
|
||||||
|
<artifactId>git-commit-id-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-dependency-plugin</artifactId>
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.asciidoctor</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>asciidoctor-maven-plugin</artifactId>
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
<inherited>false</inherited>
|
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>com.agilejava.docbkx</groupId>
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
<artifactId>docbkx-maven-plugin</artifactId>
|
<artifactId>exec-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.asciidoctor</groupId>
|
||||||
|
<artifactId>asciidoctor-maven-plugin</artifactId>
|
||||||
</plugin>
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-antrun-plugin</artifactId>
|
<artifactId>maven-antrun-plugin</artifactId>
|
||||||
<inherited>false</inherited>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.codehaus.mojo</groupId>
|
|
||||||
<artifactId>build-helper-maven-plugin</artifactId>
|
|
||||||
<inherited>false</inherited>
|
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ image:https://codecov.io/gh/spring-cloud/spring-cloud-openfeign/branch/master/gr
|
|||||||
|
|
||||||
image:https://api.codacy.com/project/badge/Grade/97b04c4e609c4b4f86b415e4437a6484["Codacy code quality", link="https://www.codacy.com/app/Spring-Cloud/spring-cloud-openfeign?utm_source=github.com&utm_medium=referral&utm_content=spring-cloud/spring-cloud-openfeign&utm_campaign=Badge_Grade"]
|
image:https://api.codacy.com/project/badge/Grade/97b04c4e609c4b4f86b415e4437a6484["Codacy code quality", link="https://www.codacy.com/app/Spring-Cloud/spring-cloud-openfeign?utm_source=github.com&utm_medium=referral&utm_content=spring-cloud/spring-cloud-openfeign&utm_campaign=Badge_Grade"]
|
||||||
|
|
||||||
|
include::_attributes.adoc[]
|
||||||
|
|
||||||
include::intro.adoc[]
|
include::intro.adoc[]
|
||||||
|
|
||||||
== Features
|
== Features
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
:doctype: book
|
||||||
|
:idprefix:
|
||||||
|
:idseparator: -
|
||||||
|
:toc: left
|
||||||
|
:toclevels: 4
|
||||||
|
:tabsize: 4
|
||||||
|
:numbered:
|
||||||
|
:sectanchors:
|
||||||
|
:sectnums:
|
||||||
|
:icons: font
|
||||||
|
:hide-uri-scheme:
|
||||||
|
:docinfo: shared,private
|
||||||
|
|
||||||
|
:sc-ext: java
|
||||||
|
:project-full-name: Spring Cloud OpenFeign
|
||||||
|
:all: {asterisk}{asterisk}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
|===
|
||||||
|
|Name | Default | Description
|
||||||
|
|
||||||
|
|feign.client.config | |
|
||||||
|
|feign.client.default-config | default |
|
||||||
|
|feign.client.default-to-properties | true |
|
||||||
|
|feign.compression.request.enabled | false | Enables the request sent by Feign to be compressed.
|
||||||
|
|feign.compression.request.mime-types | [text/xml, application/xml, application/json] | The list of supported mime types.
|
||||||
|
|feign.compression.request.min-request-size | 2048 | The minimum threshold content size.
|
||||||
|
|feign.compression.response.enabled | false | Enables the response from Feign to be compressed.
|
||||||
|
|feign.compression.response.useGzipDecoder | false | Enables the default gzip decoder to be used.
|
||||||
|
|feign.httpclient.connection-timeout | 2000 |
|
||||||
|
|feign.httpclient.connection-timer-repeat | 3000 |
|
||||||
|
|feign.httpclient.disable-ssl-validation | false |
|
||||||
|
|feign.httpclient.enabled | true | Enables the use of the Apache HTTP Client by Feign.
|
||||||
|
|feign.httpclient.follow-redirects | true |
|
||||||
|
|feign.httpclient.max-connections | 200 |
|
||||||
|
|feign.httpclient.max-connections-per-route | 50 |
|
||||||
|
|feign.httpclient.time-to-live | 900 |
|
||||||
|
|feign.httpclient.time-to-live-unit | |
|
||||||
|
|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.
|
||||||
|
|
||||||
|
|===
|
||||||
@@ -40,7 +40,7 @@ function check_if_anything_to_sync() {
|
|||||||
|
|
||||||
function retrieve_current_branch() {
|
function retrieve_current_branch() {
|
||||||
# Code getting the name of the current branch. For master we want to publish as we did until now
|
# Code getting the name of the current branch. For master we want to publish as we did until now
|
||||||
# http://stackoverflow.com/questions/1593051/how-to-programmatically-determine-the-current-checked-out-git-branch
|
# https://stackoverflow.com/questions/1593051/how-to-programmatically-determine-the-current-checked-out-git-branch
|
||||||
# If there is a branch already passed will reuse it - otherwise will try to find it
|
# If there is a branch already passed will reuse it - otherwise will try to find it
|
||||||
CURRENT_BRANCH=${BRANCH}
|
CURRENT_BRANCH=${BRANCH}
|
||||||
if [[ -z "${CURRENT_BRANCH}" ]] ; then
|
if [[ -z "${CURRENT_BRANCH}" ]] ; then
|
||||||
@@ -147,7 +147,7 @@ function copy_docs_for_current_version() {
|
|||||||
COMMIT_CHANGES="yes"
|
COMMIT_CHANGES="yes"
|
||||||
else
|
else
|
||||||
echo -e "Current branch is [${CURRENT_BRANCH}]"
|
echo -e "Current branch is [${CURRENT_BRANCH}]"
|
||||||
# http://stackoverflow.com/questions/29300806/a-bash-script-to-check-if-a-string-is-present-in-a-comma-separated-list-of-strin
|
# https://stackoverflow.com/questions/29300806/a-bash-script-to-check-if-a-string-is-present-in-a-comma-separated-list-of-strin
|
||||||
if [[ ",${WHITELISTED_BRANCHES_VALUE}," = *",${CURRENT_BRANCH},"* ]] ; then
|
if [[ ",${WHITELISTED_BRANCHES_VALUE}," = *",${CURRENT_BRANCH},"* ]] ; then
|
||||||
mkdir -p ${ROOT_FOLDER}/${CURRENT_BRANCH}
|
mkdir -p ${ROOT_FOLDER}/${CURRENT_BRANCH}
|
||||||
echo -e "Branch [${CURRENT_BRANCH}] is whitelisted! Will copy the current docs to the [${CURRENT_BRANCH}] folder"
|
echo -e "Branch [${CURRENT_BRANCH}] is whitelisted! Will copy the current docs to the [${CURRENT_BRANCH}] folder"
|
||||||
|
|||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
spring-cloud-openfeign.adoc
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
This project provides https://github.com/OpenFeign/feign[OpenFeign] integrations for Spring Boot apps through autoconfiguration and binding to the Spring Environment and other Spring programming model idioms.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* Declarative REST Client: Feign creates a dynamic implementation of an interface decorated with JAX-RS or Spring MVC annotations
|
||||||
|
|
||||||
|
## Getting Started
|
||||||
|
|
||||||
|
|
||||||
|
```java
|
||||||
|
@SpringBootApplication
|
||||||
|
@EnableFeignClients
|
||||||
|
public class WebApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(WebApplication.class, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FeignClient("name")
|
||||||
|
static interface NameService {
|
||||||
|
@RequestMapping("/")
|
||||||
|
public String getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
@@ -1,11 +1,5 @@
|
|||||||
:github-tag: master
|
|
||||||
:github-repo: spring-cloud/spring-cloud-openfeign
|
|
||||||
:github-raw: http://raw.github.com/{github-repo}/{github-tag}
|
|
||||||
:github-code: http://github.com/{github-repo}/tree/{github-tag}
|
|
||||||
:all: {asterisk}{asterisk}
|
|
||||||
:nofooter:
|
|
||||||
:branch: master
|
|
||||||
= Spring Cloud OpenFeign
|
= Spring Cloud OpenFeign
|
||||||
|
include::_attributes.adoc[]
|
||||||
|
|
||||||
*{spring-cloud-version}*
|
*{spring-cloud-version}*
|
||||||
|
|
||||||
@@ -15,13 +9,19 @@ include::intro.adoc[]
|
|||||||
[[spring-cloud-feign]]
|
[[spring-cloud-feign]]
|
||||||
== Declarative REST Client: Feign
|
== Declarative REST Client: Feign
|
||||||
|
|
||||||
https://github.com/Netflix/feign[Feign] is a declarative web service client. It makes writing web service clients easier. To use Feign create an interface and annotate it. It has pluggable annotation support including Feign annotations and JAX-RS annotations. 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 integrates Ribbon and Eureka to provide a load balanced http client when using Feign.
|
https://github.com/OpenFeign/feign[Feign] is a declarative web service client.
|
||||||
|
It makes writing web service clients easier.
|
||||||
|
To use Feign create an interface and annotate it.
|
||||||
|
It has pluggable annotation support including Feign annotations and JAX-RS annotations.
|
||||||
|
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 integrates Ribbon and 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
|
||||||
|
|
||||||
To include Feign in your project use the starter with group `org.springframework.cloud`
|
To include Feign in your project use the starter with group `org.springframework.cloud`
|
||||||
and artifact id `spring-cloud-starter-openfeign`. See the http://projects.spring.io/spring-cloud/[Spring Cloud Project page]
|
and artifact id `spring-cloud-starter-openfeign`. See the https://projects.spring.io/spring-cloud/[Spring Cloud Project page]
|
||||||
for details on setting up your build system with the current Spring Cloud Release Train.
|
for details on setting up your build system with the current Spring Cloud Release Train.
|
||||||
|
|
||||||
Example spring boot app
|
Example spring boot app
|
||||||
@@ -52,27 +52,31 @@ public interface StoreClient {
|
|||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
In the `@FeignClient` annotation the String value ("stores" above) is
|
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].
|
||||||
an arbitrary client name, which is used to create a Ribbon load
|
You can also specify a URL using the `url` attribute
|
||||||
balancer (see <<spring-cloud-ribbon,below for details of Ribbon
|
|
||||||
support>>). 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.
|
||||||
To specify your own alias value you can use the `qualifier` value
|
To specify your own alias value you can use the `qualifier` value
|
||||||
of the `@FeignClient` annotation.
|
of the `@FeignClient` annotation.
|
||||||
|
|
||||||
The Ribbon client above will want to discover the physical addresses
|
The load-balancer client above will want to discover the physical addresses
|
||||||
for the "stores" service. If your application is a Eureka client then
|
for the "stores" service. If your application is a Eureka client then
|
||||||
it will resolve the service in the Eureka service registry. If you
|
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 (see
|
in your external configuration (see
|
||||||
<<spring-cloud-ribbon-without-eureka,above for example>>).
|
<<spring-cloud-ribbon-without-eureka,above for example>>).
|
||||||
|
|
||||||
|
WARNING: In order to maintain backward compatibility, is used as the default load-balancer implementation.
|
||||||
|
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
|
||||||
|
|
||||||
A central concept in Spring Cloud's Feign support is that of the named client. Each feign client is part of an ensemble of components that work together to contact a remote server on demand, and the ensemble has a name that you give it as an application developer using the `@FeignClient` annotation. Spring Cloud creates a new ensemble as an
|
A central concept in Spring Cloud's Feign support is that of the named client. Each feign client is part of an ensemble of components that work together to contact a remote server on demand, and the ensemble has a name that you give it as an application developer using the `@FeignClient` annotation. Spring Cloud creates a new ensemble as an
|
||||||
`ApplicationContext` on demand for each named client using `FeignClientsConfiguration`. This contains (amongst other things) an `feign.Decoder`, a `feign.Encoder`, and a `feign.Contract`.
|
`ApplicationContext` on demand for each named client using `FeignClientsConfiguration`. This contains (amongst other things) an `feign.Decoder`, a `feign.Encoder`, and a `feign.Contract`.
|
||||||
|
It is possible to override the name of that ensemble by using the `contextId`
|
||||||
|
attribute of the `@FeignClient` annotation.
|
||||||
|
|
||||||
Spring Cloud lets you take full control of the feign client by declaring additional configuration (on top of the `FeignClientsConfiguration`) using `@FeignClient`. Example:
|
Spring Cloud lets you take full control of the feign client by declaring additional configuration (on top of the `FeignClientsConfiguration`) using `@FeignClient`. Example:
|
||||||
|
|
||||||
@@ -90,6 +94,10 @@ NOTE: `FooConfiguration` does not need to be annotated with `@Configuration`. Ho
|
|||||||
|
|
||||||
NOTE: The `serviceId` attribute is now deprecated in favor of the `name` attribute.
|
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
|
||||||
|
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.
|
||||||
|
|
||||||
WARNING: Previously, using the `url` attribute, did not require the `name` attribute. Using `name` is now required.
|
WARNING: Previously, using the `url` attribute, did not require the `name` attribute. Using `name` is now required.
|
||||||
|
|
||||||
Placeholders are supported in the `name` and `url` attributes.
|
Placeholders are supported in the `name` and `url` attributes.
|
||||||
@@ -109,10 +117,13 @@ Spring Cloud Netflix provides the following beans by default for feign (`BeanTyp
|
|||||||
* `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 enabled it is a `LoadBalancerFeignClient`, otherwise the default feign client is used.
|
* `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.
|
||||||
|
If none of them is in the classpath, the default feign client is used.
|
||||||
|
|
||||||
|
NOTE: `spring-cloud-starter-openfeign` contains both `spring-cloud-starter-netflix-ribbon` and `spring-cloud-starter-loadbalancer`.
|
||||||
|
|
||||||
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 `ClosableHttpClient` when using Apache or `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.
|
||||||
|
|
||||||
Spring Cloud Netflix _does not_ provide the following beans by default for feign, but still looks up beans of these types from the application context to create the feign client:
|
Spring Cloud Netflix _does not_ provide the following beans by default for feign, but still looks up beans of these types from the application context to create the feign client:
|
||||||
|
|
||||||
@@ -122,6 +133,7 @@ Spring Cloud Netflix _does not_ provide the following beans by default for feign
|
|||||||
* `Request.Options`
|
* `Request.Options`
|
||||||
* `Collection<RequestInterceptor>`
|
* `Collection<RequestInterceptor>`
|
||||||
* `SetterFactory`
|
* `SetterFactory`
|
||||||
|
* `QueryMapEncoder`
|
||||||
|
|
||||||
Creating a bean of one of those type and placing it in a `@FeignClient` configuration (such as `FooConfiguration` above) allows you to override each one of the beans described. Example:
|
Creating a bean of one of those type and placing it in a `@FeignClient` configuration (such as `FooConfiguration` above) allows you to override each one of the beans described. Example:
|
||||||
|
|
||||||
@@ -206,6 +218,27 @@ hystrix:
|
|||||||
strategy: SEMAPHORE
|
strategy: SEMAPHORE
|
||||||
----
|
----
|
||||||
|
|
||||||
|
If we want to create multiple feign clients with the same name or url
|
||||||
|
so that they would point to the same server but each with a different custom configuration then
|
||||||
|
we have to use `contextId` attribute of the `@FeignClient` in order to avoid name
|
||||||
|
collision of these configuration beans.
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
@FeignClient(contextId = "fooClient", name = "stores", configuration = FooConfiguration.class)
|
||||||
|
public interface FooClient {
|
||||||
|
//..
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
@FeignClient(contextId = "barClient", name = "stores", configuration = BarConfiguration.class)
|
||||||
|
public interface BarClient {
|
||||||
|
//..
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
=== 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
|
||||||
@@ -230,14 +263,14 @@ class FooController {
|
|||||||
.decoder(decoder)
|
.decoder(decoder)
|
||||||
.contract(contract)
|
.contract(contract)
|
||||||
.requestInterceptor(new BasicAuthRequestInterceptor("user", "user"))
|
.requestInterceptor(new BasicAuthRequestInterceptor("user", "user"))
|
||||||
.target(FooClient.class, "http://PROD-SVC");
|
.target(FooClient.class, "https://PROD-SVC");
|
||||||
|
|
||||||
this.adminClient = Feign.builder().client(client)
|
this.adminClient = Feign.builder().client(client)
|
||||||
.encoder(encoder)
|
.encoder(encoder)
|
||||||
.decoder(decoder)
|
.decoder(decoder)
|
||||||
.contract(contract)
|
.contract(contract)
|
||||||
.requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
|
.requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
|
||||||
.target(FooClient.class, "http://PROD-SVC");
|
.target(FooClient.class, "https://PROD-SVC");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
@@ -396,6 +429,14 @@ feign.compression.request.min-request-size=2048
|
|||||||
|
|
||||||
These properties allow you to be selective about the compressed media types and minimum request threshold length.
|
These properties allow you to be selective about the compressed media types and minimum request threshold length.
|
||||||
|
|
||||||
|
For http clients except OkHttpClient, default gzip decoder can be enabled to decode gzip response in ISO-8859-1 encoding:
|
||||||
|
|
||||||
|
[source,java]
|
||||||
|
---
|
||||||
|
feign.compression.response.enabled=true
|
||||||
|
feign.compression.response.useGzipDecoder=true
|
||||||
|
---
|
||||||
|
|
||||||
=== Feign logging
|
=== Feign logging
|
||||||
|
|
||||||
A logger is created for each Feign client created. By default the name of the logger is the full class name of the interface used to create the Feign client. Feign logging only responds to the `DEBUG` level.
|
A logger is created for each Feign client created. By default the name of the logger is the full class name of the interface used to create the Feign client. Feign logging only responds to the `DEBUG` level.
|
||||||
@@ -425,4 +466,64 @@ public class FooConfiguration {
|
|||||||
return Logger.Level.FULL;
|
return Logger.Level.FULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
----
|
----
|
||||||
|
|
||||||
|
=== Feign @QueryMap support
|
||||||
|
|
||||||
|
The OpenFeign `@QueryMap` annotation provides support for POJOs to be used as
|
||||||
|
GET parameter maps. Unfortunately, the default OpenFeign QueryMap annotation is
|
||||||
|
incompatible with Spring because it lacks a `value` property.
|
||||||
|
|
||||||
|
Spring Cloud OpenFeign provides an equivalent `@SpringQueryMap` annotation, which
|
||||||
|
is used to annotate a POJO or Map parameter as a query parameter map.
|
||||||
|
|
||||||
|
For example, the `Params` class defines parameters `param1` and `param2`:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
// Params.java
|
||||||
|
public class Params {
|
||||||
|
private String param1;
|
||||||
|
private String param2;
|
||||||
|
|
||||||
|
// [Getters and setters omitted for brevity]
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
The following feign client uses the `Params` class by using the `@SpringQueryMap` annotation:
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
@FeignClient("demo")
|
||||||
|
public interface DemoTemplate {
|
||||||
|
|
||||||
|
@GetMapping(path = "/demo")
|
||||||
|
String demoEndpoint(@SpringQueryMap Params params);
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
If you need more control over the generated query parameter map, you can implement a custom `QueryMapEncoder` bean.
|
||||||
|
|
||||||
|
=== HATEOAS support
|
||||||
|
|
||||||
|
Spring provides some APIs to create REST representations that follow the https://en.wikipedia.org/wiki/HATEOAS[HATEOAS] principle, https://spring.io/projects/spring-hateoas[Spring Hateoas] and https://spring.io/projects/spring-data-rest[Spring Data REST].
|
||||||
|
|
||||||
|
If your project use the `org.springframework.boot:spring-boot-starter-hateoas` starter
|
||||||
|
or the `org.springframework.boot:spring-boot-starter-data-rest` starter, Feign HATEOAS support is enabled by default.
|
||||||
|
|
||||||
|
When HATEOAS support is enabled, Feign clients are allowed to serialize
|
||||||
|
and deserialize HATEOAS representation models: https://docs.spring.io/spring-hateoas/docs/1.0.0.M1/apidocs/org/springframework/hateoas/EntityModel.html[EntityModel], https://docs.spring.io/spring-hateoas/docs/1.0.0.M1/apidocs/org/springframework/hateoas/CollectionModel.html[CollectionModel] and https://docs.spring.io/spring-hateoas/docs/1.0.0.M1/apidocs/org/springframework/hateoas/PagedModel.html[PagedModel].
|
||||||
|
|
||||||
|
[source,java,indent=0]
|
||||||
|
----
|
||||||
|
@FeignClient("demo")
|
||||||
|
public interface DemoTemplate {
|
||||||
|
|
||||||
|
@GetMapping(path = "/stores")
|
||||||
|
CollectionModel<Store> getStores();
|
||||||
|
}
|
||||||
|
----
|
||||||
|
|
||||||
|
== Configuration properties
|
||||||
|
|
||||||
|
To see the list of all Sleuth related configuration properties please check link:appendix.html[the Appendix page].
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
# "License"); you may not use this file except in compliance
|
# "License"); you may not use this file except in compliance
|
||||||
# with the License. You may obtain a copy of the License at
|
# with the License. You may obtain a copy of the License at
|
||||||
#
|
#
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
# https://www.apache.org/licenses/LICENSE-2.0
|
||||||
#
|
#
|
||||||
# Unless required by applicable law or agreed to in writing,
|
# Unless required by applicable law or agreed to in writing,
|
||||||
# software distributed under the License is distributed on an
|
# software distributed under the License is distributed on an
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
@REM "License"); you may not use this file except in compliance
|
@REM "License"); you may not use this file except in compliance
|
||||||
@REM with the License. You may obtain a copy of the License at
|
@REM with the License. You may obtain a copy of the License at
|
||||||
@REM
|
@REM
|
||||||
@REM http://www.apache.org/licenses/LICENSE-2.0
|
@REM https://www.apache.org/licenses/LICENSE-2.0
|
||||||
@REM
|
@REM
|
||||||
@REM Unless required by applicable law or agreed to in writing,
|
@REM Unless required by applicable law or agreed to in writing,
|
||||||
@REM software distributed under the License is distributed on an
|
@REM software distributed under the License is distributed on an
|
||||||
|
|||||||
@@ -1,29 +1,33 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
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.0.3.BUILD-SNAPSHOT</version>
|
<version>2.2.0.RC2</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.0.4.RELEASE</version>
|
<version>2.2.0.RC2</version>
|
||||||
<relativePath />
|
<relativePath/>
|
||||||
</parent>
|
</parent>
|
||||||
<scm>
|
<scm>
|
||||||
<url>https://github.com/spring-cloud/spring-cloud-openfeign</url>
|
<url>https://github.com/spring-cloud/spring-cloud-openfeign</url>
|
||||||
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-openfeign.git</connection>
|
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-openfeign.git
|
||||||
<developerConnection>scm:git:ssh://git@github.com/spring-cloud/spring-cloud-openfeign.git</developerConnection>
|
</connection>
|
||||||
|
<developerConnection>
|
||||||
|
scm:git:ssh://git@github.com/spring-cloud/spring-cloud-openfeign.git
|
||||||
|
</developerConnection>
|
||||||
<tag>HEAD</tag>
|
<tag>HEAD</tag>
|
||||||
</scm>
|
</scm>
|
||||||
<properties>
|
<properties>
|
||||||
<main.basedir>${basedir}</main.basedir>
|
<main.basedir>${basedir}</main.basedir>
|
||||||
<jackson.version>2.7.3</jackson.version>
|
<jackson.version>2.7.3</jackson.version>
|
||||||
<spring-cloud-commons.version>2.0.2.BUILD-SNAPSHOT</spring-cloud-commons.version>
|
<spring-cloud-commons.version>2.2.0.RC2</spring-cloud-commons.version>
|
||||||
<spring-cloud-netflix.version>2.0.2.BUILD-SNAPSHOT</spring-cloud-netflix.version>
|
<spring-cloud-netflix.version>2.2.0.RC2</spring-cloud-netflix.version>
|
||||||
|
|
||||||
<!-- Plugin versions -->
|
<!-- Plugin versions -->
|
||||||
<maven-compiler-plugin.version>3.6.1</maven-compiler-plugin.version>
|
<maven-compiler-plugin.version>3.6.1</maven-compiler-plugin.version>
|
||||||
@@ -32,11 +36,16 @@
|
|||||||
<surefire.plugin.version>2.19.1</surefire.plugin.version>
|
<surefire.plugin.version>2.19.1</surefire.plugin.version>
|
||||||
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
|
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
|
||||||
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
|
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
|
||||||
<sonar.jacoco.reportPath>${project.basedir}/../target/jacoco.exec</sonar.jacoco.reportPath>
|
<sonar.jacoco.reportPath>${project.basedir}/../target/jacoco.exec
|
||||||
|
</sonar.jacoco.reportPath>
|
||||||
<sonar.language>java</sonar.language>
|
<sonar.language>java</sonar.language>
|
||||||
</properties>
|
</properties>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.codehaus.mojo</groupId>
|
||||||
|
<artifactId>flatten-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-eclipse-plugin</artifactId>
|
<artifactId>maven-eclipse-plugin</artifactId>
|
||||||
@@ -46,11 +55,13 @@
|
|||||||
<additionalConfig>
|
<additionalConfig>
|
||||||
<file>
|
<file>
|
||||||
<name>.settings/org.eclipse.jdt.ui.prefs</name>
|
<name>.settings/org.eclipse.jdt.ui.prefs</name>
|
||||||
<location>${main.basedir}/eclipse/org.eclipse.jdt.ui.prefs</location>
|
<location>${main.basedir}/eclipse/org.eclipse.jdt.ui.prefs
|
||||||
|
</location>
|
||||||
</file>
|
</file>
|
||||||
<file>
|
<file>
|
||||||
<name>.settings/org.eclipse.jdt.core.prefs</name>
|
<name>.settings/org.eclipse.jdt.core.prefs</name>
|
||||||
<location>${main.basedir}/eclipse/org.eclipse.jdt.core.prefs</location>
|
<location>${main.basedir}/eclipse/org.eclipse.jdt.core.prefs
|
||||||
|
</location>
|
||||||
</file>
|
</file>
|
||||||
</additionalConfig>
|
</additionalConfig>
|
||||||
</configuration>
|
</configuration>
|
||||||
@@ -63,8 +74,26 @@
|
|||||||
<target>1.8</target>
|
<target>1.8</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>io.spring.javaformat</groupId>
|
||||||
|
<artifactId>spring-javaformat-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
|
|
||||||
|
<reporting>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</reporting>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
@@ -106,7 +135,7 @@
|
|||||||
<module>spring-cloud-openfeign-core</module>
|
<module>spring-cloud-openfeign-core</module>
|
||||||
<module>spring-cloud-starter-openfeign</module>
|
<module>spring-cloud-starter-openfeign</module>
|
||||||
<module>docs</module>
|
<module>docs</module>
|
||||||
</modules>
|
</modules>
|
||||||
<profiles>
|
<profiles>
|
||||||
<profile>
|
<profile>
|
||||||
<id>spring</id>
|
<id>spring</id>
|
||||||
@@ -184,7 +213,8 @@
|
|||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
<configuration>
|
||||||
<propertyName>surefireArgLine</propertyName>
|
<propertyName>surefireArgLine</propertyName>
|
||||||
<destFile>${project.build.directory}/jacoco.exec</destFile>
|
<destFile>${project.build.directory}/jacoco.exec
|
||||||
|
</destFile>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
<execution>
|
<execution>
|
||||||
@@ -195,7 +225,8 @@
|
|||||||
</goals>
|
</goals>
|
||||||
<configuration>
|
<configuration>
|
||||||
<!-- Sets the path to the file which contains the execution data. -->
|
<!-- Sets the path to the file which contains the execution data. -->
|
||||||
<dataFile>${project.build.directory}/jacoco.exec</dataFile>
|
<dataFile>${project.build.directory}/jacoco.exec
|
||||||
|
</dataFile>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
|
|||||||
@@ -0,0 +1,392 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-openfeign</artifactId>
|
||||||
|
<version>2.2.0.RC2</version>
|
||||||
|
<relativePath>..</relativePath>
|
||||||
|
</parent>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-openfeign-core</artifactId>
|
||||||
|
<version>2.2.0.RC2</version>
|
||||||
|
<name>Spring Cloud OpenFeign Core</name>
|
||||||
|
<description>Spring Cloud OpenFeign Core</description>
|
||||||
|
<url>https://spring.io/spring-cloud/spring-cloud-openfeign/spring-cloud-openfeign-core</url>
|
||||||
|
<organization>
|
||||||
|
<name>Pivotal Software, Inc.</name>
|
||||||
|
<url>https://www.spring.io</url>
|
||||||
|
</organization>
|
||||||
|
<licenses>
|
||||||
|
<license>
|
||||||
|
<name>Apache License, Version 2.0</name>
|
||||||
|
<url>https://www.apache.org/licenses/LICENSE-2.0</url>
|
||||||
|
<comments>Copyright 2014-2015 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.</comments>
|
||||||
|
</license>
|
||||||
|
</licenses>
|
||||||
|
<developers>
|
||||||
|
<developer>
|
||||||
|
<id>dsyer</id>
|
||||||
|
<name>Dave Syer</name>
|
||||||
|
<email>dsyer at pivotal.io</email>
|
||||||
|
<organization>Pivotal Software, Inc.</organization>
|
||||||
|
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||||
|
<roles>
|
||||||
|
<role>lead</role>
|
||||||
|
</roles>
|
||||||
|
</developer>
|
||||||
|
<developer>
|
||||||
|
<id>sgibb</id>
|
||||||
|
<name>Spencer Gibb</name>
|
||||||
|
<email>sgibb at pivotal.io</email>
|
||||||
|
<organization>Pivotal Software, Inc.</organization>
|
||||||
|
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||||
|
<roles>
|
||||||
|
<role>lead</role>
|
||||||
|
</roles>
|
||||||
|
</developer>
|
||||||
|
<developer>
|
||||||
|
<id>mgrzejszczak</id>
|
||||||
|
<name>Marcin Grzejszczak</name>
|
||||||
|
<email>mgrzejszczak at pivotal.io</email>
|
||||||
|
<organization>Pivotal Software, Inc.</organization>
|
||||||
|
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||||
|
<roles>
|
||||||
|
<role>developer</role>
|
||||||
|
</roles>
|
||||||
|
</developer>
|
||||||
|
<developer>
|
||||||
|
<id>rbaxter</id>
|
||||||
|
<name>Ryan Baxter</name>
|
||||||
|
<email>rbaxter at pivotal.io</email>
|
||||||
|
<organization>Pivotal Software, Inc.</organization>
|
||||||
|
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||||
|
<roles>
|
||||||
|
<role>developer</role>
|
||||||
|
</roles>
|
||||||
|
</developer>
|
||||||
|
<developer>
|
||||||
|
<id>omaciaszeksharma</id>
|
||||||
|
<name>Olga Maciaszek-Sharma</name>
|
||||||
|
<email>omaciaszeksharma at pivotal.io</email>
|
||||||
|
<organization>Pivotal Software, Inc.</organization>
|
||||||
|
<organizationUrl>https://www.spring.io</organizationUrl>
|
||||||
|
<roles>
|
||||||
|
<role>developer</role>
|
||||||
|
</roles>
|
||||||
|
</developer>
|
||||||
|
</developers>
|
||||||
|
<scm>
|
||||||
|
<connection>scm:git:git://github.com/spring-cloud/spring-cloud-openfeign.git/spring-cloud-openfeign-core</connection>
|
||||||
|
<developerConnection>scm:git:ssh://git@github.com/spring-cloud/spring-cloud-openfeign.git/spring-cloud-openfeign-core</developerConnection>
|
||||||
|
<url>https://github.com/spring-cloud/spring-cloud-openfeign/spring-cloud-openfeign-core</url>
|
||||||
|
</scm>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||||
|
<version>2.2.1.RELEASE</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-netflix-ribbon</artifactId>
|
||||||
|
<version>2.2.0.RC2</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
|
<version>2.2.1.RELEASE</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-security</artifactId>
|
||||||
|
<version>2.2.1.RELEASE</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<version>2.2.1.RELEASE</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||||
|
<version>2.2.1.RELEASE</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-reactor-netty</artifactId>
|
||||||
|
<version>2.2.1.RELEASE</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-hateoas</artifactId>
|
||||||
|
<version>2.2.1.RELEASE</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.netty</groupId>
|
||||||
|
<artifactId>netty-codec-http</artifactId>
|
||||||
|
<version>4.1.43.Final</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.projectreactor</groupId>
|
||||||
|
<artifactId>reactor-core</artifactId>
|
||||||
|
<version>3.3.0.RELEASE</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.reactivex</groupId>
|
||||||
|
<artifactId>rxjava-reactive-streams</artifactId>
|
||||||
|
<version>1.2.1</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.retry</groupId>
|
||||||
|
<artifactId>spring-retry</artifactId>
|
||||||
|
<version>1.2.4.RELEASE</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-aop</artifactId>
|
||||||
|
<version>2.2.1.RELEASE</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-commons</artifactId>
|
||||||
|
<version>2.2.0.RC2</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-context</artifactId>
|
||||||
|
<version>2.2.0.RC2</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.netflix.ribbon</groupId>
|
||||||
|
<artifactId>ribbon-loadbalancer</artifactId>
|
||||||
|
<version>2.3.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>annotations</artifactId>
|
||||||
|
<groupId>com.google.code.findbugs</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.github.openfeign</groupId>
|
||||||
|
<artifactId>feign-core</artifactId>
|
||||||
|
<version>10.4.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.github.openfeign.form</groupId>
|
||||||
|
<artifactId>feign-form-spring</artifactId>
|
||||||
|
<version>3.8.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.github.openfeign</groupId>
|
||||||
|
<artifactId>feign-slf4j</artifactId>
|
||||||
|
<version>10.4.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.github.openfeign</groupId>
|
||||||
|
<artifactId>feign-httpclient</artifactId>
|
||||||
|
<version>10.4.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.github.openfeign</groupId>
|
||||||
|
<artifactId>feign-hystrix</artifactId>
|
||||||
|
<version>10.4.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.github.openfeign</groupId>
|
||||||
|
<artifactId>feign-okhttp</artifactId>
|
||||||
|
<version>10.4.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.github.openfeign</groupId>
|
||||||
|
<artifactId>feign-java8</artifactId>
|
||||||
|
<version>10.4.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.netflix.hystrix</groupId>
|
||||||
|
<artifactId>hystrix-core</artifactId>
|
||||||
|
<version>1.5.18</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>annotations</artifactId>
|
||||||
|
<groupId>com.google.code.findbugs</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.netflix.hystrix</groupId>
|
||||||
|
<artifactId>hystrix-serialization</artifactId>
|
||||||
|
<version>1.5.18</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>annotations</artifactId>
|
||||||
|
<groupId>com.google.code.findbugs</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.netflix.hystrix</groupId>
|
||||||
|
<artifactId>hystrix-metrics-event-stream</artifactId>
|
||||||
|
<version>1.5.18</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>servlet-api</artifactId>
|
||||||
|
<groupId>javax.servlet</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.netflix.hystrix</groupId>
|
||||||
|
<artifactId>hystrix-javanica</artifactId>
|
||||||
|
<version>1.5.18</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>jsr305</artifactId>
|
||||||
|
<groupId>com.google.code.findbugs</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>annotations</artifactId>
|
||||||
|
<groupId>com.google.code.findbugs</groupId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>aspectjrt</artifactId>
|
||||||
|
<groupId>org.aspectj</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.netflix.ribbon</groupId>
|
||||||
|
<artifactId>ribbon-core</artifactId>
|
||||||
|
<version>2.3.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>annotations</artifactId>
|
||||||
|
<groupId>com.google.code.findbugs</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.netflix.ribbon</groupId>
|
||||||
|
<artifactId>ribbon-httpclient</artifactId>
|
||||||
|
<version>2.3.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<artifactId>annotations</artifactId>
|
||||||
|
<groupId>com.google.code.findbugs</groupId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.reactivex</groupId>
|
||||||
|
<artifactId>rxjava</artifactId>
|
||||||
|
<version>1.3.8</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.squareup.okhttp3</groupId>
|
||||||
|
<artifactId>okhttp</artifactId>
|
||||||
|
<version>3.14.4</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-autoconfigure-processor</artifactId>
|
||||||
|
<version>2.2.1.RELEASE</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||||
|
<version>2.2.1.RELEASE</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.data</groupId>
|
||||||
|
<artifactId>spring-data-commons</artifactId>
|
||||||
|
<version>2.2.1.RELEASE</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-loadbalancer</artifactId>
|
||||||
|
<version>2.2.0.RC2</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
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>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>org.springframework.cloud</groupId>
|
<groupId>org.springframework.cloud</groupId>
|
||||||
<artifactId>spring-cloud-openfeign</artifactId>
|
<artifactId>spring-cloud-openfeign</artifactId>
|
||||||
<version>2.0.3.BUILD-SNAPSHOT</version>
|
<version>2.2.0.RC2</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>
|
||||||
@@ -49,6 +50,11 @@
|
|||||||
<artifactId>spring-boot-starter-reactor-netty</artifactId>
|
<artifactId>spring-boot-starter-reactor-netty</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-hateoas</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.netty</groupId>
|
<groupId>io.netty</groupId>
|
||||||
<artifactId>netty-codec-http</artifactId>
|
<artifactId>netty-codec-http</artifactId>
|
||||||
@@ -157,11 +163,6 @@
|
|||||||
<artifactId>rxjava</artifactId>
|
<artifactId>rxjava</artifactId>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.sun.jersey.contribs</groupId>
|
|
||||||
<artifactId>jersey-apache-client4</artifactId>
|
|
||||||
<optional>true</optional>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.squareup.okhttp3</groupId>
|
<groupId>com.squareup.okhttp3</groupId>
|
||||||
<artifactId>okhttp</artifactId>
|
<artifactId>okhttp</artifactId>
|
||||||
@@ -187,6 +188,11 @@
|
|||||||
<artifactId>spring-cloud-test-support</artifactId>
|
<artifactId>spring-cloud-test-support</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.data</groupId>
|
||||||
|
<artifactId>spring-data-commons</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>io.projectreactor</groupId>
|
<groupId>io.projectreactor</groupId>
|
||||||
<artifactId>reactor-test</artifactId>
|
<artifactId>reactor-test</artifactId>
|
||||||
@@ -202,6 +208,11 @@
|
|||||||
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
|
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-data-rest</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.googlecode.protobuf-java-format</groupId>
|
<groupId>com.googlecode.protobuf-java-format</groupId>
|
||||||
<artifactId>protobuf-java-format</artifactId>
|
<artifactId>protobuf-java-format</artifactId>
|
||||||
@@ -214,22 +225,33 @@
|
|||||||
<version>3.4.0</version>
|
<version>3.4.0</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.vavr</groupId>
|
||||||
|
<artifactId>vavr</artifactId>
|
||||||
|
<version>0.10.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.cloud</groupId>
|
||||||
|
<artifactId>spring-cloud-loadbalancer</artifactId>
|
||||||
|
<optional>true</optional>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<profiles>
|
<profiles>
|
||||||
<profile>
|
<profile>
|
||||||
<id>java8plus</id>
|
<id>java8plus</id>
|
||||||
<activation>
|
<activation>
|
||||||
<jdk>[1.8,2.0)</jdk>
|
<jdk>[1.8,2.0)</jdk>
|
||||||
</activation>
|
</activation>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<configuration>
|
<configuration>
|
||||||
<compilerArgs>
|
<compilerArgs>
|
||||||
<arg>-parameters</arg>
|
<arg>-parameters</arg>
|
||||||
</compilerArgs>
|
</compilerArgs>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
|
|||||||
+8
-11
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2017 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -32,20 +32,19 @@ public interface AnnotatedParameterProcessor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the processor supported annotation type.
|
* Retrieves the processor supported annotation type.
|
||||||
*
|
|
||||||
* @return the annotation type
|
* @return the annotation type
|
||||||
*/
|
*/
|
||||||
Class<? extends Annotation> getAnnotationType();
|
Class<? extends Annotation> getAnnotationType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process the annotated parameter.
|
* Process the annotated parameter.
|
||||||
*
|
* @param context the parameter context
|
||||||
* @param context the parameter context
|
|
||||||
* @param annotation the annotation instance
|
* @param annotation the annotation instance
|
||||||
* @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, Method method);
|
boolean processArgument(AnnotatedParameterContext context, Annotation annotation,
|
||||||
|
Method method);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the parameter context.
|
* Specifies the parameter context.
|
||||||
@@ -56,32 +55,30 @@ public interface AnnotatedParameterProcessor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the method metadata.
|
* Retrieves the method metadata.
|
||||||
*
|
|
||||||
* @return the method metadata
|
* @return the method metadata
|
||||||
*/
|
*/
|
||||||
MethodMetadata getMethodMetadata();
|
MethodMetadata getMethodMetadata();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the index of the parameter.
|
* Retrieves the index of the parameter.
|
||||||
*
|
|
||||||
* @return the parameter index
|
* @return the parameter index
|
||||||
*/
|
*/
|
||||||
int getParameterIndex();
|
int getParameterIndex();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the parameter name.
|
* Sets the parameter name.
|
||||||
*
|
|
||||||
* @param name the name of the parameter
|
* @param name the name of the parameter
|
||||||
*/
|
*/
|
||||||
void setParameterName(String name);
|
void setParameterName(String name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the template parameter.
|
* Sets the template parameter.
|
||||||
*
|
|
||||||
* @param name the template parameter
|
* @param name the template parameter
|
||||||
* @param rest the existing parameter values
|
* @param rest the existing parameter values
|
||||||
* @return parameters
|
* @return parameters
|
||||||
*/
|
*/
|
||||||
Collection<String> setTemplateParameter(String name, Collection<String> rest);
|
Collection<String> setTemplateParameter(String name, Collection<String> rest);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 the original author or authors.
|
* Copyright 2016-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
|||||||
+5
-5
@@ -1,18 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2016 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
package org.springframework.cloud.openfeign;
|
||||||
@@ -26,8 +25,9 @@ import feign.Target;
|
|||||||
class DefaultTargeter implements Targeter {
|
class DefaultTargeter implements Targeter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
|
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
|
||||||
Target.HardCodedTarget<T> target) {
|
FeignContext context, Target.HardCodedTarget<T> target) {
|
||||||
return feign.target(target);
|
return feign.target(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-10
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2015 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -25,10 +25,11 @@ import java.lang.annotation.Target;
|
|||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scans for interfaces that declare they are feign clients (via {@link FeignClient
|
* Scans for interfaces that declare they are feign clients (via
|
||||||
* <code>@FeignClient</code>}). Configures component scanning directives for use with
|
* {@link org.springframework.cloud.openfeign.FeignClient} <code>@FeignClient</code>).
|
||||||
* {@link org.springframework.context.annotation.Configuration
|
* Configures component scanning directives for use with
|
||||||
* <code>@Configuration</code>} classes.
|
* {@link org.springframework.context.annotation.Configuration}
|
||||||
|
* <code>@Configuration</code> classes.
|
||||||
*
|
*
|
||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
@@ -55,7 +56,6 @@ public @interface EnableFeignClients {
|
|||||||
* <p>
|
* <p>
|
||||||
* Use {@link #basePackageClasses()} for a type-safe alternative to String-based
|
* Use {@link #basePackageClasses()} for a type-safe alternative to String-based
|
||||||
* package names.
|
* package names.
|
||||||
*
|
|
||||||
* @return the array of 'basePackages'.
|
* @return the array of 'basePackages'.
|
||||||
*/
|
*/
|
||||||
String[] basePackages() default {};
|
String[] basePackages() default {};
|
||||||
@@ -66,7 +66,6 @@ public @interface EnableFeignClients {
|
|||||||
* <p>
|
* <p>
|
||||||
* Consider creating a special no-op marker class or interface in each package that
|
* Consider creating a special no-op marker class or interface in each package that
|
||||||
* serves no purpose other than being referenced by this attribute.
|
* serves no purpose other than being referenced by this attribute.
|
||||||
*
|
|
||||||
* @return the array of 'basePackageClasses'.
|
* @return the array of 'basePackageClasses'.
|
||||||
*/
|
*/
|
||||||
Class<?>[] basePackageClasses() default {};
|
Class<?>[] basePackageClasses() default {};
|
||||||
@@ -77,12 +76,15 @@ public @interface EnableFeignClients {
|
|||||||
* {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
|
* {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
|
||||||
*
|
*
|
||||||
* @see FeignClientsConfiguration for the defaults
|
* @see FeignClientsConfiguration for the defaults
|
||||||
|
* @return list of default configurations
|
||||||
*/
|
*/
|
||||||
Class<?>[] defaultConfiguration() default {};
|
Class<?>[] defaultConfiguration() default {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of classes annotated with @FeignClient. If not empty, disables classpath scanning.
|
* List of classes annotated with @FeignClient. If not empty, disables classpath
|
||||||
* @return
|
* scanning.
|
||||||
|
* @return list of FeignClient classes
|
||||||
*/
|
*/
|
||||||
Class<?>[] clients() default {};
|
Class<?>[] clients() default {};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+45
-30
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2015 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -22,11 +22,19 @@ import java.util.Timer;
|
|||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.annotation.PreDestroy;
|
||||||
|
|
||||||
|
import feign.Client;
|
||||||
|
import feign.Feign;
|
||||||
|
import feign.httpclient.ApacheHttpClient;
|
||||||
|
import feign.okhttp.OkHttpClient;
|
||||||
|
import okhttp3.ConnectionPool;
|
||||||
import org.apache.http.client.HttpClient;
|
import org.apache.http.client.HttpClient;
|
||||||
import org.apache.http.client.config.RequestConfig;
|
import org.apache.http.client.config.RequestConfig;
|
||||||
import org.apache.http.config.RegistryBuilder;
|
import org.apache.http.config.RegistryBuilder;
|
||||||
import org.apache.http.conn.HttpClientConnectionManager;
|
import org.apache.http.conn.HttpClientConnectionManager;
|
||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
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;
|
||||||
@@ -38,17 +46,11 @@ import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionMa
|
|||||||
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
|
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
|
||||||
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
|
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
|
||||||
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
|
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
|
||||||
|
import org.springframework.cloud.openfeign.support.DefaultGzipDecoderConfiguration;
|
||||||
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
|
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
|
||||||
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 feign.Client;
|
|
||||||
import feign.Feign;
|
|
||||||
import feign.httpclient.ApacheHttpClient;
|
|
||||||
import feign.okhttp.OkHttpClient;
|
|
||||||
import okhttp3.ConnectionPool;
|
|
||||||
|
|
||||||
import javax.annotation.PreDestroy;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
@@ -56,7 +58,9 @@ import javax.annotation.PreDestroy;
|
|||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnClass(Feign.class)
|
@ConditionalOnClass(Feign.class)
|
||||||
@EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class})
|
@EnableConfigurationProperties({ FeignClientProperties.class,
|
||||||
|
FeignHttpClientProperties.class })
|
||||||
|
@Import(DefaultGzipDecoderConfiguration.class)
|
||||||
public class FeignAutoConfiguration {
|
public class FeignAutoConfiguration {
|
||||||
|
|
||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
@@ -77,21 +81,25 @@ public class FeignAutoConfiguration {
|
|||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
|
@ConditionalOnClass(name = "feign.hystrix.HystrixFeign")
|
||||||
protected static class HystrixFeignTargeterConfiguration {
|
protected static class HystrixFeignTargeterConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public Targeter feignTargeter() {
|
public Targeter feignTargeter() {
|
||||||
return new HystrixTargeter();
|
return new HystrixTargeter();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
|
@ConditionalOnMissingClass("feign.hystrix.HystrixFeign")
|
||||||
protected static class DefaultFeignTargeterConfiguration {
|
protected static class DefaultFeignTargeterConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public Targeter feignTargeter() {
|
public Targeter feignTargeter() {
|
||||||
return new DefaultTargeter();
|
return new DefaultTargeter();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// the following configuration is for alternate feign clients if
|
// the following configuration is for alternate feign clients if
|
||||||
@@ -104,6 +112,7 @@ public class FeignAutoConfiguration {
|
|||||||
@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 {
|
||||||
|
|
||||||
private final Timer connectionManagerTimer = new Timer(
|
private final Timer connectionManagerTimer = new Timer(
|
||||||
"FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
|
"FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
|
||||||
|
|
||||||
@@ -118,10 +127,12 @@ public class FeignAutoConfiguration {
|
|||||||
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
|
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
|
||||||
FeignHttpClientProperties httpClientProperties) {
|
FeignHttpClientProperties httpClientProperties) {
|
||||||
final HttpClientConnectionManager connectionManager = connectionManagerFactory
|
final HttpClientConnectionManager connectionManager = connectionManagerFactory
|
||||||
.newConnectionManager(httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(),
|
.newConnectionManager(httpClientProperties.isDisableSslValidation(),
|
||||||
|
httpClientProperties.getMaxConnections(),
|
||||||
httpClientProperties.getMaxConnectionsPerRoute(),
|
httpClientProperties.getMaxConnectionsPerRoute(),
|
||||||
httpClientProperties.getTimeToLive(),
|
httpClientProperties.getTimeToLive(),
|
||||||
httpClientProperties.getTimeToLiveUnit(), registryBuilder);
|
httpClientProperties.getTimeToLiveUnit(),
|
||||||
|
this.registryBuilder);
|
||||||
this.connectionManagerTimer.schedule(new TimerTask() {
|
this.connectionManagerTimer.schedule(new TimerTask() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -139,9 +150,9 @@ public class FeignAutoConfiguration {
|
|||||||
.setConnectTimeout(httpClientProperties.getConnectionTimeout())
|
.setConnectTimeout(httpClientProperties.getConnectionTimeout())
|
||||||
.setRedirectsEnabled(httpClientProperties.isFollowRedirects())
|
.setRedirectsEnabled(httpClientProperties.isFollowRedirects())
|
||||||
.build();
|
.build();
|
||||||
this.httpClient = httpClientFactory.createBuilder().
|
this.httpClient = httpClientFactory.createBuilder()
|
||||||
setConnectionManager(httpClientConnectionManager).
|
.setConnectionManager(httpClientConnectionManager)
|
||||||
setDefaultRequestConfig(defaultRequestConfig).build();
|
.setDefaultRequestConfig(defaultRequestConfig).build();
|
||||||
return this.httpClient;
|
return this.httpClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,26 +164,28 @@ public class FeignAutoConfiguration {
|
|||||||
|
|
||||||
@PreDestroy
|
@PreDestroy
|
||||||
public void destroy() throws Exception {
|
public void destroy() throws Exception {
|
||||||
connectionManagerTimer.cancel();
|
this.connectionManagerTimer.cancel();
|
||||||
if(httpClient != null) {
|
if (this.httpClient != null) {
|
||||||
httpClient.close();
|
this.httpClient.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnClass(OkHttpClient.class)
|
@ConditionalOnClass(OkHttpClient.class)
|
||||||
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
|
@ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer")
|
||||||
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
|
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
|
||||||
@ConditionalOnProperty(value = "feign.okhttp.enabled")
|
@ConditionalOnProperty("feign.okhttp.enabled")
|
||||||
protected static class OkHttpFeignConfiguration {
|
protected static class OkHttpFeignConfiguration {
|
||||||
|
|
||||||
private okhttp3.OkHttpClient okHttpClient;
|
private okhttp3.OkHttpClient okHttpClient;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean(ConnectionPool.class)
|
@ConditionalOnMissingBean(ConnectionPool.class)
|
||||||
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
|
public ConnectionPool httpClientConnectionPool(
|
||||||
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
|
FeignHttpClientProperties httpClientProperties,
|
||||||
|
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
|
||||||
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
|
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
|
||||||
Long timeToLive = httpClientProperties.getTimeToLive();
|
Long timeToLive = httpClientProperties.getTimeToLive();
|
||||||
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
|
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
|
||||||
@@ -181,22 +194,23 @@ public class FeignAutoConfiguration {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
|
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
|
||||||
ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
|
ConnectionPool connectionPool,
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PreDestroy
|
@PreDestroy
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
if(okHttpClient != null) {
|
if (this.okHttpClient != null) {
|
||||||
okHttpClient.dispatcher().executorService().shutdown();
|
this.okHttpClient.dispatcher().executorService().shutdown();
|
||||||
okHttpClient.connectionPool().evictAll();
|
this.okHttpClient.connectionPool().evictAll();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,6 +219,7 @@ public class FeignAutoConfiguration {
|
|||||||
public Client feignClient(okhttp3.OkHttpClient client) {
|
public Client feignClient(okhttp3.OkHttpClient client) {
|
||||||
return new OkHttpClient(client);
|
return new OkHttpClient(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-13
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2016 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -42,36 +42,45 @@ public @interface FeignClient {
|
|||||||
* The name of the service with optional protocol prefix. Synonym for {@link #name()
|
* The name of the service with optional protocol prefix. Synonym for {@link #name()
|
||||||
* name}. A name must be specified for all clients, whether or not a url is provided.
|
* name}. A name must be specified for all clients, whether or not a url is provided.
|
||||||
* Can be specified as property key, eg: ${propertyKey}.
|
* Can be specified as property key, eg: ${propertyKey}.
|
||||||
|
* @return the name of the service with optional protocol prefix
|
||||||
*/
|
*/
|
||||||
@AliasFor("name")
|
@AliasFor("name")
|
||||||
String value() default "";
|
String value() default "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The service id with optional protocol prefix. Synonym for {@link #value() value}.
|
* The service id with optional protocol prefix. Synonym for {@link #value() value}.
|
||||||
*
|
|
||||||
* @deprecated use {@link #name() name} instead
|
* @deprecated use {@link #name() name} instead
|
||||||
|
* @return the service id with optional protocol prefix
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
String serviceId() default "";
|
String serviceId() default "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The service id with optional protocol prefix. Synonym for {@link #value() value}.
|
* This will be used as the bean name instead of name if present, but will not be used
|
||||||
|
* as a service id.
|
||||||
|
* @return bean name instead of name if present
|
||||||
|
*/
|
||||||
|
String contextId() default "";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The service id with optional protocol prefix. Synonym for {@link #value()
|
||||||
|
* value}.
|
||||||
*/
|
*/
|
||||||
@AliasFor("value")
|
@AliasFor("value")
|
||||||
String name() default "";
|
String name() default "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the <code>@Qualifier</code> value for the feign client.
|
* @return the <code>@Qualifier</code> value for the feign client.
|
||||||
*/
|
*/
|
||||||
String qualifier() default "";
|
String qualifier() default "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An absolute URL or resolvable hostname (the protocol is optional).
|
* @return an absolute URL or resolvable hostname (the protocol is optional).
|
||||||
*/
|
*/
|
||||||
String url() default "";
|
String url() default "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether 404s should be decoded instead of throwing FeignExceptions
|
* @return whether 404s should be decoded instead of throwing FeignExceptions
|
||||||
*/
|
*/
|
||||||
boolean decode404() default false;
|
boolean decode404() default false;
|
||||||
|
|
||||||
@@ -81,33 +90,35 @@ public @interface FeignClient {
|
|||||||
* {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
|
* {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}.
|
||||||
*
|
*
|
||||||
* @see FeignClientsConfiguration for the defaults
|
* @see FeignClientsConfiguration for the defaults
|
||||||
|
* @return list of configurations for feign client
|
||||||
*/
|
*/
|
||||||
Class<?>[] configuration() default {};
|
Class<?>[] configuration() default {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fallback class for the specified Feign client interface. The fallback class must
|
* Fallback class for the specified Feign client interface. The fallback class must
|
||||||
* implement the interface annotated by this annotation and be a valid spring bean.
|
* implement the interface annotated by this annotation and be a valid spring bean.
|
||||||
|
* @return fallback class for the specified Feign client interface
|
||||||
*/
|
*/
|
||||||
Class<?> fallback() default void.class;
|
Class<?> fallback() default void.class;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define a fallback factory for the specified Feign client interface. The fallback
|
* Define a fallback factory for the specified Feign client interface. The fallback
|
||||||
* factory must produce instances of fallback classes that implement the interface
|
* factory must produce instances of fallback classes that implement the interface
|
||||||
* annotated by {@link FeignClient}. The fallback factory must be a valid spring
|
* annotated by {@link FeignClient}. The fallback factory must be a valid spring bean.
|
||||||
* bean.
|
|
||||||
*
|
*
|
||||||
* @see feign.hystrix.FallbackFactory for details.
|
* @see feign.hystrix.FallbackFactory for details.
|
||||||
|
* @return fallback factory for the specified Feign client interface
|
||||||
*/
|
*/
|
||||||
Class<?> fallbackFactory() default void.class;
|
Class<?> fallbackFactory() default void.class;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Path prefix to be used by all method-level mappings. Can be used with or without
|
* @return path prefix to be used by all method-level mappings. Can be used with or
|
||||||
* <code>@RibbonClient</code>.
|
* without <code>@RibbonClient</code>.
|
||||||
*/
|
*/
|
||||||
String path() default "";
|
String path() default "";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether to mark the feign proxy as a primary bean. Defaults to true.
|
* @return whether to mark the feign proxy as a primary bean. Defaults to true.
|
||||||
*/
|
*/
|
||||||
boolean primary() default true;
|
boolean primary() default true;
|
||||||
|
|
||||||
|
|||||||
+27
-13
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2018 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
package org.springframework.cloud.openfeign;
|
||||||
|
|
||||||
|
import feign.hystrix.FallbackFactory;
|
||||||
|
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -35,10 +37,15 @@ public class FeignClientBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public <T> Builder<T> forType(final Class<T> type, final String name) {
|
public <T> Builder<T> forType(final Class<T> type, final String name) {
|
||||||
return new Builder<>(applicationContext, type, name);
|
return new Builder<>(this.applicationContext, type, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder<T> {
|
/**
|
||||||
|
* Builder of feign targets.
|
||||||
|
*
|
||||||
|
* @param <T> type of target
|
||||||
|
*/
|
||||||
|
public static final class Builder<T> {
|
||||||
|
|
||||||
private FeignClientFactoryBean feignClientFactoryBean;
|
private FeignClientFactoryBean feignClientFactoryBean;
|
||||||
|
|
||||||
@@ -49,45 +56,52 @@ public class FeignClientBuilder {
|
|||||||
this.feignClientFactoryBean.setApplicationContext(applicationContext);
|
this.feignClientFactoryBean.setApplicationContext(applicationContext);
|
||||||
this.feignClientFactoryBean.setType(type);
|
this.feignClientFactoryBean.setType(type);
|
||||||
this.feignClientFactoryBean.setName(FeignClientsRegistrar.getName(name));
|
this.feignClientFactoryBean.setName(FeignClientsRegistrar.getName(name));
|
||||||
|
this.feignClientFactoryBean.setContextId(FeignClientsRegistrar.getName(name));
|
||||||
// preset default values - these values resemble the default values on the
|
// preset default values - these values resemble the default values on the
|
||||||
// FeignClient annotation
|
// FeignClient annotation
|
||||||
this.url("").path("").decode404(false).fallback(void.class)
|
this.url("").path("").decode404(false);
|
||||||
.fallbackFactory(void.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder url(final String url) {
|
public Builder<T> url(final String url) {
|
||||||
this.feignClientFactoryBean.setUrl(FeignClientsRegistrar.getUrl(url));
|
this.feignClientFactoryBean.setUrl(FeignClientsRegistrar.getUrl(url));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder path(final String path) {
|
public Builder<T> contextId(final String contextId) {
|
||||||
|
this.feignClientFactoryBean.setContextId(contextId);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder<T> path(final String path) {
|
||||||
this.feignClientFactoryBean.setPath(FeignClientsRegistrar.getPath(path));
|
this.feignClientFactoryBean.setPath(FeignClientsRegistrar.getPath(path));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder decode404(final boolean decode404) {
|
public Builder<T> decode404(final boolean decode404) {
|
||||||
this.feignClientFactoryBean.setDecode404(decode404);
|
this.feignClientFactoryBean.setDecode404(decode404);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder fallback(final Class<T> fallback) {
|
public Builder<T> fallback(final Class<? extends T> fallback) {
|
||||||
FeignClientsRegistrar.validateFallback(fallback);
|
FeignClientsRegistrar.validateFallback(fallback);
|
||||||
this.feignClientFactoryBean.setFallback(fallback);
|
this.feignClientFactoryBean.setFallback(fallback);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Builder fallbackFactory(final Class<T> fallbackFactory) {
|
public Builder<T> fallbackFactory(
|
||||||
|
final Class<? extends FallbackFactory<? extends T>> fallbackFactory) {
|
||||||
FeignClientsRegistrar.validateFallbackFactory(fallbackFactory);
|
FeignClientsRegistrar.validateFallbackFactory(fallbackFactory);
|
||||||
this.feignClientFactoryBean.setFallbackFactory(fallbackFactory);
|
this.feignClientFactoryBean.setFallbackFactory(fallbackFactory);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param <T> the target type of the Feign client to be created
|
|
||||||
* @return the created Feign client
|
* @return the created Feign client
|
||||||
*/
|
*/
|
||||||
public <T> T build() {
|
public T build() {
|
||||||
return this.feignClientFactoryBean.getTarget();
|
return this.feignClientFactoryBean.getTarget();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+118
-84
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2018 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -19,6 +19,19 @@ package org.springframework.cloud.openfeign;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import feign.Client;
|
||||||
|
import feign.Contract;
|
||||||
|
import feign.Feign;
|
||||||
|
import feign.Logger;
|
||||||
|
import feign.QueryMapEncoder;
|
||||||
|
import feign.Request;
|
||||||
|
import feign.RequestInterceptor;
|
||||||
|
import feign.Retryer;
|
||||||
|
import feign.Target.HardCodedTarget;
|
||||||
|
import feign.codec.Decoder;
|
||||||
|
import feign.codec.Encoder;
|
||||||
|
import feign.codec.ErrorDecoder;
|
||||||
|
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.beans.BeansException;
|
import org.springframework.beans.BeansException;
|
||||||
import org.springframework.beans.factory.FactoryBean;
|
import org.springframework.beans.factory.FactoryBean;
|
||||||
@@ -30,28 +43,18 @@ import org.springframework.context.ApplicationContextAware;
|
|||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import feign.Client;
|
|
||||||
import feign.Contract;
|
|
||||||
import feign.Feign;
|
|
||||||
import feign.Logger;
|
|
||||||
import feign.Request;
|
|
||||||
import feign.RequestInterceptor;
|
|
||||||
import feign.Retryer;
|
|
||||||
import feign.Target.HardCodedTarget;
|
|
||||||
import feign.codec.Decoder;
|
|
||||||
import feign.codec.Encoder;
|
|
||||||
import feign.codec.ErrorDecoder;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
* @author Venil Noronha
|
* @author Venil Noronha
|
||||||
* @author Eko Kurniawan Khannedy
|
* @author Eko Kurniawan Khannedy
|
||||||
* @author Gregor Zurowski
|
* @author Gregor Zurowski
|
||||||
*/
|
*/
|
||||||
class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
|
class FeignClientFactoryBean
|
||||||
ApplicationContextAware {
|
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware {
|
||||||
|
|
||||||
/***********************************
|
/***********************************
|
||||||
* WARNING! Nothing in this class should be @Autowired. It causes NPEs because of some lifecycle race condition.
|
* WARNING! Nothing in this class should be @Autowired. It causes NPEs because of some
|
||||||
|
* lifecycle race condition.
|
||||||
***********************************/
|
***********************************/
|
||||||
|
|
||||||
private Class<?> type;
|
private Class<?> type;
|
||||||
@@ -60,6 +63,8 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
|
|||||||
|
|
||||||
private String url;
|
private String url;
|
||||||
|
|
||||||
|
private String contextId;
|
||||||
|
|
||||||
private String path;
|
private String path;
|
||||||
|
|
||||||
private boolean decode404;
|
private boolean decode404;
|
||||||
@@ -72,14 +77,10 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterPropertiesSet() throws Exception {
|
public void afterPropertiesSet() throws Exception {
|
||||||
|
Assert.hasText(this.contextId, "Context id must be set");
|
||||||
Assert.hasText(this.name, "Name must be set");
|
Assert.hasText(this.name, "Name must be set");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
|
||||||
this.applicationContext = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Feign.Builder feign(FeignContext context) {
|
protected Feign.Builder feign(FeignContext context) {
|
||||||
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
|
FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class);
|
||||||
Logger logger = loggerFactory.create(this.type);
|
Logger logger = loggerFactory.create(this.type);
|
||||||
@@ -99,23 +100,33 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void configureFeign(FeignContext context, Feign.Builder builder) {
|
protected void configureFeign(FeignContext context, Feign.Builder builder) {
|
||||||
FeignClientProperties properties = applicationContext.getBean(FeignClientProperties.class);
|
FeignClientProperties properties = this.applicationContext
|
||||||
|
.getBean(FeignClientProperties.class);
|
||||||
if (properties != null) {
|
if (properties != null) {
|
||||||
if (properties.isDefaultToProperties()) {
|
if (properties.isDefaultToProperties()) {
|
||||||
configureUsingConfiguration(context, builder);
|
configureUsingConfiguration(context, builder);
|
||||||
configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
|
configureUsingProperties(
|
||||||
configureUsingProperties(properties.getConfig().get(this.name), builder);
|
properties.getConfig().get(properties.getDefaultConfig()),
|
||||||
} else {
|
builder);
|
||||||
configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder);
|
configureUsingProperties(properties.getConfig().get(this.contextId),
|
||||||
configureUsingProperties(properties.getConfig().get(this.name), builder);
|
builder);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
configureUsingProperties(
|
||||||
|
properties.getConfig().get(properties.getDefaultConfig()),
|
||||||
|
builder);
|
||||||
|
configureUsingProperties(properties.getConfig().get(this.contextId),
|
||||||
|
builder);
|
||||||
configureUsingConfiguration(context, builder);
|
configureUsingConfiguration(context, builder);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
configureUsingConfiguration(context, builder);
|
configureUsingConfiguration(context, builder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void configureUsingConfiguration(FeignContext context, Feign.Builder builder) {
|
protected void configureUsingConfiguration(FeignContext context,
|
||||||
|
Feign.Builder builder) {
|
||||||
Logger.Level level = getOptional(context, Logger.Level.class);
|
Logger.Level level = getOptional(context, Logger.Level.class);
|
||||||
if (level != null) {
|
if (level != null) {
|
||||||
builder.logLevel(level);
|
builder.logLevel(level);
|
||||||
@@ -132,18 +143,23 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
|
|||||||
if (options != null) {
|
if (options != null) {
|
||||||
builder.options(options);
|
builder.options(options);
|
||||||
}
|
}
|
||||||
Map<String, RequestInterceptor> requestInterceptors = context.getInstances(
|
Map<String, RequestInterceptor> requestInterceptors = context
|
||||||
this.name, RequestInterceptor.class);
|
.getInstances(this.contextId, RequestInterceptor.class);
|
||||||
if (requestInterceptors != null) {
|
if (requestInterceptors != null) {
|
||||||
builder.requestInterceptors(requestInterceptors.values());
|
builder.requestInterceptors(requestInterceptors.values());
|
||||||
}
|
}
|
||||||
|
QueryMapEncoder queryMapEncoder = getOptional(context, QueryMapEncoder.class);
|
||||||
if (decode404) {
|
if (queryMapEncoder != null) {
|
||||||
|
builder.queryMapEncoder(queryMapEncoder);
|
||||||
|
}
|
||||||
|
if (this.decode404) {
|
||||||
builder.decode404();
|
builder.decode404();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void configureUsingProperties(FeignClientProperties.FeignClientConfiguration config, Feign.Builder builder) {
|
protected void configureUsingProperties(
|
||||||
|
FeignClientProperties.FeignClientConfiguration config,
|
||||||
|
Feign.Builder builder) {
|
||||||
if (config == null) {
|
if (config == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -153,7 +169,8 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (config.getConnectTimeout() != null && config.getReadTimeout() != null) {
|
if (config.getConnectTimeout() != null && config.getReadTimeout() != null) {
|
||||||
builder.options(new Request.Options(config.getConnectTimeout(), config.getReadTimeout()));
|
builder.options(new Request.Options(config.getConnectTimeout(),
|
||||||
|
config.getReadTimeout()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.getRetryer() != null) {
|
if (config.getRetryer() != null) {
|
||||||
@@ -166,7 +183,8 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
|
|||||||
builder.errorDecoder(errorDecoder);
|
builder.errorDecoder(errorDecoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.getRequestInterceptors() != null && !config.getRequestInterceptors().isEmpty()) {
|
if (config.getRequestInterceptors() != null
|
||||||
|
&& !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);
|
||||||
@@ -195,23 +213,24 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
|
|||||||
|
|
||||||
private <T> T getOrInstantiate(Class<T> tClass) {
|
private <T> T getOrInstantiate(Class<T> tClass) {
|
||||||
try {
|
try {
|
||||||
return applicationContext.getBean(tClass);
|
return this.applicationContext.getBean(tClass);
|
||||||
} catch (NoSuchBeanDefinitionException e) {
|
}
|
||||||
|
catch (NoSuchBeanDefinitionException e) {
|
||||||
return BeanUtils.instantiateClass(tClass);
|
return BeanUtils.instantiateClass(tClass);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> T get(FeignContext context, Class<T> type) {
|
protected <T> T get(FeignContext context, Class<T> type) {
|
||||||
T instance = context.getInstance(this.name, type);
|
T instance = context.getInstance(this.contextId, type);
|
||||||
if (instance == null) {
|
if (instance == null) {
|
||||||
throw new IllegalStateException("No bean found of type " + type + " for "
|
throw new IllegalStateException(
|
||||||
+ this.name);
|
"No bean found of type " + type + " for " + this.contextId);
|
||||||
}
|
}
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> T getOptional(FeignContext context, Class<T> type) {
|
protected <T> T getOptional(FeignContext context, Class<T> type) {
|
||||||
return context.getInstance(this.name, type);
|
return context.getInstance(this.contextId, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
|
protected <T> T loadBalance(Feign.Builder builder, FeignContext context,
|
||||||
@@ -234,23 +253,23 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param <T> the target type of the Feign client
|
* @param <T> the target type of the Feign client
|
||||||
* @return a {@link Feign} client created with the specified data and the context information
|
* @return a {@link Feign} client created with the specified data and the context
|
||||||
|
* information
|
||||||
*/
|
*/
|
||||||
<T> T getTarget() {
|
<T> T getTarget() {
|
||||||
FeignContext context = applicationContext.getBean(FeignContext.class);
|
FeignContext context = this.applicationContext.getBean(FeignContext.class);
|
||||||
Feign.Builder builder = feign(context);
|
Feign.Builder builder = feign(context);
|
||||||
|
|
||||||
if (!StringUtils.hasText(this.url)) {
|
if (!StringUtils.hasText(this.url)) {
|
||||||
String url;
|
|
||||||
if (!this.name.startsWith("http")) {
|
if (!this.name.startsWith("http")) {
|
||||||
url = "http://" + this.name;
|
this.url = "http://" + this.name;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
url = this.name;
|
this.url = this.name;
|
||||||
}
|
}
|
||||||
url += cleanPath();
|
this.url += cleanPath();
|
||||||
return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type,
|
return (T) loadBalance(builder, context,
|
||||||
this.name, url));
|
new HardCodedTarget<>(this.type, this.name, this.url));
|
||||||
}
|
}
|
||||||
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
|
if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) {
|
||||||
this.url = "http://" + this.url;
|
this.url = "http://" + this.url;
|
||||||
@@ -261,13 +280,13 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
|
|||||||
if (client instanceof LoadBalancerFeignClient) {
|
if (client instanceof LoadBalancerFeignClient) {
|
||||||
// not load balancing because we have a url,
|
// not load balancing because we have a url,
|
||||||
// but ribbon is on the classpath, so unwrap
|
// but ribbon is on the classpath, so unwrap
|
||||||
client = ((LoadBalancerFeignClient)client).getDelegate();
|
client = ((LoadBalancerFeignClient) client).getDelegate();
|
||||||
}
|
}
|
||||||
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, new HardCodedTarget<>(
|
return (T) targeter.target(this, builder, context,
|
||||||
this.type, this.name, url));
|
new HardCodedTarget<>(this.type, this.name, url));
|
||||||
}
|
}
|
||||||
|
|
||||||
private String cleanPath() {
|
private String cleanPath() {
|
||||||
@@ -294,7 +313,7 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Class<?> getType() {
|
public Class<?> getType() {
|
||||||
return type;
|
return this.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setType(Class<?> type) {
|
public void setType(Class<?> type) {
|
||||||
@@ -302,15 +321,23 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getContextId() {
|
||||||
|
return this.contextId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContextId(String contextId) {
|
||||||
|
this.contextId = contextId;
|
||||||
|
}
|
||||||
|
|
||||||
public String getUrl() {
|
public String getUrl() {
|
||||||
return url;
|
return this.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUrl(String url) {
|
public void setUrl(String url) {
|
||||||
@@ -318,7 +345,7 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getPath() {
|
public String getPath() {
|
||||||
return path;
|
return this.path;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPath(String path) {
|
public void setPath(String path) {
|
||||||
@@ -326,7 +353,7 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDecode404() {
|
public boolean isDecode404() {
|
||||||
return decode404;
|
return this.decode404;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDecode404(boolean decode404) {
|
public void setDecode404(boolean decode404) {
|
||||||
@@ -334,11 +361,16 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ApplicationContext getApplicationContext() {
|
public ApplicationContext getApplicationContext() {
|
||||||
return applicationContext;
|
return this.applicationContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
||||||
|
this.applicationContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Class<?> getFallback() {
|
public Class<?> getFallback() {
|
||||||
return fallback;
|
return this.fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFallback(Class<?> fallback) {
|
public void setFallback(Class<?> fallback) {
|
||||||
@@ -346,7 +378,7 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Class<?> getFallbackFactory() {
|
public Class<?> getFallbackFactory() {
|
||||||
return fallbackFactory;
|
return this.fallbackFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFallbackFactory(Class<?> fallbackFactory) {
|
public void setFallbackFactory(Class<?> fallbackFactory) {
|
||||||
@@ -355,37 +387,39 @@ class FeignClientFactoryBean implements FactoryBean<Object>, InitializingBean,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) {
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
FeignClientFactoryBean that = (FeignClientFactoryBean) o;
|
FeignClientFactoryBean that = (FeignClientFactoryBean) o;
|
||||||
return Objects.equals(applicationContext, that.applicationContext) &&
|
return Objects.equals(this.applicationContext, that.applicationContext)
|
||||||
decode404 == that.decode404 &&
|
&& this.decode404 == that.decode404
|
||||||
Objects.equals(fallback, that.fallback) &&
|
&& Objects.equals(this.fallback, that.fallback)
|
||||||
Objects.equals(fallbackFactory, that.fallbackFactory) &&
|
&& Objects.equals(this.fallbackFactory, that.fallbackFactory)
|
||||||
Objects.equals(name, that.name) &&
|
&& Objects.equals(this.name, that.name)
|
||||||
Objects.equals(path, that.path) &&
|
&& Objects.equals(this.path, that.path)
|
||||||
Objects.equals(type, that.type) &&
|
&& Objects.equals(this.type, that.type)
|
||||||
Objects.equals(url, that.url);
|
&& Objects.equals(this.url, that.url);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(applicationContext, decode404, fallback, fallbackFactory,
|
return Objects.hash(this.applicationContext, this.decode404, this.fallback,
|
||||||
name, path, type, url);
|
this.fallbackFactory, this.name, this.path, this.type, this.url);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new StringBuilder("FeignClientFactoryBean{")
|
return new StringBuilder("FeignClientFactoryBean{").append("type=")
|
||||||
.append("type=").append(type).append(", ")
|
.append(this.type).append(", ").append("name='").append(this.name)
|
||||||
.append("name='").append(name).append("', ")
|
.append("', ").append("url='").append(this.url).append("', ")
|
||||||
.append("url='").append(url).append("', ")
|
.append("path='").append(this.path).append("', ").append("decode404=")
|
||||||
.append("path='").append(path).append("', ")
|
.append(this.decode404).append(", ").append("applicationContext=")
|
||||||
.append("decode404=").append(decode404).append(", ")
|
.append(this.applicationContext).append(", ").append("fallback=")
|
||||||
.append("applicationContext=").append(applicationContext).append(", ")
|
.append(this.fallback).append(", ").append("fallbackFactory=")
|
||||||
.append("fallback=").append(fallback).append(", ")
|
.append(this.fallbackFactory).append("}").toString();
|
||||||
.append("fallbackFactory=").append(fallbackFactory)
|
|
||||||
.append("}").toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-41
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2017 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -13,8 +13,14 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
package org.springframework.cloud.openfeign;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import feign.Contract;
|
import feign.Contract;
|
||||||
import feign.Logger;
|
import feign.Logger;
|
||||||
import feign.RequestInterceptor;
|
import feign.RequestInterceptor;
|
||||||
@@ -22,12 +28,8 @@ import feign.Retryer;
|
|||||||
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 org.springframework.boot.context.properties.ConfigurationProperties;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Eko Kurniawan Khannedy
|
* @author Eko Kurniawan Khannedy
|
||||||
@@ -42,7 +44,7 @@ public class FeignClientProperties {
|
|||||||
private Map<String, FeignClientConfiguration> config = new HashMap<>();
|
private Map<String, FeignClientConfiguration> config = new HashMap<>();
|
||||||
|
|
||||||
public boolean isDefaultToProperties() {
|
public boolean isDefaultToProperties() {
|
||||||
return defaultToProperties;
|
return this.defaultToProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDefaultToProperties(boolean defaultToProperties) {
|
public void setDefaultToProperties(boolean defaultToProperties) {
|
||||||
@@ -50,7 +52,7 @@ public class FeignClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getDefaultConfig() {
|
public String getDefaultConfig() {
|
||||||
return defaultConfig;
|
return this.defaultConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDefaultConfig(String defaultConfig) {
|
public void setDefaultConfig(String defaultConfig) {
|
||||||
@@ -58,7 +60,7 @@ public class FeignClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, FeignClientConfiguration> getConfig() {
|
public Map<String, FeignClientConfiguration> getConfig() {
|
||||||
return config;
|
return this.config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setConfig(Map<String, FeignClientConfiguration> config) {
|
public void setConfig(Map<String, FeignClientConfiguration> config) {
|
||||||
@@ -67,19 +69,26 @@ public class FeignClientProperties {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) {
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
FeignClientProperties that = (FeignClientProperties) o;
|
FeignClientProperties that = (FeignClientProperties) o;
|
||||||
return defaultToProperties == that.defaultToProperties &&
|
return this.defaultToProperties == that.defaultToProperties
|
||||||
Objects.equals(defaultConfig, that.defaultConfig) &&
|
&& Objects.equals(this.defaultConfig, that.defaultConfig)
|
||||||
Objects.equals(config, that.config);
|
&& Objects.equals(this.config, that.config);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(defaultToProperties, defaultConfig, config);
|
return Objects.hash(this.defaultToProperties, this.defaultConfig, this.config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feign client configuration.
|
||||||
|
*/
|
||||||
public static class FeignClientConfiguration {
|
public static class FeignClientConfiguration {
|
||||||
|
|
||||||
private Logger.Level loggerLevel;
|
private Logger.Level loggerLevel;
|
||||||
@@ -103,7 +112,7 @@ public class FeignClientProperties {
|
|||||||
private Class<Contract> contract;
|
private Class<Contract> contract;
|
||||||
|
|
||||||
public Logger.Level getLoggerLevel() {
|
public Logger.Level getLoggerLevel() {
|
||||||
return loggerLevel;
|
return this.loggerLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLoggerLevel(Logger.Level loggerLevel) {
|
public void setLoggerLevel(Logger.Level loggerLevel) {
|
||||||
@@ -111,7 +120,7 @@ public class FeignClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Integer getConnectTimeout() {
|
public Integer getConnectTimeout() {
|
||||||
return connectTimeout;
|
return this.connectTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setConnectTimeout(Integer connectTimeout) {
|
public void setConnectTimeout(Integer connectTimeout) {
|
||||||
@@ -119,7 +128,7 @@ public class FeignClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Integer getReadTimeout() {
|
public Integer getReadTimeout() {
|
||||||
return readTimeout;
|
return this.readTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setReadTimeout(Integer readTimeout) {
|
public void setReadTimeout(Integer readTimeout) {
|
||||||
@@ -127,7 +136,7 @@ public class FeignClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Class<Retryer> getRetryer() {
|
public Class<Retryer> getRetryer() {
|
||||||
return retryer;
|
return this.retryer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRetryer(Class<Retryer> retryer) {
|
public void setRetryer(Class<Retryer> retryer) {
|
||||||
@@ -135,7 +144,7 @@ public class FeignClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Class<ErrorDecoder> getErrorDecoder() {
|
public Class<ErrorDecoder> getErrorDecoder() {
|
||||||
return errorDecoder;
|
return this.errorDecoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setErrorDecoder(Class<ErrorDecoder> errorDecoder) {
|
public void setErrorDecoder(Class<ErrorDecoder> errorDecoder) {
|
||||||
@@ -143,15 +152,16 @@ public class FeignClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<Class<RequestInterceptor>> getRequestInterceptors() {
|
public List<Class<RequestInterceptor>> getRequestInterceptors() {
|
||||||
return requestInterceptors;
|
return this.requestInterceptors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRequestInterceptors(List<Class<RequestInterceptor>> requestInterceptors) {
|
public void setRequestInterceptors(
|
||||||
|
List<Class<RequestInterceptor>> requestInterceptors) {
|
||||||
this.requestInterceptors = requestInterceptors;
|
this.requestInterceptors = requestInterceptors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Boolean getDecode404() {
|
public Boolean getDecode404() {
|
||||||
return decode404;
|
return this.decode404;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDecode404(Boolean decode404) {
|
public void setDecode404(Boolean decode404) {
|
||||||
@@ -159,7 +169,7 @@ public class FeignClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Class<Decoder> getDecoder() {
|
public Class<Decoder> getDecoder() {
|
||||||
return decoder;
|
return this.decoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDecoder(Class<Decoder> decoder) {
|
public void setDecoder(Class<Decoder> decoder) {
|
||||||
@@ -167,7 +177,7 @@ public class FeignClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Class<Encoder> getEncoder() {
|
public Class<Encoder> getEncoder() {
|
||||||
return encoder;
|
return this.encoder;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEncoder(Class<Encoder> encoder) {
|
public void setEncoder(Class<Encoder> encoder) {
|
||||||
@@ -175,7 +185,7 @@ public class FeignClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Class<Contract> getContract() {
|
public Class<Contract> getContract() {
|
||||||
return contract;
|
return this.contract;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setContract(Class<Contract> contract) {
|
public void setContract(Class<Contract> contract) {
|
||||||
@@ -184,26 +194,32 @@ public class FeignClientProperties {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) {
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
FeignClientConfiguration that = (FeignClientConfiguration) o;
|
FeignClientConfiguration that = (FeignClientConfiguration) o;
|
||||||
return loggerLevel == that.loggerLevel &&
|
return this.loggerLevel == that.loggerLevel
|
||||||
Objects.equals(connectTimeout, that.connectTimeout) &&
|
&& Objects.equals(this.connectTimeout, that.connectTimeout)
|
||||||
Objects.equals(readTimeout, that.readTimeout) &&
|
&& Objects.equals(this.readTimeout, that.readTimeout)
|
||||||
Objects.equals(retryer, that.retryer) &&
|
&& Objects.equals(this.retryer, that.retryer)
|
||||||
Objects.equals(errorDecoder, that.errorDecoder) &&
|
&& Objects.equals(this.errorDecoder, that.errorDecoder)
|
||||||
Objects.equals(requestInterceptors, that.requestInterceptors) &&
|
&& Objects.equals(this.requestInterceptors, that.requestInterceptors)
|
||||||
Objects.equals(decode404, that.decode404) &&
|
&& Objects.equals(this.decode404, that.decode404)
|
||||||
Objects.equals(encoder, that.encoder) &&
|
&& Objects.equals(this.encoder, that.encoder)
|
||||||
Objects.equals(decoder, that.decoder) &&
|
&& Objects.equals(this.decoder, that.decoder)
|
||||||
Objects.equals(contract, that.contract);
|
&& Objects.equals(this.contract, that.contract);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(loggerLevel, connectTimeout, readTimeout, retryer,
|
return Objects.hash(this.loggerLevel, this.connectTimeout, this.readTimeout,
|
||||||
errorDecoder, requestInterceptors, decode404, encoder, decoder, contract);
|
this.retryer, this.errorDecoder, this.requestInterceptors,
|
||||||
|
this.decode404, this.encoder, this.decoder, this.contract);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-17
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2015 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -16,11 +16,11 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
package org.springframework.cloud.openfeign;
|
||||||
|
|
||||||
import org.springframework.cloud.context.named.NamedContextFactory;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.springframework.cloud.context.named.NamedContextFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
* @author Gregor Zurowski
|
* @author Gregor Zurowski
|
||||||
@@ -31,15 +31,16 @@ class FeignClientSpecification implements NamedContextFactory.Specification {
|
|||||||
|
|
||||||
private Class<?>[] configuration;
|
private Class<?>[] configuration;
|
||||||
|
|
||||||
public FeignClientSpecification() {}
|
FeignClientSpecification() {
|
||||||
|
}
|
||||||
|
|
||||||
public FeignClientSpecification(String name, Class<?>[] configuration) {
|
FeignClientSpecification(String name, Class<?>[] configuration) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
@@ -47,7 +48,7 @@ class FeignClientSpecification implements NamedContextFactory.Specification {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Class<?>[] getConfiguration() {
|
public Class<?>[] getConfiguration() {
|
||||||
return configuration;
|
return this.configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setConfiguration(Class<?>[] configuration) {
|
public void setConfiguration(Class<?>[] configuration) {
|
||||||
@@ -56,24 +57,27 @@ class FeignClientSpecification implements NamedContextFactory.Specification {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) {
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
FeignClientSpecification that = (FeignClientSpecification) o;
|
FeignClientSpecification that = (FeignClientSpecification) o;
|
||||||
return Objects.equals(name, that.name) &&
|
return Objects.equals(this.name, that.name)
|
||||||
Arrays.equals(configuration, that.configuration);
|
&& Arrays.equals(this.configuration, that.configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(name, configuration);
|
return Objects.hash(this.name, this.configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new StringBuilder("FeignClientSpecification{")
|
return new StringBuilder("FeignClientSpecification{").append("name='")
|
||||||
.append("name='").append(name).append("', ")
|
.append(this.name).append("', ").append("configuration=")
|
||||||
.append("configuration=").append(Arrays.toString(configuration))
|
.append(Arrays.toString(this.configuration)).append("}").toString();
|
||||||
.append("}").toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+62
-29
@@ -1,18 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2017 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
package org.springframework.cloud.openfeign;
|
||||||
@@ -20,12 +19,27 @@ package org.springframework.cloud.openfeign;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.Module;
|
||||||
|
import com.netflix.hystrix.HystrixCommand;
|
||||||
|
import feign.Contract;
|
||||||
|
import feign.Feign;
|
||||||
|
import feign.Logger;
|
||||||
|
import feign.Retryer;
|
||||||
|
import feign.codec.Decoder;
|
||||||
|
import feign.codec.Encoder;
|
||||||
|
import feign.hystrix.HystrixFeign;
|
||||||
|
import feign.optionals.OptionalDecoder;
|
||||||
|
|
||||||
import org.springframework.beans.factory.ObjectFactory;
|
import org.springframework.beans.factory.ObjectFactory;
|
||||||
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.autoconfigure.data.web.SpringDataWebProperties;
|
||||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||||
|
import org.springframework.cloud.openfeign.support.PageJacksonModule;
|
||||||
|
import org.springframework.cloud.openfeign.support.PageableSpringEncoder;
|
||||||
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
|
import org.springframework.cloud.openfeign.support.ResponseEntityDecoder;
|
||||||
import org.springframework.cloud.openfeign.support.SpringDecoder;
|
import org.springframework.cloud.openfeign.support.SpringDecoder;
|
||||||
import org.springframework.cloud.openfeign.support.SpringEncoder;
|
import org.springframework.cloud.openfeign.support.SpringEncoder;
|
||||||
@@ -37,17 +51,6 @@ import org.springframework.core.convert.ConversionService;
|
|||||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||||
import org.springframework.format.support.FormattingConversionService;
|
import org.springframework.format.support.FormattingConversionService;
|
||||||
|
|
||||||
import com.netflix.hystrix.HystrixCommand;
|
|
||||||
|
|
||||||
import feign.Contract;
|
|
||||||
import feign.Feign;
|
|
||||||
import feign.Logger;
|
|
||||||
import feign.Retryer;
|
|
||||||
import feign.codec.Decoder;
|
|
||||||
import feign.codec.Encoder;
|
|
||||||
import feign.hystrix.HystrixFeign;
|
|
||||||
import feign.optionals.OptionalDecoder;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
* @author Venil Noronha
|
* @author Venil Noronha
|
||||||
@@ -67,18 +70,40 @@ public class FeignClientsConfiguration {
|
|||||||
@Autowired(required = false)
|
@Autowired(required = false)
|
||||||
private Logger logger;
|
private Logger logger;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private SpringDataWebProperties springDataWebProperties;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public Decoder feignDecoder() {
|
public Decoder feignDecoder() {
|
||||||
return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
|
return new OptionalDecoder(
|
||||||
|
new ResponseEntityDecoder(new SpringDecoder(this.messageConverters)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
|
@ConditionalOnMissingClass("org.springframework.data.domain.Pageable")
|
||||||
public Encoder feignEncoder() {
|
public Encoder feignEncoder() {
|
||||||
return new SpringEncoder(this.messageConverters);
|
return new SpringEncoder(this.messageConverters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnClass(name = "org.springframework.data.domain.Pageable")
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public Encoder feignEncoderPageable() {
|
||||||
|
PageableSpringEncoder encoder = new PageableSpringEncoder(
|
||||||
|
new SpringEncoder(this.messageConverters));
|
||||||
|
if (springDataWebProperties != null) {
|
||||||
|
encoder.setPageParameter(
|
||||||
|
springDataWebProperties.getPageable().getPageParameter());
|
||||||
|
encoder.setSizeParameter(
|
||||||
|
springDataWebProperties.getPageable().getSizeParameter());
|
||||||
|
encoder.setSortParameter(
|
||||||
|
springDataWebProperties.getSort().getSortParameter());
|
||||||
|
}
|
||||||
|
return encoder;
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public Contract feignContract(ConversionService feignConversionService) {
|
public Contract feignContract(ConversionService feignConversionService) {
|
||||||
@@ -88,24 +113,12 @@ public class FeignClientsConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
public FormattingConversionService feignConversionService() {
|
public FormattingConversionService feignConversionService() {
|
||||||
FormattingConversionService conversionService = new DefaultFormattingConversionService();
|
FormattingConversionService conversionService = new DefaultFormattingConversionService();
|
||||||
for (FeignFormatterRegistrar feignFormatterRegistrar : feignFormatterRegistrars) {
|
for (FeignFormatterRegistrar feignFormatterRegistrar : this.feignFormatterRegistrars) {
|
||||||
feignFormatterRegistrar.registerFormatters(conversionService);
|
feignFormatterRegistrar.registerFormatters(conversionService);
|
||||||
}
|
}
|
||||||
return conversionService;
|
return conversionService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public Retryer feignRetryer() {
|
public Retryer feignRetryer() {
|
||||||
@@ -122,7 +135,27 @@ public class FeignClientsConfiguration {
|
|||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean(FeignLoggerFactory.class)
|
@ConditionalOnMissingBean(FeignLoggerFactory.class)
|
||||||
public FeignLoggerFactory feignLoggerFactory() {
|
public FeignLoggerFactory feignLoggerFactory() {
|
||||||
return new DefaultFeignLoggerFactory(logger);
|
return new DefaultFeignLoggerFactory(this.logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnClass(name = "org.springframework.data.domain.Page")
|
||||||
|
public Module pageJacksonModule() {
|
||||||
|
return new PageJacksonModule();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+87
-71
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2016 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -58,8 +58,8 @@ import org.springframework.util.StringUtils;
|
|||||||
* @author Venil Noronha
|
* @author Venil Noronha
|
||||||
* @author Gang Li
|
* @author Gang Li
|
||||||
*/
|
*/
|
||||||
class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
class FeignClientsRegistrar
|
||||||
ResourceLoaderAware, EnvironmentAware {
|
implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {
|
||||||
|
|
||||||
// patterned after Spring Integration IntegrationComponentScanRegistrar
|
// patterned after Spring Integration IntegrationComponentScanRegistrar
|
||||||
// and RibbonClientsConfigurationRegistgrar
|
// and RibbonClientsConfigurationRegistgrar
|
||||||
@@ -68,7 +68,68 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
|||||||
|
|
||||||
private Environment environment;
|
private Environment environment;
|
||||||
|
|
||||||
public FeignClientsRegistrar() {
|
FeignClientsRegistrar() {
|
||||||
|
}
|
||||||
|
|
||||||
|
static void validateFallback(final Class clazz) {
|
||||||
|
Assert.isTrue(!clazz.isInterface(),
|
||||||
|
"Fallback class must implement the interface annotated by @FeignClient");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void validateFallbackFactory(final Class clazz) {
|
||||||
|
Assert.isTrue(!clazz.isInterface(), "Fallback factory must produce instances "
|
||||||
|
+ "of fallback classes that implement the interface annotated by @FeignClient");
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getName(String name) {
|
||||||
|
if (!StringUtils.hasText(name)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
String host = null;
|
||||||
|
try {
|
||||||
|
String url;
|
||||||
|
if (!name.startsWith("http://") && !name.startsWith("https://")) {
|
||||||
|
url = "http://" + name;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
url = name;
|
||||||
|
}
|
||||||
|
host = new URI(url).getHost();
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (URISyntaxException e) {
|
||||||
|
}
|
||||||
|
Assert.state(host != null, "Service id not legal hostname (" + name + ")");
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getUrl(String url) {
|
||||||
|
if (StringUtils.hasText(url) && !(url.startsWith("#{") && url.contains("}"))) {
|
||||||
|
if (!url.contains("://")) {
|
||||||
|
url = "http://" + url;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
new URL(url);
|
||||||
|
}
|
||||||
|
catch (MalformedURLException e) {
|
||||||
|
throw new IllegalArgumentException(url + " is malformed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getPath(String path) {
|
||||||
|
if (StringUtils.hasText(path)) {
|
||||||
|
path = path.trim();
|
||||||
|
if (!path.startsWith("/")) {
|
||||||
|
path = "/" + path;
|
||||||
|
}
|
||||||
|
if (path.endsWith("/")) {
|
||||||
|
path = path.substring(0, path.length() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -171,16 +232,19 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
|||||||
definition.addPropertyValue("path", getPath(attributes));
|
definition.addPropertyValue("path", getPath(attributes));
|
||||||
String name = getName(attributes);
|
String name = getName(attributes);
|
||||||
definition.addPropertyValue("name", name);
|
definition.addPropertyValue("name", name);
|
||||||
|
String contextId = getContextId(attributes);
|
||||||
|
definition.addPropertyValue("contextId", contextId);
|
||||||
definition.addPropertyValue("type", className);
|
definition.addPropertyValue("type", className);
|
||||||
definition.addPropertyValue("decode404", attributes.get("decode404"));
|
definition.addPropertyValue("decode404", attributes.get("decode404"));
|
||||||
definition.addPropertyValue("fallback", attributes.get("fallback"));
|
definition.addPropertyValue("fallback", attributes.get("fallback"));
|
||||||
definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
|
definition.addPropertyValue("fallbackFactory", attributes.get("fallbackFactory"));
|
||||||
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
|
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
|
||||||
|
|
||||||
String alias = name + "FeignClient";
|
String alias = contextId + "FeignClient";
|
||||||
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
|
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
|
||||||
|
|
||||||
boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null
|
boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be
|
||||||
|
// null
|
||||||
|
|
||||||
beanDefinition.setPrimary(primary);
|
beanDefinition.setPrimary(primary);
|
||||||
|
|
||||||
@@ -202,19 +266,6 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
|||||||
validateFallbackFactory(annotation.getClass("fallbackFactory"));
|
validateFallbackFactory(annotation.getClass("fallbackFactory"));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void validateFallback(final Class clazz) {
|
|
||||||
Assert.isTrue(
|
|
||||||
!clazz.isInterface(),
|
|
||||||
"Fallback class must implement the interface annotated by @FeignClient"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void validateFallbackFactory(final Class clazz) {
|
|
||||||
Assert.isTrue(!clazz.isInterface(),
|
|
||||||
"Fallback factory must produce instances of fallback classes that implement the interface annotated by @FeignClient"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* for testing */ String getName(Map<String, Object> attributes) {
|
/* for testing */ String getName(Map<String, Object> attributes) {
|
||||||
String name = (String) attributes.get("serviceId");
|
String name = (String) attributes.get("serviceId");
|
||||||
if (!StringUtils.hasText(name)) {
|
if (!StringUtils.hasText(name)) {
|
||||||
@@ -227,26 +278,14 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
|||||||
return getName(name);
|
return getName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static String getName(String name) {
|
private String getContextId(Map<String, Object> attributes) {
|
||||||
if (!StringUtils.hasText(name)) {
|
String contextId = (String) attributes.get("contextId");
|
||||||
return "";
|
if (!StringUtils.hasText(contextId)) {
|
||||||
|
return getName(attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
String host = null;
|
contextId = resolve(contextId);
|
||||||
try {
|
return getName(contextId);
|
||||||
String url;
|
|
||||||
if (!name.startsWith("http://") && !name.startsWith("https://")) {
|
|
||||||
url = "http://" + name;
|
|
||||||
} else {
|
|
||||||
url = name;
|
|
||||||
}
|
|
||||||
host = new URI(url).getHost();
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (URISyntaxException e) {
|
|
||||||
}
|
|
||||||
Assert.state(host != null, "Service id not legal hostname (" + name + ")");
|
|
||||||
return name;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private String resolve(String value) {
|
private String resolve(String value) {
|
||||||
@@ -261,43 +300,16 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
|||||||
return getUrl(url);
|
return getUrl(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
static String getUrl(String url) {
|
|
||||||
if (StringUtils.hasText(url) && !(url.startsWith("#{") && url.contains("}"))) {
|
|
||||||
if (!url.contains("://")) {
|
|
||||||
url = "http://" + url;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
new URL(url);
|
|
||||||
}
|
|
||||||
catch (MalformedURLException e) {
|
|
||||||
throw new IllegalArgumentException(url + " is malformed", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getPath(Map<String, Object> attributes) {
|
private String getPath(Map<String, Object> attributes) {
|
||||||
String path = resolve((String) attributes.get("path"));
|
String path = resolve((String) attributes.get("path"));
|
||||||
return getPath(path);
|
return getPath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
static String getPath(String path) {
|
|
||||||
if (StringUtils.hasText(path)) {
|
|
||||||
path = path.trim();
|
|
||||||
if (!path.startsWith("/")) {
|
|
||||||
path = "/" + path;
|
|
||||||
}
|
|
||||||
if (path.endsWith("/")) {
|
|
||||||
path = path.substring(0, path.length() - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected ClassPathScanningCandidateComponentProvider getScanner() {
|
protected ClassPathScanningCandidateComponentProvider getScanner() {
|
||||||
return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
|
return new ClassPathScanningCandidateComponentProvider(false, this.environment) {
|
||||||
@Override
|
@Override
|
||||||
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
|
protected boolean isCandidateComponent(
|
||||||
|
AnnotatedBeanDefinition beanDefinition) {
|
||||||
boolean isCandidate = false;
|
boolean isCandidate = false;
|
||||||
if (beanDefinition.getMetadata().isIndependent()) {
|
if (beanDefinition.getMetadata().isIndependent()) {
|
||||||
if (!beanDefinition.getMetadata().isAnnotation()) {
|
if (!beanDefinition.getMetadata().isAnnotation()) {
|
||||||
@@ -334,7 +346,7 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
|||||||
}
|
}
|
||||||
return basePackages;
|
return basePackages;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getQualifier(Map<String, Object> client) {
|
private String getQualifier(Map<String, Object> client) {
|
||||||
if (client == null) {
|
if (client == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -350,7 +362,10 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
|||||||
if (client == null) {
|
if (client == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
String value = (String) client.get("value");
|
String value = (String) client.get("contextId");
|
||||||
|
if (!StringUtils.hasText(value)) {
|
||||||
|
value = (String) client.get("value");
|
||||||
|
}
|
||||||
if (!StringUtils.hasText(value)) {
|
if (!StringUtils.hasText(value)) {
|
||||||
value = (String) client.get("name");
|
value = (String) client.get("name");
|
||||||
}
|
}
|
||||||
@@ -393,10 +408,9 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link AllTypeFilter} to match if all the given delegates match.
|
* Creates a new {@link AllTypeFilter} to match if all the given delegates match.
|
||||||
*
|
|
||||||
* @param delegates must not be {@literal null}.
|
* @param delegates must not be {@literal null}.
|
||||||
*/
|
*/
|
||||||
public AllTypeFilter(List<TypeFilter> delegates) {
|
AllTypeFilter(List<TypeFilter> delegates) {
|
||||||
Assert.notNull(delegates, "This argument is required, it must not be null");
|
Assert.notNull(delegates, "This argument is required, it must not be null");
|
||||||
this.delegates = delegates;
|
this.delegates = delegates;
|
||||||
}
|
}
|
||||||
@@ -413,5 +427,7 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar,
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2015 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
|||||||
+2
-2
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2016 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
|||||||
+3
-4
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 the original author or authors.
|
* Copyright 2016-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -27,10 +27,9 @@ public interface FeignLoggerFactory {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory method to provide a {@link Logger} for a given {@link Class}.
|
* Factory method to provide a {@link Logger} for a given {@link Class}.
|
||||||
*
|
|
||||||
* @param type the {@link Class} for which a {@link Logger} instance is to be created
|
* @param type the {@link Class} for which a {@link Logger} instance is to be created
|
||||||
* @return a {@link Logger} instance
|
* @return a {@link Logger} instance
|
||||||
*/
|
*/
|
||||||
public Logger create(Class<?> type);
|
Logger create(Class<?> type);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+29
-28
@@ -1,24 +1,21 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2016 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
package org.springframework.cloud.openfeign;
|
||||||
|
|
||||||
import org.springframework.util.Assert;
|
|
||||||
|
|
||||||
import feign.Feign;
|
import feign.Feign;
|
||||||
import feign.Target;
|
import feign.Target;
|
||||||
import feign.hystrix.FallbackFactory;
|
import feign.hystrix.FallbackFactory;
|
||||||
@@ -33,66 +30,70 @@ import feign.hystrix.SetterFactory;
|
|||||||
class HystrixTargeter implements Targeter {
|
class HystrixTargeter implements Targeter {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
|
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
|
||||||
Target.HardCodedTarget<T> target) {
|
FeignContext context, Target.HardCodedTarget<T> target) {
|
||||||
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
|
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
|
||||||
return feign.target(target);
|
return feign.target(target);
|
||||||
}
|
}
|
||||||
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
|
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
|
||||||
SetterFactory setterFactory = getOptional(factory.getName(), context,
|
SetterFactory setterFactory = getOptional(factory.getName(), context,
|
||||||
SetterFactory.class);
|
SetterFactory.class);
|
||||||
if (setterFactory != null) {
|
if (setterFactory != null) {
|
||||||
builder.setterFactory(setterFactory);
|
builder.setterFactory(setterFactory);
|
||||||
}
|
}
|
||||||
Class<?> fallback = factory.getFallback();
|
Class<?> fallback = factory.getFallback();
|
||||||
if (fallback != void.class) {
|
if (fallback != void.class) {
|
||||||
return targetWithFallback(factory.getName(), context, target, builder, fallback);
|
return targetWithFallback(factory.getName(), context, target, builder,
|
||||||
|
fallback);
|
||||||
}
|
}
|
||||||
Class<?> fallbackFactory = factory.getFallbackFactory();
|
Class<?> fallbackFactory = factory.getFallbackFactory();
|
||||||
if (fallbackFactory != void.class) {
|
if (fallbackFactory != void.class) {
|
||||||
return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory);
|
return targetWithFallbackFactory(factory.getName(), context, target, builder,
|
||||||
|
fallbackFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
return feign.target(target);
|
return feign.target(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
|
private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
|
||||||
Target.HardCodedTarget<T> target,
|
Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
|
||||||
HystrixFeign.Builder builder,
|
Class<?> fallbackFactoryClass) {
|
||||||
Class<?> fallbackFactoryClass) {
|
FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>) getFromContext(
|
||||||
FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>)
|
"fallbackFactory", feignClientName, context, fallbackFactoryClass,
|
||||||
getFromContext("fallbackFactory", feignClientName, context, fallbackFactoryClass, FallbackFactory.class);
|
FallbackFactory.class);
|
||||||
return builder.target(target, fallbackFactory);
|
return builder.target(target, fallbackFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private <T> T targetWithFallback(String feignClientName, FeignContext context,
|
private <T> T targetWithFallback(String feignClientName, FeignContext context,
|
||||||
Target.HardCodedTarget<T> target,
|
Target.HardCodedTarget<T> target, HystrixFeign.Builder builder,
|
||||||
HystrixFeign.Builder builder, Class<?> fallback) {
|
Class<?> fallback) {
|
||||||
T fallbackInstance = getFromContext("fallback", feignClientName, context, fallback, target.type());
|
T fallbackInstance = getFromContext("fallback", feignClientName, context,
|
||||||
|
fallback, target.type());
|
||||||
return builder.target(target, fallbackInstance);
|
return builder.target(target, fallbackInstance);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> T getFromContext(String fallbackMechanism, String feignClientName, FeignContext context,
|
private <T> T getFromContext(String fallbackMechanism, String feignClientName,
|
||||||
Class<?> beanType, Class<T> targetType) {
|
FeignContext context, Class<?> beanType, Class<T> targetType) {
|
||||||
Object fallbackInstance = context.getInstance(feignClientName, beanType);
|
Object fallbackInstance = context.getInstance(feignClientName, beanType);
|
||||||
if (fallbackInstance == null) {
|
if (fallbackInstance == null) {
|
||||||
throw new IllegalStateException(String.format(
|
throw new IllegalStateException(String.format(
|
||||||
"No " + fallbackMechanism + " instance of type %s found for feign client %s",
|
"No " + fallbackMechanism
|
||||||
beanType, feignClientName));
|
+ " instance of type %s found for feign client %s",
|
||||||
|
beanType, feignClientName));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!targetType.isAssignableFrom(beanType)) {
|
if (!targetType.isAssignableFrom(beanType)) {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(String.format("Incompatible "
|
||||||
String.format(
|
+ fallbackMechanism
|
||||||
"Incompatible " + fallbackMechanism + " instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
|
+ " instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
|
||||||
beanType, targetType, feignClientName));
|
beanType, targetType, feignClientName));
|
||||||
}
|
}
|
||||||
return (T) fallbackInstance;
|
return (T) fallbackInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> T getOptional(String feignClientName, FeignContext context,
|
private <T> T getOptional(String feignClientName, FeignContext context,
|
||||||
Class<T> beanType) {
|
Class<T> beanType) {
|
||||||
return context.getInstance(feignClientName, beanType);
|
return context.getInstance(feignClientName, beanType);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2019 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.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
import feign.QueryMap;
|
||||||
|
|
||||||
|
import org.springframework.core.annotation.AliasFor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spring MVC equivalent of OpenFeign's {@link feign.QueryMap} parameter annotation.
|
||||||
|
*
|
||||||
|
* @author Aram Peres
|
||||||
|
* @see feign.QueryMap
|
||||||
|
* @see org.springframework.cloud.openfeign.annotation.QueryMapParameterProcessor
|
||||||
|
*/
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ ElementType.PARAMETER })
|
||||||
|
public @interface SpringQueryMap {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see QueryMap#encoded()
|
||||||
|
* @return alias for {@link #encoded()}.
|
||||||
|
*/
|
||||||
|
@AliasFor("encoded")
|
||||||
|
boolean value() default false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see QueryMap#encoded()
|
||||||
|
* @return Specifies whether parameter names and values are already encoded.
|
||||||
|
*/
|
||||||
|
@AliasFor("value")
|
||||||
|
boolean encoded() default false;
|
||||||
|
|
||||||
|
}
|
||||||
+6
-5
@@ -1,18 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2016 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
package org.springframework.cloud.openfeign;
|
||||||
@@ -24,6 +23,8 @@ import feign.Target;
|
|||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
*/
|
*/
|
||||||
interface Targeter {
|
interface Targeter {
|
||||||
<T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
|
|
||||||
Target.HardCodedTarget<T> target);
|
<T> T target(FeignClientFactoryBean factory, Feign.Builder feign,
|
||||||
|
FeignContext context, Target.HardCodedTarget<T> target);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-6
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2015 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -21,11 +21,11 @@ import java.lang.reflect.Method;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import feign.MethodMetadata;
|
||||||
|
|
||||||
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
|
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
|
||||||
import feign.MethodMetadata;
|
|
||||||
|
|
||||||
import static feign.Util.checkState;
|
import static feign.Util.checkState;
|
||||||
import static feign.Util.emptyToNull;
|
import static feign.Util.emptyToNull;
|
||||||
|
|
||||||
@@ -46,10 +46,12 @@ public class PathVariableParameterProcessor implements AnnotatedParameterProcess
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
|
public boolean processArgument(AnnotatedParameterContext context,
|
||||||
|
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.", context.getParameterIndex());
|
"PathVariable annotation was empty on param %s.",
|
||||||
|
context.getParameterIndex());
|
||||||
context.setParameterName(name);
|
context.setParameterName(name);
|
||||||
|
|
||||||
MethodMetadata data = context.getMethodMetadata();
|
MethodMetadata data = context.getMethodMetadata();
|
||||||
@@ -74,4 +76,5 @@ public class PathVariableParameterProcessor implements AnnotatedParameterProcess
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2019 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.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
import feign.MethodMetadata;
|
||||||
|
|
||||||
|
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
|
||||||
|
import org.springframework.cloud.openfeign.SpringQueryMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link SpringQueryMap} parameter processor.
|
||||||
|
*
|
||||||
|
* @author Aram Peres
|
||||||
|
* @see AnnotatedParameterProcessor
|
||||||
|
*/
|
||||||
|
public class QueryMapParameterProcessor implements AnnotatedParameterProcessor {
|
||||||
|
|
||||||
|
private static final Class<SpringQueryMap> ANNOTATION = SpringQueryMap.class;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends Annotation> getAnnotationType() {
|
||||||
|
return ANNOTATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean processArgument(AnnotatedParameterContext context,
|
||||||
|
Annotation annotation, Method method) {
|
||||||
|
int paramIndex = context.getParameterIndex();
|
||||||
|
MethodMetadata metadata = context.getMethodMetadata();
|
||||||
|
if (metadata.queryMapIndex() == null) {
|
||||||
|
metadata.queryMapIndex(paramIndex);
|
||||||
|
metadata.queryMapEncoded(SpringQueryMap.class.cast(annotation).encoded());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+11
-7
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2015 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -21,11 +21,11 @@ import java.lang.reflect.Method;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import feign.MethodMetadata;
|
||||||
|
|
||||||
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
|
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
|
||||||
import org.springframework.web.bind.annotation.RequestHeader;
|
import org.springframework.web.bind.annotation.RequestHeader;
|
||||||
|
|
||||||
import feign.MethodMetadata;
|
|
||||||
|
|
||||||
import static feign.Util.checkState;
|
import static feign.Util.checkState;
|
||||||
import static feign.Util.emptyToNull;
|
import static feign.Util.emptyToNull;
|
||||||
|
|
||||||
@@ -46,13 +46,15 @@ public class RequestHeaderParameterProcessor implements AnnotatedParameterProces
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
|
public boolean processArgument(AnnotatedParameterContext context,
|
||||||
|
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, "Header map can only be present once.");
|
checkState(data.headerMapIndex() == null,
|
||||||
|
"Header map can only be present once.");
|
||||||
data.headerMapIndex(parameterIndex);
|
data.headerMapIndex(parameterIndex);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -63,8 +65,10 @@ public class RequestHeaderParameterProcessor implements AnnotatedParameterProces
|
|||||||
"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, data.template().headers().get(name));
|
Collection<String> header = context.setTemplateParameter(name,
|
||||||
|
data.template().headers().get(name));
|
||||||
data.template().header(name, header);
|
data.template().header(name, header);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-8
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2015 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -21,14 +21,14 @@ import java.lang.reflect.Method;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import feign.MethodMetadata;
|
||||||
|
|
||||||
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
|
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
|
|
||||||
import static feign.Util.checkState;
|
import static feign.Util.checkState;
|
||||||
import static feign.Util.emptyToNull;
|
import static feign.Util.emptyToNull;
|
||||||
|
|
||||||
import feign.MethodMetadata;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link RequestParam} parameter processor.
|
* {@link RequestParam} parameter processor.
|
||||||
*
|
*
|
||||||
@@ -46,13 +46,15 @@ public class RequestParamParameterProcessor implements AnnotatedParameterProcess
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) {
|
public boolean processArgument(AnnotatedParameterContext context,
|
||||||
|
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, "Query map can only be present once.");
|
checkState(data.queryMapIndex() == null,
|
||||||
|
"Query map can only be present once.");
|
||||||
data.queryMapIndex(parameterIndex);
|
data.queryMapIndex(parameterIndex);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -61,8 +63,7 @@ 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",
|
"RequestParam.value() was empty on parameter %s", parameterIndex);
|
||||||
parameterIndex);
|
|
||||||
context.setParameterName(name);
|
context.setParameterName(name);
|
||||||
|
|
||||||
Collection<String> query = context.setTemplateParameter(name,
|
Collection<String> query = context.setTemplateParameter(name,
|
||||||
@@ -70,4 +71,5 @@ public class RequestParamParameterProcessor implements AnnotatedParameterProcess
|
|||||||
data.template().query(name, query);
|
data.template().query(name, query);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+123
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2019 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.clientconfig;
|
||||||
|
|
||||||
|
import java.util.Timer;
|
||||||
|
import java.util.TimerTask;
|
||||||
|
|
||||||
|
import javax.annotation.PreDestroy;
|
||||||
|
|
||||||
|
import org.apache.http.client.config.RequestConfig;
|
||||||
|
import org.apache.http.config.RegistryBuilder;
|
||||||
|
import org.apache.http.conn.HttpClientConnectionManager;
|
||||||
|
import org.apache.http.impl.client.CloseableHttpClient;
|
||||||
|
import org.apache.http.impl.client.HttpClientBuilder;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
|
||||||
|
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
|
||||||
|
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default configuration for {@link CloseableHttpClient}.
|
||||||
|
*
|
||||||
|
* @author Ryan Baxter
|
||||||
|
* @author Marcin Grzejszczak
|
||||||
|
* @author Spencer Gibb
|
||||||
|
* @author Olga Maciaszek-Sharma
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConditionalOnMissingBean(CloseableHttpClient.class)
|
||||||
|
public class HttpClientFeignConfiguration {
|
||||||
|
|
||||||
|
private final Timer connectionManagerTimer = new Timer(
|
||||||
|
"FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
|
||||||
|
|
||||||
|
private CloseableHttpClient httpClient;
|
||||||
|
|
||||||
|
@Autowired(required = false)
|
||||||
|
private RegistryBuilder registryBuilder;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(HttpClientConnectionManager.class)
|
||||||
|
public HttpClientConnectionManager connectionManager(
|
||||||
|
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
|
||||||
|
FeignHttpClientProperties httpClientProperties) {
|
||||||
|
final HttpClientConnectionManager connectionManager = connectionManagerFactory
|
||||||
|
.newConnectionManager(httpClientProperties.isDisableSslValidation(),
|
||||||
|
httpClientProperties.getMaxConnections(),
|
||||||
|
httpClientProperties.getMaxConnectionsPerRoute(),
|
||||||
|
httpClientProperties.getTimeToLive(),
|
||||||
|
httpClientProperties.getTimeToLiveUnit(), this.registryBuilder);
|
||||||
|
this.connectionManagerTimer.schedule(new TimerTask() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
connectionManager.closeExpiredConnections();
|
||||||
|
}
|
||||||
|
}, 30000, httpClientProperties.getConnectionTimerRepeat());
|
||||||
|
return connectionManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnProperty(value = "feign.compression.response.enabled",
|
||||||
|
havingValue = "true")
|
||||||
|
public CloseableHttpClient customHttpClient(
|
||||||
|
HttpClientConnectionManager httpClientConnectionManager,
|
||||||
|
FeignHttpClientProperties httpClientProperties) {
|
||||||
|
HttpClientBuilder builder = HttpClientBuilder.create().disableCookieManagement()
|
||||||
|
.useSystemProperties();
|
||||||
|
this.httpClient = createClient(builder, httpClientConnectionManager,
|
||||||
|
httpClientProperties);
|
||||||
|
return this.httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnProperty(value = "feign.compression.response.enabled",
|
||||||
|
havingValue = "false", matchIfMissing = true)
|
||||||
|
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory,
|
||||||
|
HttpClientConnectionManager httpClientConnectionManager,
|
||||||
|
FeignHttpClientProperties httpClientProperties) {
|
||||||
|
this.httpClient = createClient(httpClientFactory.createBuilder(),
|
||||||
|
httpClientConnectionManager, httpClientProperties);
|
||||||
|
return this.httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CloseableHttpClient createClient(HttpClientBuilder builder,
|
||||||
|
HttpClientConnectionManager httpClientConnectionManager,
|
||||||
|
FeignHttpClientProperties httpClientProperties) {
|
||||||
|
RequestConfig defaultRequestConfig = RequestConfig.custom()
|
||||||
|
.setConnectTimeout(httpClientProperties.getConnectionTimeout())
|
||||||
|
.setRedirectsEnabled(httpClientProperties.isFollowRedirects()).build();
|
||||||
|
CloseableHttpClient httpClient = builder
|
||||||
|
.setDefaultRequestConfig(defaultRequestConfig)
|
||||||
|
.setConnectionManager(httpClientConnectionManager).build();
|
||||||
|
return httpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreDestroy
|
||||||
|
public void destroy() throws Exception {
|
||||||
|
this.connectionManagerTimer.cancel();
|
||||||
|
if (this.httpClient != null) {
|
||||||
|
this.httpClient.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+79
@@ -0,0 +1,79 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2019 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.clientconfig;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.annotation.PreDestroy;
|
||||||
|
|
||||||
|
import okhttp3.ConnectionPool;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
|
||||||
|
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
|
||||||
|
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defualt configuration for {@link OkHttpClient}.
|
||||||
|
*
|
||||||
|
* @author Ryan Baxter
|
||||||
|
* @author Marcin Grzejszczak
|
||||||
|
* @author Spencer Gibb
|
||||||
|
* @author Olga Maciaszek-Sharma
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
|
||||||
|
public class OkHttpFeignConfiguration {
|
||||||
|
|
||||||
|
private okhttp3.OkHttpClient okHttpClient;
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean(ConnectionPool.class)
|
||||||
|
public ConnectionPool httpClientConnectionPool(
|
||||||
|
FeignHttpClientProperties httpClientProperties,
|
||||||
|
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
|
||||||
|
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
|
||||||
|
Long timeToLive = httpClientProperties.getTimeToLive();
|
||||||
|
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
|
||||||
|
return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
|
||||||
|
ConnectionPool connectionPool,
|
||||||
|
FeignHttpClientProperties httpClientProperties) {
|
||||||
|
Boolean followRedirects = httpClientProperties.isFollowRedirects();
|
||||||
|
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
|
||||||
|
this.okHttpClient = httpClientFactory
|
||||||
|
.createBuilder(httpClientProperties.isDisableSslValidation())
|
||||||
|
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
|
||||||
|
.followRedirects(followRedirects).connectionPool(connectionPool).build();
|
||||||
|
return this.okHttpClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@PreDestroy
|
||||||
|
public void destroy() {
|
||||||
|
if (this.okHttpClient != null) {
|
||||||
|
this.okHttpClient.dispatcher().executorService().shutdown();
|
||||||
|
this.okHttpClient.connectionPool().evictAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+8
-8
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2017 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -18,6 +18,7 @@ package org.springframework.cloud.openfeign.encoding;
|
|||||||
|
|
||||||
import feign.RequestInterceptor;
|
import feign.RequestInterceptor;
|
||||||
import feign.RequestTemplate;
|
import feign.RequestTemplate;
|
||||||
|
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -34,7 +35,6 @@ public abstract class BaseRequestInterceptor implements RequestInterceptor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new instance of {@link BaseRequestInterceptor}.
|
* Creates new instance of {@link BaseRequestInterceptor}.
|
||||||
*
|
|
||||||
* @param properties the encoding properties
|
* @param properties the encoding properties
|
||||||
*/
|
*/
|
||||||
protected BaseRequestInterceptor(FeignClientEncodingProperties properties) {
|
protected BaseRequestInterceptor(FeignClientEncodingProperties properties) {
|
||||||
@@ -44,12 +44,12 @@ public abstract class BaseRequestInterceptor implements RequestInterceptor {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the header if it wasn't yet specified.
|
* Adds the header if it wasn't yet specified.
|
||||||
*
|
|
||||||
* @param requestTemplate the request
|
* @param requestTemplate the request
|
||||||
* @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, String... values) {
|
protected void addHeader(RequestTemplate requestTemplate, String name,
|
||||||
|
String... values) {
|
||||||
|
|
||||||
if (!requestTemplate.headers().containsKey(name)) {
|
if (!requestTemplate.headers().containsKey(name)) {
|
||||||
requestTemplate.header(name, values);
|
requestTemplate.header(name, values);
|
||||||
@@ -57,7 +57,7 @@ public abstract class BaseRequestInterceptor implements RequestInterceptor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected FeignClientEncodingProperties getProperties() {
|
protected FeignClientEncodingProperties getProperties() {
|
||||||
return properties;
|
return this.properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-6
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2017 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -39,15 +39,18 @@ 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", matchIfMissing = false)
|
@ConditionalOnProperty(value = "feign.compression.response.enabled",
|
||||||
//The OK HTTP client uses "transparent" compression.
|
matchIfMissing = false)
|
||||||
//If the accept-encoding header is present it disable transparent compression
|
// The OK HTTP client uses "transparent" compression.
|
||||||
|
// If the accept-encoding header is present it disable transparent compression
|
||||||
@ConditionalOnMissingBean(type = "okhttp3.OkHttpClient")
|
@ConditionalOnMissingBean(type = "okhttp3.OkHttpClient")
|
||||||
@AutoConfigureAfter(FeignAutoConfiguration.class)
|
@AutoConfigureAfter(FeignAutoConfiguration.class)
|
||||||
public class FeignAcceptGzipEncodingAutoConfiguration {
|
public class FeignAcceptGzipEncodingAutoConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public FeignAcceptGzipEncodingInterceptor feignAcceptGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
|
public FeignAcceptGzipEncodingInterceptor feignAcceptGzipEncodingInterceptor(
|
||||||
|
FeignClientEncodingProperties properties) {
|
||||||
return new FeignAcceptGzipEncodingInterceptor(properties);
|
return new FeignAcceptGzipEncodingInterceptor(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+12
-10
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2017 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -19,10 +19,11 @@ package org.springframework.cloud.openfeign.encoding;
|
|||||||
import feign.RequestTemplate;
|
import feign.RequestTemplate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables the HTTP response payload compression by specifying the {@code Accept-Encoding} headers.
|
* Enables the HTTP response payload compression by specifying the {@code Accept-Encoding}
|
||||||
* Although this does not yet mean that the requests will be compressed, it requires the remote server
|
* headers. Although this does not yet mean that the requests will be compressed, it
|
||||||
* to understand the header and be configured to compress responses. Still no all responses might be compressed
|
* requires the remote server to understand the header and be configured to compress
|
||||||
* based on the media type matching and other factors like the response content length.
|
* responses. Still no all responses might be compressed based on the media type matching
|
||||||
|
* and other factors like the response content length.
|
||||||
*
|
*
|
||||||
* @author Jakub Narloch
|
* @author Jakub Narloch
|
||||||
*/
|
*/
|
||||||
@@ -30,10 +31,10 @@ 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(FeignClientEncodingProperties properties) {
|
protected FeignAcceptGzipEncodingInterceptor(
|
||||||
|
FeignClientEncodingProperties properties) {
|
||||||
super(properties);
|
super(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,7 +44,8 @@ public class FeignAcceptGzipEncodingInterceptor extends BaseRequestInterceptor {
|
|||||||
@Override
|
@Override
|
||||||
public void apply(RequestTemplate template) {
|
public void apply(RequestTemplate template) {
|
||||||
|
|
||||||
addHeader(template, HttpEncoding.ACCEPT_ENCODING_HEADER, HttpEncoding.GZIP_ENCODING,
|
addHeader(template, HttpEncoding.ACCEPT_ENCODING_HEADER,
|
||||||
HttpEncoding.DEFLATE_ENCODING);
|
HttpEncoding.GZIP_ENCODING, HttpEncoding.DEFLATE_ENCODING);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-16
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2017 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -16,11 +16,11 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.openfeign.encoding;
|
package org.springframework.cloud.openfeign.encoding;
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Feign encoding properties.
|
* The Feign encoding properties.
|
||||||
*
|
*
|
||||||
@@ -32,7 +32,8 @@ public class FeignClientEncodingProperties {
|
|||||||
/**
|
/**
|
||||||
* The list of supported mime types.
|
* The list of supported mime types.
|
||||||
*/
|
*/
|
||||||
private String[] mimeTypes = new String[]{"text/xml", "application/xml", "application/json"};
|
private String[] mimeTypes = new String[] { "text/xml", "application/xml",
|
||||||
|
"application/json" };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The minimum threshold content size.
|
* The minimum threshold content size.
|
||||||
@@ -40,7 +41,7 @@ public class FeignClientEncodingProperties {
|
|||||||
private int minRequestSize = 2048;
|
private int minRequestSize = 2048;
|
||||||
|
|
||||||
public String[] getMimeTypes() {
|
public String[] getMimeTypes() {
|
||||||
return mimeTypes;
|
return this.mimeTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMimeTypes(String[] mimeTypes) {
|
public void setMimeTypes(String[] mimeTypes) {
|
||||||
@@ -48,7 +49,7 @@ public class FeignClientEncodingProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getMinRequestSize() {
|
public int getMinRequestSize() {
|
||||||
return minRequestSize;
|
return this.minRequestSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMinRequestSize(int minRequestSize) {
|
public void setMinRequestSize(int minRequestSize) {
|
||||||
@@ -57,24 +58,28 @@ public class FeignClientEncodingProperties {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) {
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
FeignClientEncodingProperties that = (FeignClientEncodingProperties) o;
|
FeignClientEncodingProperties that = (FeignClientEncodingProperties) o;
|
||||||
return Arrays.equals(mimeTypes, that.mimeTypes) &&
|
return Arrays.equals(this.mimeTypes, that.mimeTypes)
|
||||||
Objects.equals(minRequestSize, that.minRequestSize);
|
&& Objects.equals(this.minRequestSize, that.minRequestSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(mimeTypes, minRequestSize);
|
return Objects.hash(this.mimeTypes, this.minRequestSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return new StringBuilder("FeignClientEncodingProperties{")
|
return new StringBuilder("FeignClientEncodingProperties{").append("mimeTypes=")
|
||||||
.append("mimeTypes=").append(Arrays.toString(mimeTypes)).append(", ")
|
.append(Arrays.toString(this.mimeTypes)).append(", ")
|
||||||
.append("minRequestSize=").append(minRequestSize)
|
.append("minRequestSize=").append(this.minRequestSize).append("}")
|
||||||
.append("}").toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-6
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2017 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -39,15 +39,18 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
@EnableConfigurationProperties(FeignClientEncodingProperties.class)
|
@EnableConfigurationProperties(FeignClientEncodingProperties.class)
|
||||||
@ConditionalOnClass(Feign.class)
|
@ConditionalOnClass(Feign.class)
|
||||||
@ConditionalOnBean(Client.class)
|
@ConditionalOnBean(Client.class)
|
||||||
//The OK HTTP client uses "transparent" compression.
|
// The OK HTTP client uses "transparent" compression.
|
||||||
//If the content-encoding header is present it disable transparent compression
|
// If the content-encoding header is present it disable transparent compression
|
||||||
@ConditionalOnMissingBean(type = "okhttp3.OkHttpClient")
|
@ConditionalOnMissingBean(type = "okhttp3.OkHttpClient")
|
||||||
@ConditionalOnProperty(value = "feign.compression.request.enabled", matchIfMissing = false)
|
@ConditionalOnProperty(value = "feign.compression.request.enabled",
|
||||||
|
matchIfMissing = false)
|
||||||
@AutoConfigureAfter(FeignAutoConfiguration.class)
|
@AutoConfigureAfter(FeignAutoConfiguration.class)
|
||||||
public class FeignContentGzipEncodingAutoConfiguration {
|
public class FeignContentGzipEncodingAutoConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public FeignContentGzipEncodingInterceptor feignContentGzipEncodingInterceptor(FeignClientEncodingProperties properties) {
|
public FeignContentGzipEncodingInterceptor feignContentGzipEncodingInterceptor(
|
||||||
|
FeignClientEncodingProperties properties) {
|
||||||
return new FeignContentGzipEncodingInterceptor(properties);
|
return new FeignContentGzipEncodingInterceptor(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-14
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2017 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -16,13 +16,14 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.openfeign.encoding;
|
package org.springframework.cloud.openfeign.encoding;
|
||||||
|
|
||||||
import feign.RequestTemplate;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import feign.RequestTemplate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enables the HTTP request payload compression by specifying the {@code Content-Encoding} headers.
|
* Enables the HTTP request payload compression by specifying the {@code Content-Encoding}
|
||||||
|
* headers.
|
||||||
*
|
*
|
||||||
* @author Jakub Narloch
|
* @author Jakub Narloch
|
||||||
*/
|
*/
|
||||||
@@ -30,10 +31,10 @@ 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(FeignClientEncodingProperties properties) {
|
protected FeignContentGzipEncodingInterceptor(
|
||||||
|
FeignClientEncodingProperties properties) {
|
||||||
super(properties);
|
super(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,14 +45,13 @@ 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, HttpEncoding.GZIP_ENCODING,
|
addHeader(template, HttpEncoding.CONTENT_ENCODING_HEADER,
|
||||||
HttpEncoding.DEFLATE_ENCODING);
|
HttpEncoding.GZIP_ENCODING, HttpEncoding.DEFLATE_ENCODING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the request requires GZIP compression.
|
* Returns whether the request requires GZIP compression.
|
||||||
*
|
|
||||||
* @param template the request template
|
* @param template the request template
|
||||||
* @return true if request requires compression, false otherwise
|
* @return true if request requires compression, false otherwise
|
||||||
*/
|
*/
|
||||||
@@ -64,7 +64,6 @@ public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the request content length exceed configured minimum size.
|
* Returns whether the request content length exceed configured minimum size.
|
||||||
*
|
|
||||||
* @param contentLength the content length header value
|
* @param contentLength the content length header value
|
||||||
* @return true if length is grater than minimum size, false otherwise
|
* @return true if length is grater than minimum size, false otherwise
|
||||||
*/
|
*/
|
||||||
@@ -78,14 +77,14 @@ public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor
|
|||||||
final String strLen = contentLength.iterator().next();
|
final String strLen = contentLength.iterator().next();
|
||||||
final long length = Long.parseLong(strLen);
|
final long length = Long.parseLong(strLen);
|
||||||
return length > getProperties().getMinRequestSize();
|
return length > getProperties().getMinRequestSize();
|
||||||
} catch (NumberFormatException ex) {
|
}
|
||||||
|
catch (NumberFormatException ex) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the content mime types matches the configures mime types.
|
* Returns whether the content mime types matches the configures mime types.
|
||||||
*
|
|
||||||
* @param contentTypes the content types
|
* @param contentTypes the content types
|
||||||
* @return true if any specified content type matches the request content types
|
* @return true if any specified content type matches the request content types
|
||||||
*/
|
*/
|
||||||
@@ -94,7 +93,8 @@ public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getProperties().getMimeTypes() == null || getProperties().getMimeTypes().length == 0) {
|
if (getProperties().getMimeTypes() == null
|
||||||
|
|| 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;
|
||||||
}
|
}
|
||||||
@@ -107,4 +107,5 @@ public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2017 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -52,4 +52,5 @@ public interface HttpEncoding {
|
|||||||
* The Deflate encoding.
|
* The Deflate encoding.
|
||||||
*/
|
*/
|
||||||
String DEFLATE_ENCODING = "deflate";
|
String DEFLATE_ENCODING = "deflate";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+96
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2016-2019 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.hateoas;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.ObjectProvider;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||||
|
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.ConditionalOnWebApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration;
|
||||||
|
import org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.hateoas.RepresentationModel;
|
||||||
|
import org.springframework.hateoas.mediatype.MessageResolver;
|
||||||
|
import org.springframework.hateoas.mediatype.hal.CurieProvider;
|
||||||
|
import org.springframework.hateoas.mediatype.hal.DefaultCurieProvider;
|
||||||
|
import org.springframework.hateoas.mediatype.hal.HalConfiguration;
|
||||||
|
import org.springframework.hateoas.mediatype.hal.Jackson2HalModule;
|
||||||
|
import org.springframework.hateoas.server.LinkRelationProvider;
|
||||||
|
import org.springframework.hateoas.server.mvc.TypeConstrainedMappingJackson2HttpMessageConverter;
|
||||||
|
|
||||||
|
import static org.springframework.hateoas.MediaTypes.HAL_JSON;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Hector Espert
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConditionalOnWebApplication
|
||||||
|
@ConditionalOnClass(RepresentationModel.class)
|
||||||
|
@AutoConfigureAfter({ JacksonAutoConfiguration.class,
|
||||||
|
HttpMessageConvertersAutoConfiguration.class,
|
||||||
|
RepositoryRestMvcAutoConfiguration.class })
|
||||||
|
@AutoConfigureBefore(HypermediaAutoConfiguration.class)
|
||||||
|
public class FeignHalAutoConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public TypeConstrainedMappingJackson2HttpMessageConverter halJacksonHttpMessageConverter(
|
||||||
|
ObjectProvider<ObjectMapper> objectMapper,
|
||||||
|
ObjectProvider<HalConfiguration> halConfiguration,
|
||||||
|
ObjectProvider<MessageResolver> messageResolver,
|
||||||
|
ObjectProvider<CurieProvider> curieProvider,
|
||||||
|
ObjectProvider<LinkRelationProvider> linkRelationProvider) {
|
||||||
|
|
||||||
|
ObjectMapper mapper = objectMapper.getIfAvailable(ObjectMapper::new).copy();
|
||||||
|
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
|
||||||
|
|
||||||
|
HalConfiguration configuration = halConfiguration
|
||||||
|
.getIfAvailable(HalConfiguration::new);
|
||||||
|
|
||||||
|
CurieProvider curieProviderInstance = curieProvider
|
||||||
|
.getIfAvailable(() -> new DefaultCurieProvider(Collections.emptyMap()));
|
||||||
|
|
||||||
|
Jackson2HalModule.HalHandlerInstantiator halHandlerInstantiator = new Jackson2HalModule.HalHandlerInstantiator(
|
||||||
|
linkRelationProvider.getIfAvailable(), curieProviderInstance,
|
||||||
|
messageResolver.getIfAvailable(), configuration);
|
||||||
|
|
||||||
|
mapper.setHandlerInstantiator(halHandlerInstantiator);
|
||||||
|
|
||||||
|
if (!Jackson2HalModule.isAlreadyRegisteredIn(mapper)) {
|
||||||
|
Jackson2HalModule halModule = new Jackson2HalModule();
|
||||||
|
mapper.registerModule(halModule);
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeConstrainedMappingJackson2HttpMessageConverter converter = new TypeConstrainedMappingJackson2HttpMessageConverter(
|
||||||
|
RepresentationModel.class);
|
||||||
|
converter.setSupportedMediaTypes(Arrays.asList(HAL_JSON));
|
||||||
|
converter.setObjectMapper(mapper);
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2019 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 feign.Client;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration instantiating a {@link BlockingLoadBalancerClient}-based {@link Client}
|
||||||
|
* object that uses {@link Client.Default} under the hood.
|
||||||
|
*
|
||||||
|
* @author Olga Maciaszek-Sharma
|
||||||
|
* @since 2.2.0
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
class DefaultFeignLoadBalancerConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public Client feignClient(BlockingLoadBalancerClient loadBalancerClient) {
|
||||||
|
return new FeignBlockingLoadBalancerClient(new Client.Default(null, null),
|
||||||
|
loadBalancerClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+86
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2019 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.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
|
import feign.Client;
|
||||||
|
import feign.Request;
|
||||||
|
import feign.Response;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import org.springframework.cloud.client.ServiceInstance;
|
||||||
|
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link Client} implementation that uses {@link BlockingLoadBalancerClient} to select
|
||||||
|
* a {@link ServiceInstance} to use while resolving the request host.
|
||||||
|
*
|
||||||
|
* @author Olga Maciaszek-Sharma
|
||||||
|
* @since 2.2.0
|
||||||
|
*/
|
||||||
|
class FeignBlockingLoadBalancerClient implements Client {
|
||||||
|
|
||||||
|
private static final Log LOG = LogFactory
|
||||||
|
.getLog(FeignBlockingLoadBalancerClient.class);
|
||||||
|
|
||||||
|
private final Client delegate;
|
||||||
|
|
||||||
|
private final BlockingLoadBalancerClient loadBalancerClient;
|
||||||
|
|
||||||
|
FeignBlockingLoadBalancerClient(Client delegate,
|
||||||
|
BlockingLoadBalancerClient loadBalancerClient) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
this.loadBalancerClient = loadBalancerClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Response execute(Request request, Request.Options options) throws IOException {
|
||||||
|
final URI originalUri = URI.create(request.url());
|
||||||
|
String serviceId = originalUri.getHost();
|
||||||
|
Assert.state(serviceId != null,
|
||||||
|
"Request URI does not contain a valid hostname: " + originalUri);
|
||||||
|
ServiceInstance instance = loadBalancerClient.choose(serviceId);
|
||||||
|
if (instance == null) {
|
||||||
|
String message = "Load balancer does not contain an instance for the service "
|
||||||
|
+ serviceId;
|
||||||
|
if (LOG.isWarnEnabled()) {
|
||||||
|
LOG.warn(message);
|
||||||
|
}
|
||||||
|
return Response.builder().request(request)
|
||||||
|
.status(HttpStatus.SERVICE_UNAVAILABLE.value())
|
||||||
|
.body(message, StandardCharsets.UTF_8).build();
|
||||||
|
}
|
||||||
|
String reconstructedUrl = loadBalancerClient.reconstructURI(instance, originalUri)
|
||||||
|
.toString();
|
||||||
|
Request newRequest = Request.create(request.httpMethod(), reconstructedUrl,
|
||||||
|
request.headers(), request.requestBody());
|
||||||
|
|
||||||
|
return delegate.execute(newRequest, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Visible for tests
|
||||||
|
Client getDelegate() {
|
||||||
|
return delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+57
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2019 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 feign.Client;
|
||||||
|
import feign.Feign;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
import org.springframework.cloud.loadbalancer.blocking.client.BlockingLoadBalancerClient;
|
||||||
|
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
||||||
|
import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration;
|
||||||
|
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An autoconfiguration that instantiates {@link BlockingLoadBalancerClient}-based
|
||||||
|
* implementations of {@link Client}. In order to use this load-balancing mechanism, the
|
||||||
|
* Ribbon-based implementation has to be disabled by setting
|
||||||
|
* <code>spring.cloud.loadbalancer.ribbon.enabled</code> to <code>true</code>.
|
||||||
|
*
|
||||||
|
* @author Olga Maciaszek-Sharma
|
||||||
|
* @since 2.2.0
|
||||||
|
*/
|
||||||
|
@ConditionalOnClass(Feign.class)
|
||||||
|
@ConditionalOnBean(BlockingLoadBalancerClient.class)
|
||||||
|
@AutoConfigureBefore(FeignAutoConfiguration.class)
|
||||||
|
@AutoConfigureAfter(FeignRibbonClientAutoConfiguration.class)
|
||||||
|
@EnableConfigurationProperties(FeignHttpClientProperties.class)
|
||||||
|
@Configuration
|
||||||
|
// 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({ HttpClientFeignLoadBalancerConfiguration.class,
|
||||||
|
OkHttpFeignLoadBalancerConfiguration.class,
|
||||||
|
DefaultFeignLoadBalancerConfiguration.class })
|
||||||
|
class FeignLoadBalancerAutoConfiguration {
|
||||||
|
|
||||||
|
}
|
||||||
+55
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2019 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 feign.Client;
|
||||||
|
import feign.httpclient.ApacheHttpClient;
|
||||||
|
import org.apache.http.client.HttpClient;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||||
|
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.loadbalancer.blocking.client.BlockingLoadBalancerClient;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration instantiating a {@link BlockingLoadBalancerClient}-based {@link Client}
|
||||||
|
* object that uses {@link ApacheHttpClient} under the hood.
|
||||||
|
*
|
||||||
|
* @author Olga Maciaszek-Sharma
|
||||||
|
* @since 2.2.0
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConditionalOnClass(ApacheHttpClient.class)
|
||||||
|
@ConditionalOnBean(BlockingLoadBalancerClient.class)
|
||||||
|
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
|
||||||
|
@Import(HttpClientFeignConfiguration.class)
|
||||||
|
class HttpClientFeignLoadBalancerConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public Client feignClient(BlockingLoadBalancerClient loadBalancerClient,
|
||||||
|
HttpClient httpClient) {
|
||||||
|
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
|
||||||
|
return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+54
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2019 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 feign.Client;
|
||||||
|
import feign.okhttp.OkHttpClient;
|
||||||
|
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||||
|
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.loadbalancer.blocking.client.BlockingLoadBalancerClient;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration instantiating a {@link BlockingLoadBalancerClient}-based {@link Client}
|
||||||
|
* object that uses {@link OkHttpClient} under the hood.
|
||||||
|
*
|
||||||
|
* @author Olga Maciaszek-Sharma
|
||||||
|
* @since 2.2.0
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConditionalOnClass(OkHttpClient.class)
|
||||||
|
@ConditionalOnProperty("feign.okhttp.enabled")
|
||||||
|
@ConditionalOnBean(BlockingLoadBalancerClient.class)
|
||||||
|
@Import(OkHttpFeignConfiguration.class)
|
||||||
|
class OkHttpFeignLoadBalancerConfiguration {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
public Client feignClient(okhttp3.OkHttpClient okHttpClient,
|
||||||
|
BlockingLoadBalancerClient loadBalancerClient) {
|
||||||
|
OkHttpClient delegate = new OkHttpClient(okHttpClient);
|
||||||
|
return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+18
-13
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2015 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -18,14 +18,14 @@ package org.springframework.cloud.openfeign.ribbon;
|
|||||||
|
|
||||||
import java.util.Map;
|
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.client.loadbalancer.LoadBalancedRetryFactory;
|
||||||
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
import org.springframework.cloud.netflix.ribbon.ServerIntrospector;
|
||||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||||
import org.springframework.util.ConcurrentReferenceHashMap;
|
import org.springframework.util.ConcurrentReferenceHashMap;
|
||||||
|
|
||||||
import com.netflix.client.config.IClientConfig;
|
|
||||||
import com.netflix.loadbalancer.ILoadBalancer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory for SpringLoadBalancer instances that caches the entries created.
|
* Factory for SpringLoadBalancer instances that caches the entries created.
|
||||||
*
|
*
|
||||||
@@ -36,8 +36,9 @@ import com.netflix.loadbalancer.ILoadBalancer;
|
|||||||
*/
|
*/
|
||||||
public class CachingSpringLoadBalancerFactory {
|
public class CachingSpringLoadBalancerFactory {
|
||||||
|
|
||||||
private final SpringClientFactory factory;
|
protected final SpringClientFactory factory;
|
||||||
private LoadBalancedRetryFactory loadBalancedRetryFactory = null;
|
|
||||||
|
protected LoadBalancedRetryFactory loadBalancedRetryFactory = null;
|
||||||
|
|
||||||
private volatile Map<String, FeignLoadBalancer> cache = new ConcurrentReferenceHashMap<>();
|
private volatile Map<String, FeignLoadBalancer> cache = new ConcurrentReferenceHashMap<>();
|
||||||
|
|
||||||
@@ -45,23 +46,27 @@ public class CachingSpringLoadBalancerFactory {
|
|||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CachingSpringLoadBalancerFactory(SpringClientFactory factory, LoadBalancedRetryFactory loadBalancedRetryPolicyFactory) {
|
public CachingSpringLoadBalancerFactory(SpringClientFactory factory,
|
||||||
|
LoadBalancedRetryFactory loadBalancedRetryPolicyFactory) {
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
this.loadBalancedRetryFactory = loadBalancedRetryPolicyFactory;
|
this.loadBalancedRetryFactory = loadBalancedRetryPolicyFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FeignLoadBalancer create(String clientName) {
|
public FeignLoadBalancer create(String clientName) {
|
||||||
FeignLoadBalancer client = this.cache.get(clientName);
|
FeignLoadBalancer client = this.cache.get(clientName);
|
||||||
if(client != null) {
|
if (client != null) {
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
IClientConfig config = this.factory.getClientConfig(clientName);
|
IClientConfig config = this.factory.getClientConfig(clientName);
|
||||||
ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
|
ILoadBalancer lb = this.factory.getLoadBalancer(clientName);
|
||||||
ServerIntrospector serverIntrospector = this.factory.getInstance(clientName, ServerIntrospector.class);
|
ServerIntrospector serverIntrospector = this.factory.getInstance(clientName,
|
||||||
client = loadBalancedRetryFactory != null ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
|
ServerIntrospector.class);
|
||||||
loadBalancedRetryFactory) : new FeignLoadBalancer(lb, config, serverIntrospector);
|
client = this.loadBalancedRetryFactory != null
|
||||||
|
? new RetryableFeignLoadBalancer(lb, config, serverIntrospector,
|
||||||
|
this.loadBalancedRetryFactory)
|
||||||
|
: new FeignLoadBalancer(lb, config, serverIntrospector);
|
||||||
this.cache.put(clientName, client);
|
this.cache.put(clientName, client);
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-6
@@ -1,23 +1,23 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2017 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign.ribbon;
|
package org.springframework.cloud.openfeign.ribbon;
|
||||||
|
|
||||||
import feign.Client;
|
import feign.Client;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
@@ -28,11 +28,13 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
class DefaultFeignLoadBalancedConfiguration {
|
class DefaultFeignLoadBalancedConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
|
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
|
||||||
SpringClientFactory clientFactory) {
|
SpringClientFactory clientFactory) {
|
||||||
return new LoadBalancerFeignClient(new Client.Default(null, null),
|
return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory,
|
||||||
cachingFactory, clientFactory);
|
clientFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+41
-23
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2015 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -16,10 +16,6 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.openfeign.ribbon;
|
package org.springframework.cloud.openfeign.ribbon;
|
||||||
|
|
||||||
import feign.Client;
|
|
||||||
import feign.Request;
|
|
||||||
import feign.Response;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -29,11 +25,6 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
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 com.netflix.client.AbstractLoadBalancerAwareClient;
|
import com.netflix.client.AbstractLoadBalancerAwareClient;
|
||||||
import com.netflix.client.ClientException;
|
import com.netflix.client.ClientException;
|
||||||
import com.netflix.client.ClientRequest;
|
import com.netflix.client.ClientRequest;
|
||||||
@@ -43,6 +34,15 @@ import com.netflix.client.RetryHandler;
|
|||||||
import com.netflix.client.config.IClientConfig;
|
import com.netflix.client.config.IClientConfig;
|
||||||
import com.netflix.loadbalancer.ILoadBalancer;
|
import com.netflix.loadbalancer.ILoadBalancer;
|
||||||
import com.netflix.loadbalancer.Server;
|
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;
|
import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToSecureConnectionIfNeeded;
|
||||||
|
|
||||||
@@ -51,18 +51,23 @@ import static org.springframework.cloud.netflix.ribbon.RibbonUtils.updateToSecur
|
|||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
* @author Ryan Baxter
|
* @author Ryan Baxter
|
||||||
* @author Tim Ysewyn
|
* @author Tim Ysewyn
|
||||||
|
* @author Olga Maciaszek-Sharma
|
||||||
*/
|
*/
|
||||||
public class FeignLoadBalancer extends
|
public class FeignLoadBalancer extends
|
||||||
AbstractLoadBalancerAwareClient<FeignLoadBalancer.RibbonRequest, FeignLoadBalancer.RibbonResponse> {
|
AbstractLoadBalancerAwareClient<FeignLoadBalancer.RibbonRequest, FeignLoadBalancer.RibbonResponse> {
|
||||||
|
|
||||||
private final RibbonProperties ribbon;
|
private final RibbonProperties ribbon;
|
||||||
|
|
||||||
protected int connectTimeout;
|
protected int connectTimeout;
|
||||||
|
|
||||||
protected int readTimeout;
|
protected int readTimeout;
|
||||||
|
|
||||||
protected IClientConfig clientConfig;
|
protected IClientConfig clientConfig;
|
||||||
|
|
||||||
protected ServerIntrospector serverIntrospector;
|
protected ServerIntrospector serverIntrospector;
|
||||||
|
|
||||||
public FeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig,
|
public FeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig,
|
||||||
ServerIntrospector serverIntrospector) {
|
ServerIntrospector serverIntrospector) {
|
||||||
super(lb, clientConfig);
|
super(lb, clientConfig);
|
||||||
this.setRetryHandler(RetryHandler.DEFAULT);
|
this.setRetryHandler(RetryHandler.DEFAULT);
|
||||||
this.clientConfig = clientConfig;
|
this.clientConfig = clientConfig;
|
||||||
@@ -79,8 +84,7 @@ public class FeignLoadBalancer extends
|
|||||||
Request.Options options;
|
Request.Options options;
|
||||||
if (configOverride != null) {
|
if (configOverride != null) {
|
||||||
RibbonProperties override = RibbonProperties.from(configOverride);
|
RibbonProperties override = RibbonProperties.from(configOverride);
|
||||||
options = new Request.Options(
|
options = new Request.Options(override.connectTimeout(this.connectTimeout),
|
||||||
override.connectTimeout(this.connectTimeout),
|
|
||||||
override.readTimeout(this.readTimeout));
|
override.readTimeout(this.readTimeout));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -97,7 +101,7 @@ public class FeignLoadBalancer extends
|
|||||||
return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
|
return new RequestSpecificRetryHandler(true, true, this.getRetryHandler(),
|
||||||
requestConfig);
|
requestConfig);
|
||||||
}
|
}
|
||||||
if (!request.toRequest().method().equals("GET")) {
|
if (!request.toRequest().httpMethod().name().equals("GET")) {
|
||||||
return new RequestSpecificRetryHandler(true, false, this.getRetryHandler(),
|
return new RequestSpecificRetryHandler(true, false, this.getRetryHandler(),
|
||||||
requestConfig);
|
requestConfig);
|
||||||
}
|
}
|
||||||
@@ -109,16 +113,18 @@ public class FeignLoadBalancer extends
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URI reconstructURIWithServer(Server server, URI original) {
|
public URI reconstructURIWithServer(Server server, URI original) {
|
||||||
URI uri = updateToSecureConnectionIfNeeded(original, this.clientConfig, this.serverIntrospector, server);
|
URI uri = updateToSecureConnectionIfNeeded(original, this.clientConfig,
|
||||||
|
this.serverIntrospector, server);
|
||||||
return super.reconstructURIWithServer(server, uri);
|
return super.reconstructURIWithServer(server, uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class RibbonRequest extends ClientRequest implements Cloneable {
|
protected static class RibbonRequest extends ClientRequest implements Cloneable {
|
||||||
|
|
||||||
private final Request request;
|
private final Request request;
|
||||||
|
|
||||||
private final Client client;
|
private final Client client;
|
||||||
|
|
||||||
RibbonRequest(Client client, Request request, URI uri) {
|
protected RibbonRequest(Client client, Request request, URI uri) {
|
||||||
this.client = client;
|
this.client = client;
|
||||||
setUri(uri);
|
setUri(uri);
|
||||||
this.request = toRequest(request);
|
this.request = toRequest(request);
|
||||||
@@ -127,7 +133,8 @@ public class FeignLoadBalancer extends
|
|||||||
private Request toRequest(Request request) {
|
private Request toRequest(Request request) {
|
||||||
Map<String, Collection<String>> headers = new LinkedHashMap<>(
|
Map<String, Collection<String>> headers = new LinkedHashMap<>(
|
||||||
request.headers());
|
request.headers());
|
||||||
return Request.create(request.method(),getUri().toASCIIString(),headers,request.body(),request.charset());
|
return Request.create(request.httpMethod(), getUri().toASCIIString(), headers,
|
||||||
|
request.requestBody());
|
||||||
}
|
}
|
||||||
|
|
||||||
Request toRequest() {
|
Request toRequest() {
|
||||||
@@ -142,7 +149,8 @@ public class FeignLoadBalancer extends
|
|||||||
return new HttpRequest() {
|
return new HttpRequest() {
|
||||||
@Override
|
@Override
|
||||||
public HttpMethod getMethod() {
|
public HttpMethod getMethod() {
|
||||||
return HttpMethod.resolve(RibbonRequest.this.toRequest().method());
|
return HttpMethod
|
||||||
|
.resolve(RibbonRequest.this.toRequest().httpMethod().name());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -158,8 +166,9 @@ public class FeignLoadBalancer extends
|
|||||||
@Override
|
@Override
|
||||||
public HttpHeaders getHeaders() {
|
public HttpHeaders getHeaders() {
|
||||||
Map<String, List<String>> headers = new HashMap<>();
|
Map<String, List<String>> headers = new HashMap<>();
|
||||||
Map<String, Collection<String>> feignHeaders = RibbonRequest.this.toRequest().headers();
|
Map<String, Collection<String>> feignHeaders = RibbonRequest.this
|
||||||
for(String key : feignHeaders.keySet()) {
|
.toRequest().headers();
|
||||||
|
for (String key : feignHeaders.keySet()) {
|
||||||
headers.put(key, new ArrayList<String>(feignHeaders.get(key)));
|
headers.put(key, new ArrayList<String>(feignHeaders.get(key)));
|
||||||
}
|
}
|
||||||
HttpHeaders httpHeaders = new HttpHeaders();
|
HttpHeaders httpHeaders = new HttpHeaders();
|
||||||
@@ -170,19 +179,28 @@ public class FeignLoadBalancer extends
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Request getRequest() {
|
||||||
|
return this.request;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Client getClient() {
|
||||||
|
return this.client;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object clone() {
|
public Object clone() {
|
||||||
return new RibbonRequest(this.client, this.request, getUri());
|
return new RibbonRequest(this.client, this.request, getUri());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static class RibbonResponse implements IResponse {
|
protected static class RibbonResponse implements IResponse {
|
||||||
|
|
||||||
private final URI uri;
|
private final URI uri;
|
||||||
|
|
||||||
private final Response response;
|
private final Response response;
|
||||||
|
|
||||||
RibbonResponse(URI uri, Response response) {
|
protected RibbonResponse(URI uri, Response response) {
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
this.response = response;
|
this.response = response;
|
||||||
}
|
}
|
||||||
@@ -225,4 +243,4 @@ public class FeignLoadBalancer extends
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+46
-36
@@ -1,19 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright 2013-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* * Copyright 2013-2016 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.
|
||||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
* You may obtain a copy of the License at
|
||||||
* * you may not use this file except in compliance with the License.
|
|
||||||
* * You may obtain a copy of the License at
|
|
||||||
* *
|
|
||||||
* * http://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.
|
|
||||||
*
|
*
|
||||||
|
* 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;
|
package org.springframework.cloud.openfeign.ribbon;
|
||||||
@@ -34,9 +32,13 @@ import org.springframework.retry.RetryContext;
|
|||||||
* @author Ryan Baxter
|
* @author Ryan Baxter
|
||||||
*/
|
*/
|
||||||
public class FeignRetryPolicy extends InterceptorRetryPolicy {
|
public class FeignRetryPolicy extends InterceptorRetryPolicy {
|
||||||
|
|
||||||
private HttpRequest request;
|
private HttpRequest request;
|
||||||
|
|
||||||
private String serviceId;
|
private String serviceId;
|
||||||
public FeignRetryPolicy(HttpRequest request, LoadBalancedRetryPolicy policy, ServiceInstanceChooser serviceInstanceChooser, String serviceName) {
|
|
||||||
|
public FeignRetryPolicy(HttpRequest request, LoadBalancedRetryPolicy policy,
|
||||||
|
ServiceInstanceChooser serviceInstanceChooser, String serviceName) {
|
||||||
super(request, policy, serviceInstanceChooser, serviceName);
|
super(request, policy, serviceInstanceChooser, serviceName);
|
||||||
this.request = request;
|
this.request = request;
|
||||||
this.serviceId = serviceName;
|
this.serviceId = serviceName;
|
||||||
@@ -45,17 +47,18 @@ public class FeignRetryPolicy extends InterceptorRetryPolicy {
|
|||||||
@Override
|
@Override
|
||||||
public boolean canRetry(RetryContext context) {
|
public boolean canRetry(RetryContext context) {
|
||||||
/*
|
/*
|
||||||
* In InterceptorRetryPolicy.canRetry we ask the LoadBalancer to choose a server if one is not
|
* In InterceptorRetryPolicy.canRetry we ask the LoadBalancer to choose a server
|
||||||
* set in the retry context and then return true. RetryTemplat calls the canRetry method of
|
* if one is not set in the retry context and then return true. RetryTemplat calls
|
||||||
* the policy even on its first execution. So the fact that we didnt have a service instance set
|
* the canRetry method of the policy even on its first execution. So the fact that
|
||||||
* in the RetryContext signaled that it was the first execution and we should return true.
|
* 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
|
* In the Feign scenario, Feign as actually already queried the load balancer for
|
||||||
* and we set that service instance in the context when we call the open method of the policy. So in
|
* a service instance and we set that service instance in the context when we call
|
||||||
* the Feign case we just return true if the retry count is 0 indicating we haven't yet made a failed
|
* the open method of the policy. So in the Feign case we just return true if the
|
||||||
* request.
|
* retry count is 0 indicating we haven't yet made a failed request.
|
||||||
*/
|
*/
|
||||||
if(context.getRetryCount() == 0) {
|
if (context.getRetryCount() == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return super.canRetry(context);
|
return super.canRetry(context);
|
||||||
@@ -64,20 +67,25 @@ public class FeignRetryPolicy extends InterceptorRetryPolicy {
|
|||||||
@Override
|
@Override
|
||||||
public RetryContext open(RetryContext parent) {
|
public RetryContext open(RetryContext parent) {
|
||||||
/*
|
/*
|
||||||
* With Feign (unlike Ribbon) the request already has the URI for the service instance
|
* With Feign (unlike Ribbon) the request already has the URI for the service
|
||||||
* we are going to make the request to, so extract that information and set the service
|
* instance we are going to make the request to, so extract that information and
|
||||||
* instance in the context. In the Ribbon scenario the URI in the request object still has
|
* set the service instance in the context. In the Ribbon scenario the URI in the
|
||||||
* the service id so we choose and set the service instance later on.
|
* request object still has the service id so we choose and set the service
|
||||||
|
* instance later on.
|
||||||
*/
|
*/
|
||||||
LoadBalancedRetryContext context = new LoadBalancedRetryContext(parent, this.request);
|
LoadBalancedRetryContext context = new LoadBalancedRetryContext(parent,
|
||||||
context.setServiceInstance(new FeignRetryPolicyServiceInstance(serviceId, request));
|
this.request);
|
||||||
|
context.setServiceInstance(
|
||||||
|
new FeignRetryPolicyServiceInstance(this.serviceId, this.request));
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
class FeignRetryPolicyServiceInstance implements ServiceInstance {
|
class FeignRetryPolicyServiceInstance implements ServiceInstance {
|
||||||
|
|
||||||
private String serviceId;
|
private String serviceId;
|
||||||
|
|
||||||
private HttpRequest request;
|
private HttpRequest request;
|
||||||
|
|
||||||
private Map<String, String> metadata;
|
private Map<String, String> metadata;
|
||||||
|
|
||||||
FeignRetryPolicyServiceInstance(String serviceId, HttpRequest request) {
|
FeignRetryPolicyServiceInstance(String serviceId, HttpRequest request) {
|
||||||
@@ -88,32 +96,34 @@ public class FeignRetryPolicy extends InterceptorRetryPolicy {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getServiceId() {
|
public String getServiceId() {
|
||||||
return serviceId;
|
return this.serviceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getHost() {
|
public String getHost() {
|
||||||
return request.getURI().getHost();
|
return this.request.getURI().getHost();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getPort() {
|
public int getPort() {
|
||||||
return request.getURI().getPort();
|
return this.request.getURI().getPort();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSecure() {
|
public boolean isSecure() {
|
||||||
return "https".equals(request.getURI().getScheme());
|
return "https".equals(this.request.getURI().getScheme());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public URI getUri() {
|
public URI getUri() {
|
||||||
return request.getURI();
|
return this.request.getURI();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getMetadata() {
|
public Map<String, String> getMetadata() {
|
||||||
return metadata;
|
return this.metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
|
|||||||
+16
-12
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2017 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -16,37 +16,41 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.openfeign.ribbon;
|
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.AutoConfigureBefore;
|
||||||
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.context.properties.EnableConfigurationProperties;
|
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.netflix.ribbon.SpringClientFactory;
|
||||||
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
||||||
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
|
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
|
||||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.context.annotation.Primary;
|
import org.springframework.context.annotation.Primary;
|
||||||
|
|
||||||
import com.netflix.loadbalancer.ILoadBalancer;
|
|
||||||
|
|
||||||
import feign.Feign;
|
|
||||||
import feign.Request;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Autoconfiguration to be activated if Feign is in use and needs to be use Ribbon as a
|
* Autoconfiguration to be activated if Feign is in use and needs to be use Ribbon as a
|
||||||
* load balancer.
|
* load balancer.
|
||||||
*
|
*
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
|
* @author Olga Maciaszek-Sharma
|
||||||
*/
|
*/
|
||||||
@ConditionalOnClass({ ILoadBalancer.class, Feign.class })
|
@ConditionalOnClass({ ILoadBalancer.class, Feign.class })
|
||||||
|
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.ribbon.enabled",
|
||||||
|
matchIfMissing = true)
|
||||||
@Configuration
|
@Configuration
|
||||||
@AutoConfigureBefore(FeignAutoConfiguration.class)
|
@AutoConfigureBefore(FeignAutoConfiguration.class)
|
||||||
@EnableConfigurationProperties({ FeignHttpClientProperties.class })
|
@EnableConfigurationProperties({ FeignHttpClientProperties.class })
|
||||||
//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 https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
|
// see
|
||||||
|
// https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
|
||||||
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
|
@Import({ HttpClientFeignLoadBalancedConfiguration.class,
|
||||||
OkHttpFeignLoadBalancedConfiguration.class,
|
OkHttpFeignLoadBalancedConfiguration.class,
|
||||||
DefaultFeignLoadBalancedConfiguration.class })
|
DefaultFeignLoadBalancedConfiguration.class })
|
||||||
@@ -66,8 +70,7 @@ public class FeignRibbonClientAutoConfiguration {
|
|||||||
@ConditionalOnMissingBean
|
@ConditionalOnMissingBean
|
||||||
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
|
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
|
||||||
public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(
|
public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory(
|
||||||
SpringClientFactory factory,
|
SpringClientFactory factory, LoadBalancedRetryFactory retryFactory) {
|
||||||
LoadBalancedRetryFactory retryFactory) {
|
|
||||||
return new CachingSpringLoadBalancerFactory(factory, retryFactory);
|
return new CachingSpringLoadBalancerFactory(factory, retryFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,4 +79,5 @@ public class FeignRibbonClientAutoConfiguration {
|
|||||||
public Request.Options feignRequestOptions() {
|
public Request.Options feignRequestOptions() {
|
||||||
return LoadBalancerFeignClient.DEFAULT_OPTIONS;
|
return LoadBalancerFeignClient.DEFAULT_OPTIONS;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-91
@@ -1,128 +1,50 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2017 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign.ribbon;
|
package org.springframework.cloud.openfeign.ribbon;
|
||||||
|
|
||||||
import feign.Client;
|
import feign.Client;
|
||||||
import feign.httpclient.ApacheHttpClient;
|
import feign.httpclient.ApacheHttpClient;
|
||||||
|
|
||||||
import java.util.Timer;
|
|
||||||
import java.util.TimerTask;
|
|
||||||
import javax.annotation.PreDestroy;
|
|
||||||
import org.apache.http.client.HttpClient;
|
import org.apache.http.client.HttpClient;
|
||||||
import org.apache.http.client.config.RequestConfig;
|
|
||||||
import org.apache.http.config.RegistryBuilder;
|
|
||||||
import org.apache.http.conn.HttpClientConnectionManager;
|
|
||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
|
||||||
import org.apache.http.impl.client.HttpClientBuilder;
|
|
||||||
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.ConditionalOnProperty;
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory;
|
|
||||||
import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory;
|
|
||||||
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
|
|
||||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
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.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
|
* @author Olga Maciaszek-Sharma
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnClass(ApacheHttpClient.class)
|
@ConditionalOnClass(ApacheHttpClient.class)
|
||||||
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
|
@ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true)
|
||||||
|
@Import(HttpClientFeignConfiguration.class)
|
||||||
class HttpClientFeignLoadBalancedConfiguration {
|
class HttpClientFeignLoadBalancedConfiguration {
|
||||||
|
|
||||||
@Configuration
|
@Bean
|
||||||
@ConditionalOnMissingBean(CloseableHttpClient.class)
|
@ConditionalOnMissingBean(Client.class)
|
||||||
protected static class HttpClientFeignConfiguration {
|
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
|
||||||
private final Timer connectionManagerTimer = new Timer(
|
SpringClientFactory clientFactory, HttpClient httpClient) {
|
||||||
"FeignApacheHttpClientConfiguration.connectionManagerTimer", true);
|
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
|
||||||
|
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
|
||||||
private CloseableHttpClient httpClient;
|
|
||||||
|
|
||||||
@Autowired(required = false)
|
|
||||||
private RegistryBuilder registryBuilder;
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean(HttpClientConnectionManager.class)
|
|
||||||
public HttpClientConnectionManager connectionManager(
|
|
||||||
ApacheHttpClientConnectionManagerFactory connectionManagerFactory,
|
|
||||||
FeignHttpClientProperties httpClientProperties) {
|
|
||||||
final HttpClientConnectionManager connectionManager = connectionManagerFactory
|
|
||||||
.newConnectionManager(httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(),
|
|
||||||
httpClientProperties.getMaxConnectionsPerRoute(),
|
|
||||||
httpClientProperties.getTimeToLive(),
|
|
||||||
httpClientProperties.getTimeToLiveUnit(), registryBuilder);
|
|
||||||
this.connectionManagerTimer.schedule(new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
connectionManager.closeExpiredConnections();
|
|
||||||
}
|
|
||||||
}, 30000, httpClientProperties.getConnectionTimerRepeat());
|
|
||||||
return connectionManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnProperty(value = "feign.compression.response.enabled", havingValue = "true")
|
|
||||||
public CloseableHttpClient customHttpClient(HttpClientConnectionManager httpClientConnectionManager,
|
|
||||||
FeignHttpClientProperties httpClientProperties) {
|
|
||||||
HttpClientBuilder builder = HttpClientBuilder.create().disableCookieManagement().useSystemProperties();
|
|
||||||
this.httpClient = createClient(builder, httpClientConnectionManager, httpClientProperties);
|
|
||||||
return this.httpClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnProperty(value = "feign.compression.response.enabled", havingValue = "false", matchIfMissing = true)
|
|
||||||
public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory, HttpClientConnectionManager httpClientConnectionManager,
|
|
||||||
FeignHttpClientProperties httpClientProperties) {
|
|
||||||
this.httpClient = createClient(httpClientFactory.createBuilder(), httpClientConnectionManager, httpClientProperties);
|
|
||||||
return this.httpClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CloseableHttpClient createClient(HttpClientBuilder builder, HttpClientConnectionManager httpClientConnectionManager,
|
|
||||||
FeignHttpClientProperties httpClientProperties) {
|
|
||||||
RequestConfig defaultRequestConfig = RequestConfig.custom()
|
|
||||||
.setConnectTimeout(httpClientProperties.getConnectionTimeout())
|
|
||||||
.setRedirectsEnabled(httpClientProperties.isFollowRedirects())
|
|
||||||
.build();
|
|
||||||
CloseableHttpClient httpClient = builder.setDefaultRequestConfig(defaultRequestConfig).
|
|
||||||
setConnectionManager(httpClientConnectionManager).build();
|
|
||||||
return httpClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreDestroy
|
|
||||||
public void destroy() throws Exception {
|
|
||||||
connectionManagerTimer.cancel();
|
|
||||||
if(httpClient != null) {
|
|
||||||
httpClient.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean(Client.class)
|
|
||||||
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
|
|
||||||
SpringClientFactory clientFactory, HttpClient httpClient) {
|
|
||||||
ApacheHttpClient delegate = new ApacheHttpClient(httpClient);
|
|
||||||
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+32
-26
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2015 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -19,17 +19,16 @@ package org.springframework.cloud.openfeign.ribbon;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
|
||||||
|
|
||||||
import com.netflix.client.ClientException;
|
import com.netflix.client.ClientException;
|
||||||
import com.netflix.client.config.CommonClientConfigKey;
|
import com.netflix.client.config.CommonClientConfigKey;
|
||||||
import com.netflix.client.config.DefaultClientConfigImpl;
|
import com.netflix.client.config.DefaultClientConfigImpl;
|
||||||
import com.netflix.client.config.IClientConfig;
|
import com.netflix.client.config.IClientConfig;
|
||||||
|
|
||||||
import feign.Client;
|
import feign.Client;
|
||||||
import feign.Request;
|
import feign.Request;
|
||||||
import feign.Response;
|
import feign.Response;
|
||||||
|
|
||||||
|
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
*
|
*
|
||||||
@@ -39,17 +38,37 @@ public class LoadBalancerFeignClient implements Client {
|
|||||||
static final Request.Options DEFAULT_OPTIONS = new Request.Options();
|
static final Request.Options DEFAULT_OPTIONS = new Request.Options();
|
||||||
|
|
||||||
private final Client delegate;
|
private final Client delegate;
|
||||||
|
|
||||||
private CachingSpringLoadBalancerFactory lbClientFactory;
|
private CachingSpringLoadBalancerFactory lbClientFactory;
|
||||||
|
|
||||||
private SpringClientFactory clientFactory;
|
private SpringClientFactory clientFactory;
|
||||||
|
|
||||||
public LoadBalancerFeignClient(Client delegate,
|
public LoadBalancerFeignClient(Client delegate,
|
||||||
CachingSpringLoadBalancerFactory lbClientFactory,
|
CachingSpringLoadBalancerFactory lbClientFactory,
|
||||||
SpringClientFactory clientFactory) {
|
SpringClientFactory clientFactory) {
|
||||||
this.delegate = delegate;
|
this.delegate = delegate;
|
||||||
this.lbClientFactory = lbClientFactory;
|
this.lbClientFactory = lbClientFactory;
|
||||||
this.clientFactory = clientFactory;
|
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
|
@Override
|
||||||
public Response execute(Request request, Request.Options options) throws IOException {
|
public Response execute(Request request, Request.Options options) throws IOException {
|
||||||
try {
|
try {
|
||||||
@@ -60,8 +79,8 @@ public class LoadBalancerFeignClient implements Client {
|
|||||||
this.delegate, request, uriWithoutHost);
|
this.delegate, request, uriWithoutHost);
|
||||||
|
|
||||||
IClientConfig requestConfig = getClientConfig(options, clientName);
|
IClientConfig requestConfig = getClientConfig(options, clientName);
|
||||||
return lbClient(clientName).executeWithLoadBalancer(ribbonRequest,
|
return lbClient(clientName)
|
||||||
requestConfig).toResponse();
|
.executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse();
|
||||||
}
|
}
|
||||||
catch (ClientException e) {
|
catch (ClientException e) {
|
||||||
IOException io = findIOException(e);
|
IOException io = findIOException(e);
|
||||||
@@ -76,7 +95,8 @@ public class LoadBalancerFeignClient implements Client {
|
|||||||
IClientConfig requestConfig;
|
IClientConfig requestConfig;
|
||||||
if (options == DEFAULT_OPTIONS) {
|
if (options == DEFAULT_OPTIONS) {
|
||||||
requestConfig = this.clientFactory.getClientConfig(clientName);
|
requestConfig = this.clientFactory.getClientConfig(clientName);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
requestConfig = new FeignOptionsClientConfig(options);
|
requestConfig = new FeignOptionsClientConfig(options);
|
||||||
}
|
}
|
||||||
return requestConfig;
|
return requestConfig;
|
||||||
@@ -96,28 +116,13 @@ public class LoadBalancerFeignClient implements Client {
|
|||||||
return this.delegate;
|
return this.delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
private FeignLoadBalancer lbClient(String clientName) {
|
private FeignLoadBalancer lbClient(String clientName) {
|
||||||
return this.lbClientFactory.create(clientName);
|
return this.lbClientFactory.create(clientName);
|
||||||
}
|
}
|
||||||
|
|
||||||
static class FeignOptionsClientConfig extends DefaultClientConfigImpl {
|
static class FeignOptionsClientConfig extends DefaultClientConfigImpl {
|
||||||
|
|
||||||
public FeignOptionsClientConfig(Request.Options options) {
|
FeignOptionsClientConfig(Request.Options options) {
|
||||||
setProperty(CommonClientConfigKey.ConnectTimeout,
|
setProperty(CommonClientConfigKey.ConnectTimeout,
|
||||||
options.connectTimeoutMillis());
|
options.connectTimeoutMillis());
|
||||||
setProperty(CommonClientConfigKey.ReadTimeout, options.readTimeoutMillis());
|
setProperty(CommonClientConfigKey.ReadTimeout, options.readTimeoutMillis());
|
||||||
@@ -134,4 +139,5 @@ public class LoadBalancerFeignClient implements Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-47
@@ -1,87 +1,49 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2017 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign.ribbon;
|
package org.springframework.cloud.openfeign.ribbon;
|
||||||
|
|
||||||
import feign.Client;
|
import feign.Client;
|
||||||
import feign.okhttp.OkHttpClient;
|
import feign.okhttp.OkHttpClient;
|
||||||
import okhttp3.ConnectionPool;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import javax.annotation.PreDestroy;
|
|
||||||
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.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory;
|
|
||||||
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
|
|
||||||
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
|
|
||||||
import org.springframework.cloud.netflix.ribbon.SpringClientFactory;
|
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.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
|
* @author Olga Maciaszek-Sharma
|
||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@ConditionalOnClass(OkHttpClient.class)
|
@ConditionalOnClass(OkHttpClient.class)
|
||||||
@ConditionalOnProperty(value = "feign.okhttp.enabled")
|
@ConditionalOnProperty("feign.okhttp.enabled")
|
||||||
|
@Import(OkHttpFeignConfiguration.class)
|
||||||
class OkHttpFeignLoadBalancedConfiguration {
|
class OkHttpFeignLoadBalancedConfiguration {
|
||||||
|
|
||||||
@Configuration
|
|
||||||
@ConditionalOnMissingBean(okhttp3.OkHttpClient.class)
|
|
||||||
protected static class OkHttpFeignConfiguration {
|
|
||||||
private okhttp3.OkHttpClient okHttpClient;
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
@ConditionalOnMissingBean(ConnectionPool.class)
|
|
||||||
public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties,
|
|
||||||
OkHttpClientConnectionPoolFactory connectionPoolFactory) {
|
|
||||||
Integer maxTotalConnections = httpClientProperties.getMaxConnections();
|
|
||||||
Long timeToLive = httpClientProperties.getTimeToLive();
|
|
||||||
TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit();
|
|
||||||
return connectionPoolFactory.create(maxTotalConnections, timeToLive, ttlUnit);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
|
||||||
public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory,
|
|
||||||
ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) {
|
|
||||||
Boolean followRedirects = httpClientProperties.isFollowRedirects();
|
|
||||||
Integer connectTimeout = httpClientProperties.getConnectionTimeout();
|
|
||||||
this.okHttpClient = httpClientFactory.createBuilder(httpClientProperties.isDisableSslValidation()).
|
|
||||||
connectTimeout(connectTimeout, TimeUnit.MILLISECONDS).
|
|
||||||
followRedirects(followRedirects).
|
|
||||||
connectionPool(connectionPool).build();
|
|
||||||
return this.okHttpClient;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreDestroy
|
|
||||||
public void destroy() {
|
|
||||||
if(okHttpClient != null) {
|
|
||||||
okHttpClient.dispatcher().executorService().shutdown();
|
|
||||||
okHttpClient.connectionPool().evictAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
@ConditionalOnMissingBean(Client.class)
|
@ConditionalOnMissingBean(Client.class)
|
||||||
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
|
public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory,
|
||||||
SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) {
|
SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) {
|
||||||
OkHttpClient delegate = new OkHttpClient(okHttpClient);
|
OkHttpClient delegate = new OkHttpClient(okHttpClient);
|
||||||
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
|
return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+58
-41
@@ -1,28 +1,32 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright 2013-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* * Copyright 2013-2016 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.
|
||||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
* You may obtain a copy of the License at
|
||||||
* * you may not use this file except in compliance with the License.
|
|
||||||
* * You may obtain a copy of the License at
|
|
||||||
* *
|
|
||||||
* * http://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.
|
|
||||||
*
|
*
|
||||||
|
* 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;
|
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.Request;
|
||||||
import feign.Response;
|
import feign.Response;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URI;
|
|
||||||
import org.springframework.cloud.client.ServiceInstance;
|
import org.springframework.cloud.client.ServiceInstance;
|
||||||
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;
|
||||||
@@ -40,71 +44,82 @@ 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.StreamUtils;
|
import org.springframework.util.StreamUtils;
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link FeignLoadBalancer} that leverages Spring Retry to retry failed requests.
|
* A {@link FeignLoadBalancer} that leverages Spring Retry to retry failed requests.
|
||||||
|
*
|
||||||
* @author Ryan Baxter
|
* @author Ryan Baxter
|
||||||
* @author Gang Li
|
* @author Gang Li
|
||||||
*/
|
*/
|
||||||
public class RetryableFeignLoadBalancer extends FeignLoadBalancer implements ServiceInstanceChooser {
|
public class RetryableFeignLoadBalancer extends FeignLoadBalancer
|
||||||
|
implements ServiceInstanceChooser {
|
||||||
|
|
||||||
private final LoadBalancedRetryFactory loadBalancedRetryFactory;
|
private final LoadBalancedRetryFactory loadBalancedRetryFactory;
|
||||||
|
|
||||||
public RetryableFeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig, ServerIntrospector serverIntrospector,
|
public RetryableFeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig,
|
||||||
LoadBalancedRetryFactory loadBalancedRetryFactory) {
|
ServerIntrospector serverIntrospector,
|
||||||
|
LoadBalancedRetryFactory loadBalancedRetryFactory) {
|
||||||
super(lb, clientConfig, serverIntrospector);
|
super(lb, clientConfig, serverIntrospector);
|
||||||
this.loadBalancedRetryFactory = loadBalancedRetryFactory;
|
this.loadBalancedRetryFactory = loadBalancedRetryFactory;
|
||||||
this.setRetryHandler(new DefaultLoadBalancerRetryHandler(clientConfig));
|
this.setRetryHandler(new DefaultLoadBalancerRetryHandler(clientConfig));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RibbonResponse execute(final RibbonRequest request, IClientConfig configOverride)
|
public RibbonResponse execute(final RibbonRequest request,
|
||||||
throws IOException {
|
IClientConfig configOverride) throws IOException {
|
||||||
final Request.Options options;
|
final Request.Options options;
|
||||||
if (configOverride != null) {
|
if (configOverride != null) {
|
||||||
RibbonProperties ribbon = RibbonProperties.from(configOverride);
|
RibbonProperties ribbon = RibbonProperties.from(configOverride);
|
||||||
options = new Request.Options(
|
options = new Request.Options(ribbon.connectTimeout(this.connectTimeout),
|
||||||
ribbon.connectTimeout(this.connectTimeout),
|
|
||||||
ribbon.readTimeout(this.readTimeout));
|
ribbon.readTimeout(this.readTimeout));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
options = new Request.Options(this.connectTimeout, this.readTimeout);
|
options = new Request.Options(this.connectTimeout, this.readTimeout);
|
||||||
}
|
}
|
||||||
final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryFactory.createRetryPolicy(this.getClientName(), this);
|
final LoadBalancedRetryPolicy retryPolicy = this.loadBalancedRetryFactory
|
||||||
|
.createRetryPolicy(this.getClientName(), this);
|
||||||
RetryTemplate retryTemplate = new RetryTemplate();
|
RetryTemplate retryTemplate = new RetryTemplate();
|
||||||
BackOffPolicy backOffPolicy = loadBalancedRetryFactory.createBackOffPolicy(this.getClientName());
|
BackOffPolicy backOffPolicy = this.loadBalancedRetryFactory
|
||||||
retryTemplate.setBackOffPolicy(backOffPolicy == null ? new NoBackOffPolicy() : backOffPolicy);
|
.createBackOffPolicy(this.getClientName());
|
||||||
RetryListener[] retryListeners = this.loadBalancedRetryFactory.createRetryListeners(this.getClientName());
|
retryTemplate.setBackOffPolicy(
|
||||||
|
backOffPolicy == null ? new NoBackOffPolicy() : backOffPolicy);
|
||||||
|
RetryListener[] retryListeners = this.loadBalancedRetryFactory
|
||||||
|
.createRetryListeners(this.getClientName());
|
||||||
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 FeignRetryPolicy(request.toHttpRequest(), retryPolicy, this, this.getClientName()));
|
: new FeignRetryPolicy(request.toHttpRequest(), retryPolicy, this,
|
||||||
|
this.getClientName()));
|
||||||
return retryTemplate.execute(new RetryCallback<RibbonResponse, IOException>() {
|
return retryTemplate.execute(new RetryCallback<RibbonResponse, IOException>() {
|
||||||
@Override
|
@Override
|
||||||
public RibbonResponse doWithRetry(RetryContext retryContext) throws IOException {
|
public RibbonResponse doWithRetry(RetryContext retryContext)
|
||||||
|
throws IOException {
|
||||||
Request feignRequest = null;
|
Request feignRequest = null;
|
||||||
//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
|
||||||
//extract the server and update the request being made
|
// extract the server and update the request being made
|
||||||
if (retryContext instanceof LoadBalancedRetryContext) {
|
if (retryContext instanceof LoadBalancedRetryContext) {
|
||||||
ServiceInstance service = ((LoadBalancedRetryContext) retryContext).getServiceInstance();
|
ServiceInstance service = ((LoadBalancedRetryContext) retryContext)
|
||||||
|
.getServiceInstance();
|
||||||
if (service != null) {
|
if (service != null) {
|
||||||
feignRequest = ((RibbonRequest) request.replaceUri(reconstructURIWithServer(new Server(service.getHost(), service.getPort()), request.getUri()))).toRequest();
|
feignRequest = ((RibbonRequest) request
|
||||||
|
.replaceUri(reconstructURIWithServer(
|
||||||
|
new Server(service.getHost(), service.getPort()),
|
||||||
|
request.getUri()))).toRequest();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (feignRequest == null) {
|
if (feignRequest == null) {
|
||||||
feignRequest = request.toRequest();
|
feignRequest = request.toRequest();
|
||||||
}
|
}
|
||||||
Response response = request.client().execute(feignRequest, options);
|
Response response = request.client().execute(feignRequest, options);
|
||||||
if (retryPolicy != null && retryPolicy.retryableStatusCode(response.status())) {
|
if (retryPolicy != null
|
||||||
byte[] byteArray = response.body() == null ? new byte[]{} : StreamUtils.copyToByteArray(response.body().asInputStream());
|
&& retryPolicy.retryableStatusCode(response.status())) {
|
||||||
|
byte[] byteArray = response.body() == null ? new byte[] {}
|
||||||
|
: StreamUtils
|
||||||
|
.copyToByteArray(response.body().asInputStream());
|
||||||
response.close();
|
response.close();
|
||||||
throw new RibbonResponseStatusCodeException(RetryableFeignLoadBalancer.this.clientName, response,
|
throw new RibbonResponseStatusCodeException(
|
||||||
|
RetryableFeignLoadBalancer.this.clientName, response,
|
||||||
byteArray, request.getUri());
|
byteArray, request.getUri());
|
||||||
}
|
}
|
||||||
return new RibbonResponse(request.getUri(), response);
|
return new RibbonResponse(request.getUri(), response);
|
||||||
@@ -120,7 +135,8 @@ public class RetryableFeignLoadBalancer extends FeignLoadBalancer implements Ser
|
|||||||
@Override
|
@Override
|
||||||
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
|
public RequestSpecificRetryHandler getRequestSpecificRetryHandler(
|
||||||
FeignLoadBalancer.RibbonRequest request, IClientConfig requestConfig) {
|
FeignLoadBalancer.RibbonRequest request, IClientConfig requestConfig) {
|
||||||
return new RequestSpecificRetryHandler(false, false, this.getRetryHandler(), requestConfig);
|
return new RequestSpecificRetryHandler(false, false, this.getRetryHandler(),
|
||||||
|
requestConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -128,4 +144,5 @@ public class RetryableFeignLoadBalancer extends FeignLoadBalancer implements Ser
|
|||||||
return new RibbonLoadBalancerClient.RibbonServer(serviceId,
|
return new RibbonLoadBalancerClient.RibbonServer(serviceId,
|
||||||
this.getLoadBalancer().chooseServer(serviceId));
|
this.getLoadBalancer().chooseServer(serviceId));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-11
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2018 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -13,33 +13,37 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign.ribbon;
|
package org.springframework.cloud.openfeign.ribbon;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
import feign.Response;
|
import feign.Response;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.net.URI;
|
|
||||||
import org.springframework.cloud.client.loadbalancer.RetryableStatusCodeException;
|
import org.springframework.cloud.client.loadbalancer.RetryableStatusCodeException;
|
||||||
import org.springframework.util.StreamUtils;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link RetryableStatusCodeException} for {@link Response}s
|
* A {@link RetryableStatusCodeException} for {@link Response}s.
|
||||||
|
*
|
||||||
* @author Ryan Baxter
|
* @author Ryan Baxter
|
||||||
*/
|
*/
|
||||||
public class RibbonResponseStatusCodeException extends RetryableStatusCodeException {
|
public class RibbonResponseStatusCodeException extends RetryableStatusCodeException {
|
||||||
private Response response;
|
|
||||||
|
|
||||||
public RibbonResponseStatusCodeException(String serviceId, Response response, byte[] body, URI uri) {
|
private final Response response;
|
||||||
|
|
||||||
|
public RibbonResponseStatusCodeException(String serviceId, Response response,
|
||||||
|
byte[] body, URI uri) {
|
||||||
super(serviceId, response.status(), response, uri);
|
super(serviceId, response.status(), response, uri);
|
||||||
this.response = Response.builder().body(new ByteArrayInputStream(body), body.length)
|
this.response = Response.builder()
|
||||||
|
.body(new ByteArrayInputStream(body), body.length)
|
||||||
.headers(response.headers()).reason(response.reason())
|
.headers(response.headers()).reason(response.reason())
|
||||||
.status(response.status()).request(response.request()).build();
|
.status(response.status()).request(response.request()).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response getResponse() {
|
public Response getResponse() {
|
||||||
return response;
|
return this.response;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+85
@@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2019 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.cloud.openfeign.support;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
|
import feign.FeignException;
|
||||||
|
import feign.Response;
|
||||||
|
import feign.Util;
|
||||||
|
import feign.codec.Decoder;
|
||||||
|
|
||||||
|
import org.springframework.cloud.openfeign.encoding.HttpEncoding;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When response is compressed as gzip, this decompresses and uses {@link SpringDecoder}
|
||||||
|
* to decode.
|
||||||
|
*
|
||||||
|
* @author Jaesik Kim
|
||||||
|
*/
|
||||||
|
public class DefaultGzipDecoder implements Decoder {
|
||||||
|
|
||||||
|
private Decoder decoder;
|
||||||
|
|
||||||
|
public DefaultGzipDecoder(Decoder decoder) {
|
||||||
|
this.decoder = decoder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object decode(final Response response, Type type)
|
||||||
|
throws IOException, FeignException {
|
||||||
|
Collection<String> encoding = response.headers()
|
||||||
|
.containsKey(HttpEncoding.CONTENT_ENCODING_HEADER)
|
||||||
|
? response.headers().get(HttpEncoding.CONTENT_ENCODING_HEADER)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (encoding != null) {
|
||||||
|
if (encoding.contains(HttpEncoding.GZIP_ENCODING)) {
|
||||||
|
String decompressedBody = decompress(response);
|
||||||
|
if (decompressedBody != null) {
|
||||||
|
Response decompressedResponse = response.toBuilder()
|
||||||
|
.body(decompressedBody.getBytes()).build();
|
||||||
|
return decoder.decode(decompressedResponse, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return decoder.decode(response, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String decompress(Response response) throws IOException {
|
||||||
|
if (response.body() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try (GZIPInputStream gzipInputStream = new GZIPInputStream(
|
||||||
|
response.body().asInputStream());
|
||||||
|
BufferedReader reader = new BufferedReader(
|
||||||
|
new InputStreamReader(gzipInputStream, Util.ISO_8859_1))) {
|
||||||
|
String outputString = "";
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
outputString += line;
|
||||||
|
}
|
||||||
|
return outputString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+59
@@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2019 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 feign.codec.Decoder;
|
||||||
|
import feign.optionals.OptionalDecoder;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.ObjectFactory;
|
||||||
|
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
|
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||||
|
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configures Default Gzip Decoder.
|
||||||
|
*
|
||||||
|
* @author Jaesik Kim
|
||||||
|
*/
|
||||||
|
@Configuration
|
||||||
|
@ConditionalOnProperty("feign.compression.response.enabled")
|
||||||
|
// The OK HTTP client uses "transparent" compression.
|
||||||
|
// If the accept-encoding header is present, it disables transparent compression
|
||||||
|
@ConditionalOnMissingBean(type = "okhttp3.OkHttpClient")
|
||||||
|
@AutoConfigureAfter(FeignAutoConfiguration.class)
|
||||||
|
public class DefaultGzipDecoderConfiguration {
|
||||||
|
|
||||||
|
private ObjectFactory<HttpMessageConverters> messageConverters;
|
||||||
|
|
||||||
|
public DefaultGzipDecoderConfiguration(
|
||||||
|
ObjectFactory<HttpMessageConverters> messageConverters) {
|
||||||
|
this.messageConverters = messageConverters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
@ConditionalOnProperty("feign.compression.response.useGzipDecoder")
|
||||||
|
public Decoder defaultGzipDecoder() {
|
||||||
|
return new OptionalDecoder(new ResponseEntityDecoder(
|
||||||
|
new DefaultGzipDecoder(new SpringDecoder(messageConverters))));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+29
-5
@@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2019 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;
|
package org.springframework.cloud.openfeign.support;
|
||||||
|
|
||||||
import com.netflix.hystrix.HystrixCommand;
|
import com.netflix.hystrix.HystrixCommand;
|
||||||
@@ -6,8 +22,11 @@ import com.netflix.hystrix.HystrixThreadPoolKey;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience class for implementing feign fallbacks that return {@link HystrixCommand}.
|
* 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}.
|
* Also useful for return types of {@link rx.Observable} and
|
||||||
* For those return types, just call {@link FallbackCommand#observe()} or {@link FallbackCommand#queue()} respectively.
|
* {@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
|
* @author Spencer Gibb
|
||||||
*/
|
*/
|
||||||
public class FallbackCommand<T> extends HystrixCommand<T> {
|
public class FallbackCommand<T> extends HystrixCommand<T> {
|
||||||
@@ -28,17 +47,21 @@ public class FallbackCommand<T> extends HystrixCommand<T> {
|
|||||||
this.result = result;
|
this.result = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FallbackCommand(T result, HystrixCommandGroupKey group, int executionIsolationThreadTimeoutInMilliseconds) {
|
public FallbackCommand(T result, HystrixCommandGroupKey group,
|
||||||
|
int executionIsolationThreadTimeoutInMilliseconds) {
|
||||||
super(group, executionIsolationThreadTimeoutInMilliseconds);
|
super(group, executionIsolationThreadTimeoutInMilliseconds);
|
||||||
this.result = result;
|
this.result = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FallbackCommand(T result, HystrixCommandGroupKey group, HystrixThreadPoolKey threadPool) {
|
public FallbackCommand(T result, HystrixCommandGroupKey group,
|
||||||
|
HystrixThreadPoolKey threadPool) {
|
||||||
super(group, threadPool);
|
super(group, threadPool);
|
||||||
this.result = result;
|
this.result = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FallbackCommand(T result, HystrixCommandGroupKey group, HystrixThreadPoolKey threadPool, int executionIsolationThreadTimeoutInMilliseconds) {
|
public FallbackCommand(T result, HystrixCommandGroupKey group,
|
||||||
|
HystrixThreadPoolKey threadPool,
|
||||||
|
int executionIsolationThreadTimeoutInMilliseconds) {
|
||||||
super(group, threadPool, executionIsolationThreadTimeoutInMilliseconds);
|
super(group, threadPool, executionIsolationThreadTimeoutInMilliseconds);
|
||||||
this.result = result;
|
this.result = result;
|
||||||
}
|
}
|
||||||
@@ -52,4 +75,5 @@ public class FallbackCommand<T> extends HystrixCommand<T> {
|
|||||||
protected T run() throws Exception {
|
protected T run() throws Exception {
|
||||||
return this.result;
|
return this.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+60
-21
@@ -1,24 +1,23 @@
|
|||||||
/*
|
/*
|
||||||
|
* Copyright 2013-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* * Copyright 2013-2016 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.
|
||||||
* * Licensed under the Apache License, Version 2.0 (the "License");
|
* You may obtain a copy of the License at
|
||||||
* * you may not use this file except in compliance with the License.
|
|
||||||
* * You may obtain a copy of the License at
|
|
||||||
* *
|
|
||||||
* * http://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.
|
|
||||||
*
|
*
|
||||||
|
* 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;
|
package org.springframework.cloud.openfeign.support;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,26 +25,65 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
|||||||
*/
|
*/
|
||||||
@ConfigurationProperties(prefix = "feign.httpclient")
|
@ConfigurationProperties(prefix = "feign.httpclient")
|
||||||
public class FeignHttpClientProperties {
|
public class FeignHttpClientProperties {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value for disabling SSL validation.
|
||||||
|
*/
|
||||||
public static final boolean DEFAULT_DISABLE_SSL_VALIDATION = false;
|
public static final boolean DEFAULT_DISABLE_SSL_VALIDATION = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value for max number od connections.
|
||||||
|
*/
|
||||||
public static final int DEFAULT_MAX_CONNECTIONS = 200;
|
public static final int DEFAULT_MAX_CONNECTIONS = 200;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value for max number od connections per route.
|
||||||
|
*/
|
||||||
public static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 50;
|
public static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 50;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value for time to live.
|
||||||
|
*/
|
||||||
public static final long DEFAULT_TIME_TO_LIVE = 900L;
|
public static final long DEFAULT_TIME_TO_LIVE = 900L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default time to live unit.
|
||||||
|
*/
|
||||||
public static final TimeUnit DEFAULT_TIME_TO_LIVE_UNIT = TimeUnit.SECONDS;
|
public static final TimeUnit DEFAULT_TIME_TO_LIVE_UNIT = TimeUnit.SECONDS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value for following redirects.
|
||||||
|
*/
|
||||||
public static final boolean DEFAULT_FOLLOW_REDIRECTS = true;
|
public static final boolean DEFAULT_FOLLOW_REDIRECTS = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value for connection timeout.
|
||||||
|
*/
|
||||||
public static final int DEFAULT_CONNECTION_TIMEOUT = 2000;
|
public static final int DEFAULT_CONNECTION_TIMEOUT = 2000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default value for connection timer repeat.
|
||||||
|
*/
|
||||||
public static final int DEFAULT_CONNECTION_TIMER_REPEAT = 3000;
|
public static final int DEFAULT_CONNECTION_TIMER_REPEAT = 3000;
|
||||||
|
|
||||||
private boolean disableSslValidation = DEFAULT_DISABLE_SSL_VALIDATION;
|
private boolean disableSslValidation = DEFAULT_DISABLE_SSL_VALIDATION;
|
||||||
|
|
||||||
private int maxConnections = DEFAULT_MAX_CONNECTIONS;
|
private int maxConnections = DEFAULT_MAX_CONNECTIONS;
|
||||||
|
|
||||||
private int maxConnectionsPerRoute = DEFAULT_MAX_CONNECTIONS_PER_ROUTE;
|
private int maxConnectionsPerRoute = DEFAULT_MAX_CONNECTIONS_PER_ROUTE;
|
||||||
|
|
||||||
private long timeToLive = DEFAULT_TIME_TO_LIVE;
|
private long timeToLive = DEFAULT_TIME_TO_LIVE;
|
||||||
|
|
||||||
private TimeUnit timeToLiveUnit = DEFAULT_TIME_TO_LIVE_UNIT;
|
private TimeUnit timeToLiveUnit = DEFAULT_TIME_TO_LIVE_UNIT;
|
||||||
|
|
||||||
private boolean followRedirects = DEFAULT_FOLLOW_REDIRECTS;
|
private boolean followRedirects = DEFAULT_FOLLOW_REDIRECTS;
|
||||||
|
|
||||||
private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
|
private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT;
|
||||||
|
|
||||||
private int connectionTimerRepeat = DEFAULT_CONNECTION_TIMER_REPEAT;
|
private int connectionTimerRepeat = DEFAULT_CONNECTION_TIMER_REPEAT;
|
||||||
|
|
||||||
public int getConnectionTimerRepeat() {
|
public int getConnectionTimerRepeat() {
|
||||||
return connectionTimerRepeat;
|
return this.connectionTimerRepeat;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setConnectionTimerRepeat(int connectionTimerRepeat) {
|
public void setConnectionTimerRepeat(int connectionTimerRepeat) {
|
||||||
@@ -53,7 +91,7 @@ public class FeignHttpClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDisableSslValidation() {
|
public boolean isDisableSslValidation() {
|
||||||
return disableSslValidation;
|
return this.disableSslValidation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDisableSslValidation(boolean disableSslValidation) {
|
public void setDisableSslValidation(boolean disableSslValidation) {
|
||||||
@@ -61,7 +99,7 @@ public class FeignHttpClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxConnections() {
|
public int getMaxConnections() {
|
||||||
return maxConnections;
|
return this.maxConnections;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMaxConnections(int maxConnections) {
|
public void setMaxConnections(int maxConnections) {
|
||||||
@@ -69,7 +107,7 @@ public class FeignHttpClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getMaxConnectionsPerRoute() {
|
public int getMaxConnectionsPerRoute() {
|
||||||
return maxConnectionsPerRoute;
|
return this.maxConnectionsPerRoute;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMaxConnectionsPerRoute(int maxConnectionsPerRoute) {
|
public void setMaxConnectionsPerRoute(int maxConnectionsPerRoute) {
|
||||||
@@ -77,7 +115,7 @@ public class FeignHttpClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public long getTimeToLive() {
|
public long getTimeToLive() {
|
||||||
return timeToLive;
|
return this.timeToLive;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTimeToLive(long timeToLive) {
|
public void setTimeToLive(long timeToLive) {
|
||||||
@@ -85,7 +123,7 @@ public class FeignHttpClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public TimeUnit getTimeToLiveUnit() {
|
public TimeUnit getTimeToLiveUnit() {
|
||||||
return timeToLiveUnit;
|
return this.timeToLiveUnit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTimeToLiveUnit(TimeUnit timeToLiveUnit) {
|
public void setTimeToLiveUnit(TimeUnit timeToLiveUnit) {
|
||||||
@@ -93,7 +131,7 @@ public class FeignHttpClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isFollowRedirects() {
|
public boolean isFollowRedirects() {
|
||||||
return followRedirects;
|
return this.followRedirects;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFollowRedirects(boolean followRedirects) {
|
public void setFollowRedirects(boolean followRedirects) {
|
||||||
@@ -101,10 +139,11 @@ public class FeignHttpClientProperties {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getConnectionTimeout() {
|
public int getConnectionTimeout() {
|
||||||
return connectionTimeout;
|
return this.connectionTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setConnectionTimeout(int connectionTimeout) {
|
public void setConnectionTimeout(int connectionTimeout) {
|
||||||
this.connectionTimeout = connectionTimeout;
|
this.connectionTimeout = connectionTimeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-3
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2015 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -24,10 +24,17 @@ import java.util.Map;
|
|||||||
|
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
|
|
||||||
|
import static java.util.Optional.ofNullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
|
* @author Olga Maciaszek-Sharma
|
||||||
*/
|
*/
|
||||||
public class FeignUtils {
|
public final class FeignUtils {
|
||||||
|
|
||||||
|
private FeignUtils() {
|
||||||
|
throw new IllegalStateException("Can't instantiate a utility class");
|
||||||
|
}
|
||||||
|
|
||||||
static HttpHeaders getHttpHeaders(Map<String, Collection<String>> headers) {
|
static HttpHeaders getHttpHeaders(Map<String, Collection<String>> headers) {
|
||||||
HttpHeaders httpHeaders = new HttpHeaders();
|
HttpHeaders httpHeaders = new HttpHeaders();
|
||||||
@@ -47,4 +54,12 @@ public class FeignUtils {
|
|||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Collection<String> addTemplateParameter(Collection<String> possiblyNull,
|
||||||
|
String paramName) {
|
||||||
|
Collection<String> params = ofNullable(possiblyNull).map(ArrayList::new)
|
||||||
|
.orElse(new ArrayList<>());
|
||||||
|
params.add(String.format("{%s}", paramName));
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+171
@@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2018 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.cloud.openfeign.support;
|
||||||
|
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.fasterxml.jackson.core.Version;
|
||||||
|
import com.fasterxml.jackson.databind.Module;
|
||||||
|
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
|
||||||
|
|
||||||
|
import org.springframework.data.domain.Page;
|
||||||
|
import org.springframework.data.domain.PageImpl;
|
||||||
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This jackson module provides support to deserialize spring {@link Page} objects.
|
||||||
|
*
|
||||||
|
* @author Pascal Büttiker
|
||||||
|
*/
|
||||||
|
public class PageJacksonModule extends Module {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getModuleName() {
|
||||||
|
return "PageJacksonModule";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Version version() {
|
||||||
|
return new Version(0, 1, 0, "", null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setupModule(SetupContext context) {
|
||||||
|
context.setMixInAnnotations(Page.class, PageMixIn.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonDeserialize(as = SimplePageImpl.class)
|
||||||
|
private interface PageMixIn {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static class SimplePageImpl<T> implements Page<T> {
|
||||||
|
|
||||||
|
private final Page<T> delegate;
|
||||||
|
|
||||||
|
SimplePageImpl(@JsonProperty("content") List<T> content,
|
||||||
|
@JsonProperty("page") int number, @JsonProperty("size") int size,
|
||||||
|
@JsonProperty("totalElements") long totalElements) {
|
||||||
|
delegate = new PageImpl<>(content, PageRequest.of(number, size),
|
||||||
|
totalElements);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
@Override
|
||||||
|
public int getTotalPages() {
|
||||||
|
return delegate.getTotalPages();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
@Override
|
||||||
|
public long getTotalElements() {
|
||||||
|
return delegate.getTotalElements();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty("page")
|
||||||
|
@Override
|
||||||
|
public int getNumber() {
|
||||||
|
return delegate.getNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
@Override
|
||||||
|
public int getSize() {
|
||||||
|
return delegate.getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
@Override
|
||||||
|
public int getNumberOfElements() {
|
||||||
|
return delegate.getNumberOfElements();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
@Override
|
||||||
|
public List<T> getContent() {
|
||||||
|
return delegate.getContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
@Override
|
||||||
|
public boolean hasContent() {
|
||||||
|
return delegate.hasContent();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@Override
|
||||||
|
public Sort getSort() {
|
||||||
|
return delegate.getSort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
@Override
|
||||||
|
public boolean isFirst() {
|
||||||
|
return delegate.isFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
@Override
|
||||||
|
public boolean isLast() {
|
||||||
|
return delegate.isLast();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return delegate.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@Override
|
||||||
|
public boolean hasPrevious() {
|
||||||
|
return delegate.hasPrevious();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@Override
|
||||||
|
public Pageable nextPageable() {
|
||||||
|
return delegate.nextPageable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@Override
|
||||||
|
public Pageable previousPageable() {
|
||||||
|
return delegate.previousPageable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@Override
|
||||||
|
public <S> Page<S> map(Function<? super T, ? extends S> converter) {
|
||||||
|
return delegate.map(converter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@Override
|
||||||
|
public Iterator<T> iterator() {
|
||||||
|
return delegate.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+134
@@ -0,0 +1,134 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2018 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.cloud.openfeign.support;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import feign.RequestTemplate;
|
||||||
|
import feign.codec.EncodeException;
|
||||||
|
import feign.codec.Encoder;
|
||||||
|
|
||||||
|
import org.springframework.data.domain.Pageable;
|
||||||
|
import org.springframework.data.domain.Sort;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides support for encoding spring Pageable via composition.
|
||||||
|
*
|
||||||
|
* @author Pascal Büttiker
|
||||||
|
*/
|
||||||
|
public class PageableSpringEncoder implements Encoder {
|
||||||
|
|
||||||
|
private final Encoder delegate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page index parameter name.
|
||||||
|
*/
|
||||||
|
private String pageParameter = "page";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page size parameter name.
|
||||||
|
*/
|
||||||
|
private String sizeParameter = "size";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort parameter name.
|
||||||
|
*/
|
||||||
|
private String sortParameter = "sort";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new PageableSpringEncoder with the given delegate for fallback. If no
|
||||||
|
* delegate is provided and this encoder cant handle the request, an EncodeException
|
||||||
|
* is thrown.
|
||||||
|
* @param delegate The optional delegate.
|
||||||
|
*/
|
||||||
|
public PageableSpringEncoder(Encoder delegate) {
|
||||||
|
this.delegate = delegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPageParameter(String pageParameter) {
|
||||||
|
this.pageParameter = pageParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSizeParameter(String sizeParameter) {
|
||||||
|
this.sizeParameter = sizeParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSortParameter(String sortParameter) {
|
||||||
|
this.sortParameter = sortParameter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void encode(Object object, Type bodyType, RequestTemplate template)
|
||||||
|
throws EncodeException {
|
||||||
|
|
||||||
|
if (supports(object)) {
|
||||||
|
if (object instanceof Pageable) {
|
||||||
|
Pageable pageable = (Pageable) object;
|
||||||
|
|
||||||
|
if (pageable.isPaged()) {
|
||||||
|
template.query(pageParameter, pageable.getPageNumber() + "");
|
||||||
|
template.query(sizeParameter, pageable.getPageSize() + "");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pageable.getSort() != null) {
|
||||||
|
applySort(template, pageable.getSort());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (object instanceof Sort) {
|
||||||
|
Sort sort = (Sort) object;
|
||||||
|
applySort(template, sort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (delegate != null) {
|
||||||
|
delegate.encode(object, bodyType, template);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new EncodeException(
|
||||||
|
"PageableSpringEncoder does not support the given object "
|
||||||
|
+ object.getClass()
|
||||||
|
+ " and no delegate was provided for fallback!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applySort(RequestTemplate template, Sort sort) {
|
||||||
|
Collection<String> existingSorts = template.queries().get("sort");
|
||||||
|
List<String> sortQueries = existingSorts != null ? new ArrayList<>(existingSorts)
|
||||||
|
: new ArrayList<>();
|
||||||
|
if (!sortParameter.equals("sort")) {
|
||||||
|
existingSorts = template.queries().get(sortParameter);
|
||||||
|
if (existingSorts != null) {
|
||||||
|
sortQueries.addAll(existingSorts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Sort.Order order : sort) {
|
||||||
|
sortQueries.add(order.getProperty() + "," + order.getDirection());
|
||||||
|
}
|
||||||
|
if (!sortQueries.isEmpty()) {
|
||||||
|
template.query(sortParameter, sortQueries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean supports(Object object) {
|
||||||
|
return object instanceof Pageable || object instanceof Sort;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+30
-12
@@ -1,3 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2019 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;
|
package org.springframework.cloud.openfeign.support;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -5,20 +21,21 @@ import java.lang.reflect.ParameterizedType;
|
|||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
import feign.FeignException;
|
||||||
|
import feign.Response;
|
||||||
|
import feign.codec.Decoder;
|
||||||
|
|
||||||
import org.springframework.http.HttpEntity;
|
import org.springframework.http.HttpEntity;
|
||||||
import org.springframework.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.util.LinkedMultiValueMap;
|
import org.springframework.util.LinkedMultiValueMap;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
|
|
||||||
import feign.FeignException;
|
|
||||||
import feign.Response;
|
|
||||||
import feign.codec.Decoder;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decoder adds compatibility for Spring MVC's ResponseEntity to any other decoder via
|
* Decoder adds compatibility for Spring MVC's ResponseEntity to any other decoder via
|
||||||
* composition.
|
* composition.
|
||||||
* @author chadjaros
|
*
|
||||||
|
* @author chad jaros
|
||||||
*/
|
*/
|
||||||
public class ResponseEntityDecoder implements Decoder {
|
public class ResponseEntityDecoder implements Decoder {
|
||||||
|
|
||||||
@@ -29,12 +46,12 @@ public class ResponseEntityDecoder implements Decoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object decode(final Response response, Type type) throws IOException,
|
public Object decode(final Response response, Type type)
|
||||||
FeignException {
|
throws IOException, FeignException {
|
||||||
|
|
||||||
if (isParameterizeHttpEntity(type)) {
|
if (isParameterizeHttpEntity(type)) {
|
||||||
type = ((ParameterizedType) type).getActualTypeArguments()[0];
|
type = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||||
Object decodedObject = decoder.decode(response, type);
|
Object decodedObject = this.decoder.decode(response, type);
|
||||||
|
|
||||||
return createResponse(decodedObject, response);
|
return createResponse(decodedObject, response);
|
||||||
}
|
}
|
||||||
@@ -42,7 +59,7 @@ public class ResponseEntityDecoder implements Decoder {
|
|||||||
return createResponse(null, response);
|
return createResponse(null, response);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return decoder.decode(response, type);
|
return this.decoder.decode(response, type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +86,8 @@ 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, HttpStatus.valueOf(response
|
return new ResponseEntity<>((T) instance, headers,
|
||||||
.status()));
|
HttpStatus.valueOf(response.status()));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
|
|||||||
+12
-12
@@ -1,30 +1,32 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2017 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign.support;
|
package org.springframework.cloud.openfeign.support;
|
||||||
|
|
||||||
import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHeaders;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.lang.reflect.ParameterizedType;
|
import java.lang.reflect.ParameterizedType;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.lang.reflect.WildcardType;
|
import java.lang.reflect.WildcardType;
|
||||||
|
|
||||||
|
import feign.FeignException;
|
||||||
|
import feign.Response;
|
||||||
|
import feign.codec.DecodeException;
|
||||||
|
import feign.codec.Decoder;
|
||||||
|
|
||||||
import org.springframework.beans.factory.ObjectFactory;
|
import org.springframework.beans.factory.ObjectFactory;
|
||||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
@@ -32,10 +34,7 @@ import org.springframework.http.HttpStatus;
|
|||||||
import org.springframework.http.client.ClientHttpResponse;
|
import org.springframework.http.client.ClientHttpResponse;
|
||||||
import org.springframework.web.client.HttpMessageConverterExtractor;
|
import org.springframework.web.client.HttpMessageConverterExtractor;
|
||||||
|
|
||||||
import feign.FeignException;
|
import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHeaders;
|
||||||
import feign.Response;
|
|
||||||
import feign.codec.DecodeException;
|
|
||||||
import feign.codec.Decoder;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
@@ -59,11 +58,12 @@ public class SpringDecoder implements Decoder {
|
|||||||
|
|
||||||
return extractor.extractData(new FeignResponseAdapter(response));
|
return extractor.extractData(new FeignResponseAdapter(response));
|
||||||
}
|
}
|
||||||
throw new DecodeException(
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FeignResponseAdapter implements ClientHttpResponse {
|
private final class FeignResponseAdapter implements ClientHttpResponse {
|
||||||
|
|
||||||
private final Response response;
|
private final Response response;
|
||||||
|
|
||||||
|
|||||||
+30
-23
@@ -1,18 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2017 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign.support;
|
package org.springframework.cloud.openfeign.support;
|
||||||
@@ -26,10 +25,17 @@ import java.nio.charset.StandardCharsets;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import feign.Request;
|
||||||
|
import feign.RequestTemplate;
|
||||||
|
import feign.codec.EncodeException;
|
||||||
|
import feign.codec.Encoder;
|
||||||
|
import feign.form.spring.SpringFormEncoder;
|
||||||
import org.apache.commons.logging.Log;
|
import org.apache.commons.logging.Log;
|
||||||
import org.apache.commons.logging.LogFactory;
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.beans.factory.ObjectFactory;
|
import org.springframework.beans.factory.ObjectFactory;
|
||||||
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
|
||||||
|
import org.springframework.cloud.openfeign.encoding.HttpEncoding;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.HttpOutputMessage;
|
import org.springframework.http.HttpOutputMessage;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@@ -38,17 +44,12 @@ import org.springframework.http.converter.HttpMessageConverter;
|
|||||||
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
|
import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
import feign.RequestTemplate;
|
|
||||||
import feign.codec.EncodeException;
|
|
||||||
import feign.codec.Encoder;
|
|
||||||
import feign.form.spring.SpringFormEncoder;
|
|
||||||
|
|
||||||
import static org.springframework.cloud.openfeign.support.FeignUtils.getHeaders;
|
import static org.springframework.cloud.openfeign.support.FeignUtils.getHeaders;
|
||||||
import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHeaders;
|
import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHeaders;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
* @author ScienJus
|
* @author Scien Jus
|
||||||
*/
|
*/
|
||||||
public class SpringEncoder implements Encoder {
|
public class SpringEncoder implements Encoder {
|
||||||
|
|
||||||
@@ -68,7 +69,8 @@ public class SpringEncoder implements Encoder {
|
|||||||
// template.body(conversionService.convert(object, String.class));
|
// template.body(conversionService.convert(object, String.class));
|
||||||
if (requestBody != null) {
|
if (requestBody != null) {
|
||||||
Class<?> requestType = requestBody.getClass();
|
Class<?> requestType = requestBody.getClass();
|
||||||
Collection<String> contentTypes = request.headers().get("Content-Type");
|
Collection<String> contentTypes = request.headers()
|
||||||
|
.get(HttpEncoding.CONTENT_TYPE);
|
||||||
|
|
||||||
MediaType requestContentType = null;
|
MediaType requestContentType = null;
|
||||||
if (contentTypes != null && !contentTypes.isEmpty()) {
|
if (contentTypes != null && !contentTypes.isEmpty()) {
|
||||||
@@ -78,12 +80,13 @@ public class SpringEncoder implements Encoder {
|
|||||||
|
|
||||||
if (bodyType != null && bodyType.equals(MultipartFile.class)) {
|
if (bodyType != null && bodyType.equals(MultipartFile.class)) {
|
||||||
if (Objects.equals(requestContentType, MediaType.MULTIPART_FORM_DATA)) {
|
if (Objects.equals(requestContentType, MediaType.MULTIPART_FORM_DATA)) {
|
||||||
springFormEncoder.encode(requestBody, bodyType, request);
|
this.springFormEncoder.encode(requestBody, bodyType, request);
|
||||||
return;
|
return;
|
||||||
} else {
|
}
|
||||||
String message = "Content-Type \"" + MediaType.MULTIPART_FORM_DATA +
|
else {
|
||||||
"\" not set for request body of type " +
|
String message = "Content-Type \"" + MediaType.MULTIPART_FORM_DATA
|
||||||
requestBody.getClass().getSimpleName();
|
+ "\" not set for request body of type "
|
||||||
|
+ requestBody.getClass().getSimpleName();
|
||||||
throw new EncodeException(message);
|
throw new EncodeException(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,8 +97,8 @@ public class SpringEncoder implements Encoder {
|
|||||||
if (log.isDebugEnabled()) {
|
if (log.isDebugEnabled()) {
|
||||||
if (requestContentType != null) {
|
if (requestContentType != null) {
|
||||||
log.debug("Writing [" + requestBody + "] as \""
|
log.debug("Writing [" + requestBody + "] as \""
|
||||||
+ requestContentType + "\" using ["
|
+ requestContentType + "\" using [" + messageConverter
|
||||||
+ messageConverter + "]");
|
+ "]");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
log.debug("Writing [" + requestBody + "] using ["
|
log.debug("Writing [" + requestBody + "] using ["
|
||||||
@@ -123,13 +126,17 @@ public class SpringEncoder implements Encoder {
|
|||||||
Charset charset;
|
Charset charset;
|
||||||
if (messageConverter instanceof ByteArrayHttpMessageConverter) {
|
if (messageConverter instanceof ByteArrayHttpMessageConverter) {
|
||||||
charset = null;
|
charset = null;
|
||||||
} else if (messageConverter instanceof ProtobufHttpMessageConverter &&
|
}
|
||||||
ProtobufHttpMessageConverter.PROTOBUF.isCompatibleWith(outputMessage.getHeaders().getContentType())) {
|
else if (messageConverter instanceof ProtobufHttpMessageConverter
|
||||||
|
&& ProtobufHttpMessageConverter.PROTOBUF.isCompatibleWith(
|
||||||
|
outputMessage.getHeaders().getContentType())) {
|
||||||
charset = null;
|
charset = null;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
charset = StandardCharsets.UTF_8;
|
charset = StandardCharsets.UTF_8;
|
||||||
}
|
}
|
||||||
request.body(outputMessage.getOutputStream().toByteArray(), charset);
|
request.body(Request.Body.encoded(
|
||||||
|
outputMessage.getOutputStream().toByteArray(), charset));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,14 +149,14 @@ public class SpringEncoder implements Encoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FeignOutputMessage implements HttpOutputMessage {
|
private final class FeignOutputMessage implements HttpOutputMessage {
|
||||||
|
|
||||||
private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||||
|
|
||||||
private final HttpHeaders httpHeaders;
|
private final HttpHeaders httpHeaders;
|
||||||
|
|
||||||
private FeignOutputMessage(RequestTemplate request) {
|
private FeignOutputMessage(RequestTemplate request) {
|
||||||
httpHeaders = getHttpHeaders(request.headers());
|
this.httpHeaders = getHttpHeaders(request.headers());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+124
-37
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2016 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -18,6 +18,7 @@ package org.springframework.cloud.openfeign.support;
|
|||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Parameter;
|
||||||
import java.lang.reflect.Type;
|
import java.lang.reflect.Type;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -28,16 +29,26 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import feign.Contract;
|
||||||
|
import feign.Feign;
|
||||||
|
import feign.MethodMetadata;
|
||||||
|
import feign.Param;
|
||||||
|
import feign.Request;
|
||||||
|
|
||||||
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
|
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
|
||||||
import org.springframework.cloud.openfeign.annotation.PathVariableParameterProcessor;
|
import org.springframework.cloud.openfeign.annotation.PathVariableParameterProcessor;
|
||||||
|
import org.springframework.cloud.openfeign.annotation.QueryMapParameterProcessor;
|
||||||
import org.springframework.cloud.openfeign.annotation.RequestHeaderParameterProcessor;
|
import org.springframework.cloud.openfeign.annotation.RequestHeaderParameterProcessor;
|
||||||
import org.springframework.cloud.openfeign.annotation.RequestParamParameterProcessor;
|
import org.springframework.cloud.openfeign.annotation.RequestParamParameterProcessor;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.context.ResourceLoaderAware;
|
import org.springframework.context.ResourceLoaderAware;
|
||||||
import org.springframework.core.DefaultParameterNameDiscoverer;
|
import org.springframework.core.DefaultParameterNameDiscoverer;
|
||||||
|
import org.springframework.core.MethodParameter;
|
||||||
import org.springframework.core.ParameterNameDiscoverer;
|
import org.springframework.core.ParameterNameDiscoverer;
|
||||||
|
import org.springframework.core.ResolvableType;
|
||||||
import org.springframework.core.annotation.AnnotationUtils;
|
import org.springframework.core.annotation.AnnotationUtils;
|
||||||
import org.springframework.core.convert.ConversionService;
|
import org.springframework.core.convert.ConversionService;
|
||||||
|
import org.springframework.core.convert.TypeDescriptor;
|
||||||
import org.springframework.core.convert.support.DefaultConversionService;
|
import org.springframework.core.convert.support.DefaultConversionService;
|
||||||
import org.springframework.core.io.DefaultResourceLoader;
|
import org.springframework.core.io.DefaultResourceLoader;
|
||||||
import org.springframework.core.io.ResourceLoader;
|
import org.springframework.core.io.ResourceLoader;
|
||||||
@@ -48,16 +59,15 @@ import org.springframework.web.bind.annotation.RequestMethod;
|
|||||||
|
|
||||||
import static feign.Util.checkState;
|
import static feign.Util.checkState;
|
||||||
import static feign.Util.emptyToNull;
|
import static feign.Util.emptyToNull;
|
||||||
|
import static org.springframework.cloud.openfeign.support.FeignUtils.addTemplateParameter;
|
||||||
import static org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation;
|
import static org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation;
|
||||||
|
|
||||||
import feign.Contract;
|
|
||||||
import feign.Feign;
|
|
||||||
import feign.MethodMetadata;
|
|
||||||
import feign.Param;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
* @author Abhijit Sarkar
|
* @author Abhijit Sarkar
|
||||||
|
* @author Halvdan Hoem Grelland
|
||||||
|
* @author Aram Peres
|
||||||
|
* @author Olga Maciaszek-Sharma
|
||||||
*/
|
*/
|
||||||
public class SpringMvcContract extends Contract.BaseContract
|
public class SpringMvcContract extends Contract.BaseContract
|
||||||
implements ResourceLoaderAware {
|
implements ResourceLoaderAware {
|
||||||
@@ -66,13 +76,22 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
|
|
||||||
private static final String CONTENT_TYPE = "Content-Type";
|
private static final String CONTENT_TYPE = "Content-Type";
|
||||||
|
|
||||||
|
private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor
|
||||||
|
.valueOf(String.class);
|
||||||
|
|
||||||
|
private static final TypeDescriptor ITERABLE_TYPE_DESCRIPTOR = TypeDescriptor
|
||||||
|
.valueOf(Iterable.class);
|
||||||
|
|
||||||
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
|
private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer();
|
||||||
|
|
||||||
private final Map<Class<? extends Annotation>, AnnotatedParameterProcessor> annotatedArgumentProcessors;
|
private final Map<Class<? extends Annotation>, AnnotatedParameterProcessor> annotatedArgumentProcessors;
|
||||||
|
|
||||||
private final Map<String, Method> processedMethods = new HashMap<>();
|
private final Map<String, Method> processedMethods = new HashMap<>();
|
||||||
|
|
||||||
private final ConversionService conversionService;
|
private final ConversionService conversionService;
|
||||||
private final Param.Expander expander;
|
|
||||||
|
private final ConvertingExpanderFactory convertingExpanderFactory;
|
||||||
|
|
||||||
private ResourceLoader resourceLoader = new DefaultResourceLoader();
|
private ResourceLoader resourceLoader = new DefaultResourceLoader();
|
||||||
|
|
||||||
public SpringMvcContract() {
|
public SpringMvcContract() {
|
||||||
@@ -100,7 +119,43 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
}
|
}
|
||||||
this.annotatedArgumentProcessors = toAnnotatedArgumentProcessorMap(processors);
|
this.annotatedArgumentProcessors = toAnnotatedArgumentProcessorMap(processors);
|
||||||
this.conversionService = conversionService;
|
this.conversionService = conversionService;
|
||||||
this.expander = new ConvertingExpander(conversionService);
|
this.convertingExpanderFactory = new ConvertingExpanderFactory(conversionService);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TypeDescriptor createTypeDescriptor(Method method, int paramIndex) {
|
||||||
|
Parameter parameter = method.getParameters()[paramIndex];
|
||||||
|
MethodParameter methodParameter = MethodParameter.forParameter(parameter);
|
||||||
|
TypeDescriptor typeDescriptor = new TypeDescriptor(methodParameter);
|
||||||
|
|
||||||
|
// Feign applies the Param.Expander to each element of an Iterable, so in those
|
||||||
|
// cases we need to provide a TypeDescriptor of the element.
|
||||||
|
if (typeDescriptor.isAssignableTo(ITERABLE_TYPE_DESCRIPTOR)) {
|
||||||
|
TypeDescriptor elementTypeDescriptor = getElementTypeDescriptor(
|
||||||
|
typeDescriptor);
|
||||||
|
|
||||||
|
checkState(elementTypeDescriptor != null,
|
||||||
|
"Could not resolve element type of Iterable type %s. Not declared?",
|
||||||
|
typeDescriptor);
|
||||||
|
|
||||||
|
typeDescriptor = elementTypeDescriptor;
|
||||||
|
}
|
||||||
|
return typeDescriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static TypeDescriptor getElementTypeDescriptor(
|
||||||
|
TypeDescriptor typeDescriptor) {
|
||||||
|
TypeDescriptor elementTypeDescriptor = typeDescriptor.getElementTypeDescriptor();
|
||||||
|
// that means it's not a collection but it is iterable, gh-135
|
||||||
|
if (elementTypeDescriptor == null
|
||||||
|
&& Iterable.class.isAssignableFrom(typeDescriptor.getType())) {
|
||||||
|
ResolvableType type = typeDescriptor.getResolvableType().as(Iterable.class)
|
||||||
|
.getGeneric(0);
|
||||||
|
if (type.resolve() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new TypeDescriptor(type, null, typeDescriptor.getAnnotations());
|
||||||
|
}
|
||||||
|
return elementTypeDescriptor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -121,7 +176,7 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
if (!pathValue.startsWith("/")) {
|
if (!pathValue.startsWith("/")) {
|
||||||
pathValue = "/" + pathValue;
|
pathValue = "/" + pathValue;
|
||||||
}
|
}
|
||||||
data.template().insert(0, pathValue);
|
data.template().uri(pathValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,7 +222,7 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
methods = new RequestMethod[] { RequestMethod.GET };
|
methods = new RequestMethod[] { RequestMethod.GET };
|
||||||
}
|
}
|
||||||
checkOne(method, methods, "method");
|
checkOne(method, methods, "method");
|
||||||
data.template().method(methods[0].name());
|
data.template().method(Request.HttpMethod.valueOf(methods[0].name()));
|
||||||
|
|
||||||
// path
|
// path
|
||||||
checkAtMostOne(method, methodMapping.value(), "value");
|
checkAtMostOne(method, methodMapping.value(), "value");
|
||||||
@@ -176,11 +231,10 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
if (pathValue != null) {
|
if (pathValue != null) {
|
||||||
pathValue = resolve(pathValue);
|
pathValue = resolve(pathValue);
|
||||||
// Append path from @RequestMapping if value is present on method
|
// Append path from @RequestMapping if value is present on method
|
||||||
if (!pathValue.startsWith("/")
|
if (!pathValue.startsWith("/") && !data.template().path().endsWith("/")) {
|
||||||
&& !data.template().toString().endsWith("/")) {
|
|
||||||
pathValue = "/" + pathValue;
|
pathValue = "/" + pathValue;
|
||||||
}
|
}
|
||||||
data.template().append(pathValue);
|
data.template().uri(pathValue, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,10 +293,17 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
processParameterAnnotation, method);
|
processParameterAnnotation, method);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isHttpAnnotation && data.indexToExpander().get(paramIndex) == null
|
|
||||||
&& this.conversionService.canConvert(
|
if (isHttpAnnotation && data.indexToExpander().get(paramIndex) == null) {
|
||||||
method.getParameterTypes()[paramIndex], String.class)) {
|
TypeDescriptor typeDescriptor = createTypeDescriptor(method, paramIndex);
|
||||||
data.indexToExpander().put(paramIndex, this.expander);
|
if (this.conversionService.canConvert(typeDescriptor,
|
||||||
|
STRING_TYPE_DESCRIPTOR)) {
|
||||||
|
Param.Expander expander = this.convertingExpanderFactory
|
||||||
|
.getExpander(typeDescriptor);
|
||||||
|
if (expander != null) {
|
||||||
|
data.indexToExpander().put(paramIndex, expander);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return isHttpAnnotation;
|
return isHttpAnnotation;
|
||||||
}
|
}
|
||||||
@@ -275,7 +336,7 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
int index = header.indexOf('=');
|
int index = header.indexOf('=');
|
||||||
if (!header.contains("!=") && index >= 0) {
|
if (!header.contains("!=") && index >= 0) {
|
||||||
md.template().header(resolve(header.substring(0, index)),
|
md.template().header(resolve(header.substring(0, index)),
|
||||||
resolve(header.substring(index + 1).trim()));
|
resolve(header.substring(index + 1).trim()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -297,6 +358,7 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
annotatedArgumentResolvers.add(new PathVariableParameterProcessor());
|
annotatedArgumentResolvers.add(new PathVariableParameterProcessor());
|
||||||
annotatedArgumentResolvers.add(new RequestParamParameterProcessor());
|
annotatedArgumentResolvers.add(new RequestParamParameterProcessor());
|
||||||
annotatedArgumentResolvers.add(new RequestHeaderParameterProcessor());
|
annotatedArgumentResolvers.add(new RequestHeaderParameterProcessor());
|
||||||
|
annotatedArgumentResolvers.add(new QueryMapParameterProcessor());
|
||||||
|
|
||||||
return annotatedArgumentResolvers;
|
return annotatedArgumentResolvers;
|
||||||
}
|
}
|
||||||
@@ -319,13 +381,51 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
parameterAnnotation.annotationType(), null);
|
parameterAnnotation.annotationType(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldAddParameterName(int parameterIndex, Type[] parameterTypes, String[] parameterNames) {
|
private boolean shouldAddParameterName(int parameterIndex, Type[] parameterTypes,
|
||||||
|
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
|
||||||
&& parameterTypes != null && parameterTypes.length > parameterIndex;
|
&& parameterTypes != null && parameterTypes.length > parameterIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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 this.conversionService.convert(value, String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ConvertingExpanderFactory {
|
||||||
|
|
||||||
|
private final ConversionService conversionService;
|
||||||
|
|
||||||
|
ConvertingExpanderFactory(ConversionService conversionService) {
|
||||||
|
this.conversionService = conversionService;
|
||||||
|
}
|
||||||
|
|
||||||
|
Param.Expander getExpander(TypeDescriptor typeDescriptor) {
|
||||||
|
return value -> {
|
||||||
|
Object converted = this.conversionService.convert(value, typeDescriptor,
|
||||||
|
STRING_TYPE_DESCRIPTOR);
|
||||||
|
return (String) converted;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private class SimpleAnnotatedParameterContext
|
private class SimpleAnnotatedParameterContext
|
||||||
implements AnnotatedParameterProcessor.AnnotatedParameterContext {
|
implements AnnotatedParameterProcessor.AnnotatedParameterContext {
|
||||||
|
|
||||||
@@ -333,7 +433,7 @@ public class SpringMvcContract extends Contract.BaseContract
|
|||||||
|
|
||||||
private final int parameterIndex;
|
private final int parameterIndex;
|
||||||
|
|
||||||
public SimpleAnnotatedParameterContext(MethodMetadata methodMetadata,
|
SimpleAnnotatedParameterContext(MethodMetadata methodMetadata,
|
||||||
int parameterIndex) {
|
int parameterIndex) {
|
||||||
this.methodMetadata = methodMetadata;
|
this.methodMetadata = methodMetadata;
|
||||||
this.parameterIndex = parameterIndex;
|
this.parameterIndex = parameterIndex;
|
||||||
@@ -357,22 +457,9 @@ 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 addTemplatedParam(rest, name);
|
return addTemplateParameter(rest, name);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 this.conversionService.convert(value, String.class);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+42
-33
@@ -1,33 +1,42 @@
|
|||||||
{"groups": [
|
{
|
||||||
], "properties": [
|
"groups": [
|
||||||
{
|
],
|
||||||
"name": "feign.hystrix.enabled",
|
"properties": [
|
||||||
"type": "java.lang.Boolean",
|
{
|
||||||
"description": "If true, an OpenFeign client will be wrapped with a Hystrix circuit breaker.",
|
"name": "feign.hystrix.enabled",
|
||||||
"defaultValue": "false"
|
"type": "java.lang.Boolean",
|
||||||
},
|
"description": "If true, an OpenFeign client will be wrapped with a Hystrix circuit breaker.",
|
||||||
{
|
"defaultValue": "false"
|
||||||
"name": "feign.httpclient.enabled",
|
},
|
||||||
"type": "java.lang.Boolean",
|
{
|
||||||
"description": "Enables the use of the Apache HTTP Client by Feign.",
|
"name": "feign.httpclient.enabled",
|
||||||
"defaultValue": "true"
|
"type": "java.lang.Boolean",
|
||||||
},
|
"description": "Enables the use of the Apache HTTP Client by Feign.",
|
||||||
{
|
"defaultValue": "true"
|
||||||
"name": "feign.okhttp.enabled",
|
},
|
||||||
"type": "java.lang.Boolean",
|
{
|
||||||
"description": "Enables the use of the OK HTTP Client by Feign.",
|
"name": "feign.okhttp.enabled",
|
||||||
"defaultValue": "false"
|
"type": "java.lang.Boolean",
|
||||||
},
|
"description": "Enables the use of the OK HTTP Client by Feign.",
|
||||||
{
|
"defaultValue": "false"
|
||||||
"name": "feign.compression.response.enabled",
|
},
|
||||||
"type": "java.lang.Boolean",
|
{
|
||||||
"description": "Enables the response from Feign to be compressed.",
|
"name": "feign.compression.response.enabled",
|
||||||
"defaultValue": "false"
|
"type": "java.lang.Boolean",
|
||||||
},
|
"description": "Enables the response from Feign to be compressed.",
|
||||||
{
|
"defaultValue": "false"
|
||||||
"name": "feign.compression.request.enabled",
|
},
|
||||||
"type": "java.lang.Boolean",
|
{
|
||||||
"description": "Enables the request sent by Feign to be compressed.",
|
"name": "feign.compression.response.useGzipDecoder",
|
||||||
"defaultValue": "false"
|
"type": "java.lang.Boolean",
|
||||||
}
|
"description": "Enables the default gzip decoder to be used.",
|
||||||
]}
|
"defaultValue": "false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "feign.compression.request.enabled",
|
||||||
|
"type": "java.lang.Boolean",
|
||||||
|
"description": "Enables the request sent by Feign to be compressed.",
|
||||||
|
"defaultValue": "false"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||||
org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration,\
|
org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration,\
|
||||||
|
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,\
|
||||||
org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration
|
org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration,\
|
||||||
|
org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration
|
||||||
|
|||||||
+159
@@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2019 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.Objects;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Jaesik Kim
|
||||||
|
*/
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
@SpringBootTest(classes = DefaultGzipDecoderTests.Application.class,
|
||||||
|
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
|
||||||
|
value = { "spring.application.name=defaultGzipDecoderTests",
|
||||||
|
"feign.compression.response.enabled=true",
|
||||||
|
"feign.compression.response.useGzipDecoder=true",
|
||||||
|
"feign.client.config.default.loggerLevel=full",
|
||||||
|
"logging.level.org.springframework.cloud.openfeign=DEBUG" })
|
||||||
|
@DirtiesContext
|
||||||
|
public class DefaultGzipDecoderTests extends FeignClientFactoryBean {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
FeignContext context;
|
||||||
|
|
||||||
|
@Value("${local.server.port}")
|
||||||
|
private int port = 0;
|
||||||
|
|
||||||
|
public DefaultGzipDecoderTests() {
|
||||||
|
setName("tests");
|
||||||
|
setContextId("test");
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestClient testClient() {
|
||||||
|
setType(this.getClass());
|
||||||
|
return feign(context).target(TestClient.class, "http://localhost:" + this.port);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testBodyDecompress() {
|
||||||
|
ResponseEntity<Hello> response = testClient().getGzipResponse();
|
||||||
|
assertThat(response).as("response was null").isNotNull();
|
||||||
|
assertThat(response.getStatusCode()).as("wrong status code")
|
||||||
|
.isEqualTo(HttpStatus.OK);
|
||||||
|
Hello hello = response.getBody();
|
||||||
|
assertThat(hello).as("hello was null").isNotNull();
|
||||||
|
assertThat(hello).as("first hello didn't match")
|
||||||
|
.isEqualTo(new Hello("hello world via response"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNullBodyDecompress() {
|
||||||
|
ResponseEntity<Hello> response = testClient().getNullResponse();
|
||||||
|
assertThat(response).as("response was null").isNotNull();
|
||||||
|
assertThat(response.getStatusCode()).as("wrong status code")
|
||||||
|
.isEqualTo(HttpStatus.OK);
|
||||||
|
Hello hello = response.getBody();
|
||||||
|
assertThat(hello).as("hello was not null").isNull();
|
||||||
|
assertThat(hello).as("null hello didn't match").isEqualTo(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Hello {
|
||||||
|
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
Hello() {
|
||||||
|
}
|
||||||
|
|
||||||
|
Hello(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Hello that = (Hello) o;
|
||||||
|
return Objects.equals(message, that.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected interface TestClient {
|
||||||
|
|
||||||
|
@GetMapping("/helloGzipResponse")
|
||||||
|
ResponseEntity<Hello> getGzipResponse();
|
||||||
|
|
||||||
|
@GetMapping("/nullGzipResponse")
|
||||||
|
ResponseEntity<Hello> getNullResponse();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@EnableAutoConfiguration
|
||||||
|
@RestController
|
||||||
|
@Import(NoSecurityConfiguration.class)
|
||||||
|
protected static class Application implements TestClient {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseEntity<Hello> getGzipResponse() {
|
||||||
|
return ResponseEntity.ok(new Hello("hello world via response"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResponseEntity<Hello> getNullResponse() {
|
||||||
|
return ResponseEntity.ok(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2013-2015 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.codec.Encoder;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||||
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
|
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
||||||
|
import org.springframework.cloud.openfeign.support.PageableSpringEncoder;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Spencer Gibb
|
||||||
|
*/
|
||||||
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
|
@SpringBootTest(classes = EnableFeignClientsSpringDataTests.PlainConfiguration.class)
|
||||||
|
@DirtiesContext
|
||||||
|
public class EnableFeignClientsSpringDataTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FeignContext feignContext;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void encoderDefaultCorrect() {
|
||||||
|
|
||||||
|
PageableSpringEncoder.class
|
||||||
|
.cast(this.feignContext.getInstance("foo", Encoder.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
@Import({ PropertyPlaceholderAutoConfiguration.class, ArchaiusAutoConfiguration.class,
|
||||||
|
FeignAutoConfiguration.class })
|
||||||
|
protected static class PlainConfiguration {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+41
-31
@@ -1,35 +1,21 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2017 the original author or authors.
|
* Copyright 2013-2015 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
package org.springframework.cloud.openfeign;
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
|
||||||
import org.springframework.cloud.openfeign.support.SpringEncoder;
|
|
||||||
import org.springframework.cloud.openfeign.support.SpringMvcContract;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.context.annotation.Import;
|
|
||||||
import org.springframework.test.annotation.DirtiesContext;
|
|
||||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
|
||||||
|
|
||||||
import feign.Contract;
|
import feign.Contract;
|
||||||
import feign.Feign;
|
import feign.Feign;
|
||||||
import feign.Logger;
|
import feign.Logger;
|
||||||
@@ -37,50 +23,74 @@ import feign.codec.Decoder;
|
|||||||
import feign.codec.Encoder;
|
import feign.codec.Encoder;
|
||||||
import feign.optionals.OptionalDecoder;
|
import feign.optionals.OptionalDecoder;
|
||||||
import feign.slf4j.Slf4jLogger;
|
import feign.slf4j.Slf4jLogger;
|
||||||
|
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.netflix.archaius.ArchaiusAutoConfiguration;
|
||||||
|
import org.springframework.cloud.openfeign.support.SpringEncoder;
|
||||||
|
import org.springframework.cloud.openfeign.support.SpringMvcContract;
|
||||||
|
import org.springframework.cloud.test.ClassPathExclusions;
|
||||||
|
import org.springframework.cloud.test.ModifiedClassPathRunner;
|
||||||
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
*/
|
*/
|
||||||
@RunWith(SpringJUnit4ClassRunner.class)
|
@RunWith(ModifiedClassPathRunner.class)
|
||||||
@SpringBootTest(classes = EnableFeignClientsTests.PlainConfiguration.class)
|
@ClassPathExclusions({ "spring-data-commons-*.jar" })
|
||||||
@DirtiesContext
|
|
||||||
public class EnableFeignClientsTests {
|
public class EnableFeignClientsTests {
|
||||||
|
|
||||||
@Autowired
|
private ConfigurableApplicationContext context;
|
||||||
private FeignContext feignContext;
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
|
||||||
|
.properties("debug=true", "feign.httpclient.enabled=false")
|
||||||
|
.sources(EnableFeignClientsTests.PlainConfiguration.class).run();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
if (context != null) {
|
||||||
|
context.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void decoderDefaultCorrect() {
|
public void decoderDefaultCorrect() {
|
||||||
OptionalDecoder.class
|
OptionalDecoder.class.cast(this.context.getBeansOfType(Decoder.class).get(0));
|
||||||
.cast(this.feignContext.getInstance("foo", Decoder.class));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void encoderDefaultCorrect() {
|
public void encoderDefaultCorrect() {
|
||||||
SpringEncoder.class.cast(this.feignContext.getInstance("foo", Encoder.class));
|
SpringEncoder.class.cast(this.context.getBeansOfType(Encoder.class).get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void loggerDefaultCorrect() {
|
public void loggerDefaultCorrect() {
|
||||||
Slf4jLogger.class.cast(this.feignContext.getInstance("foo", Logger.class));
|
Slf4jLogger.class.cast(this.context.getBeansOfType(Logger.class).get(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void contractDefaultCorrect() {
|
public void contractDefaultCorrect() {
|
||||||
SpringMvcContract.class
|
SpringMvcContract.class.cast(this.context.getBeansOfType(Contract.class).get(0));
|
||||||
.cast(this.feignContext.getInstance("foo", Contract.class));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void builderDefaultCorrect() {
|
public void builderDefaultCorrect() {
|
||||||
Feign.Builder.class
|
Feign.Builder.class.cast(this.context.getBeansOfType(Feign.Builder.class).get(0));
|
||||||
.cast(this.feignContext.getInstance("foo", Feign.Builder.class));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@Import({ PropertyPlaceholderAutoConfiguration.class, ArchaiusAutoConfiguration.class,
|
@Import({ ArchaiusAutoConfiguration.class, FeignAutoConfiguration.class })
|
||||||
FeignAutoConfiguration.class })
|
|
||||||
protected static class PlainConfiguration {
|
protected static class PlainConfiguration {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+49
-23
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2018 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -24,17 +24,20 @@ 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.Assert;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.ExpectedException;
|
import org.junit.rules.ExpectedException;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
import org.springframework.cloud.openfeign.testclients.TestClient;
|
import org.springframework.cloud.openfeign.testclients.TestClient;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Sven Döring
|
* @author Sven Döring
|
||||||
*/
|
*/
|
||||||
@@ -65,14 +68,14 @@ public class FeignClientBuilderTests {
|
|||||||
fieldName);
|
fieldName);
|
||||||
ReflectionUtils.makeAccessible(field);
|
ReflectionUtils.makeAccessible(field);
|
||||||
final Object value = ReflectionUtils.getField(field, factoryBean);
|
final Object value = ReflectionUtils.getField(field, factoryBean);
|
||||||
Assert.assertEquals("Expected value for the field '" + fieldName + "':",
|
assertThat(value).as("Expected value for the field '" + fieldName + "':")
|
||||||
expectedValue, value);
|
.isEqualTo(expectedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
this.applicationContext = Mockito.mock(ApplicationContext.class);
|
this.applicationContext = Mockito.mock(ApplicationContext.class);
|
||||||
this.feignClientBuilder = new FeignClientBuilder(applicationContext);
|
this.feignClientBuilder = new FeignClientBuilder(this.applicationContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -91,20 +94,21 @@ 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.
|
||||||
Assert.assertThat(methodNames, Matchers.contains("decode404", "fallback",
|
assertThat(methodNames).containsExactly("contextId", "decode404", "fallback",
|
||||||
"fallbackFactory", "name", "path", "url"));
|
"fallbackFactory", "name", "path", "url");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void forType_preinitializedBuilder() {
|
public void forType_preinitializedBuilder() {
|
||||||
// when:
|
// when:
|
||||||
final FeignClientBuilder.Builder builder = feignClientBuilder
|
final FeignClientBuilder.Builder builder = this.feignClientBuilder
|
||||||
.forType(FeignClientBuilderTests.class, "TestClient");
|
.forType(TestFeignClient.class, "TestClient");
|
||||||
|
|
||||||
// then:
|
// then:
|
||||||
assertFactoryBeanField(builder, "applicationContext", applicationContext);
|
assertFactoryBeanField(builder, "applicationContext", this.applicationContext);
|
||||||
assertFactoryBeanField(builder, "type", FeignClientBuilderTests.class);
|
assertFactoryBeanField(builder, "type", TestFeignClient.class);
|
||||||
assertFactoryBeanField(builder, "name", "TestClient");
|
assertFactoryBeanField(builder, "name", "TestClient");
|
||||||
|
assertFactoryBeanField(builder, "contextId", "TestClient");
|
||||||
|
|
||||||
// and:
|
// and:
|
||||||
assertFactoryBeanField(builder, "url",
|
assertFactoryBeanField(builder, "url",
|
||||||
@@ -122,37 +126,59 @@ public class FeignClientBuilderTests {
|
|||||||
@Test
|
@Test
|
||||||
public void forType_allFieldsSetOnBuilder() {
|
public void forType_allFieldsSetOnBuilder() {
|
||||||
// when:
|
// when:
|
||||||
final FeignClientBuilder.Builder builder = feignClientBuilder
|
final FeignClientBuilder.Builder builder = this.feignClientBuilder
|
||||||
.forType(FeignClientBuilderTests.class, "TestClient").decode404(true)
|
.forType(TestFeignClient.class, "TestClient").decode404(true)
|
||||||
.fallback(Object.class).fallbackFactory(Object.class).path("Path/")
|
.fallback(TestFeignClientFallback.class)
|
||||||
.url("Url/");
|
.fallbackFactory(TestFeignClientFallbackFactory.class).path("Path/")
|
||||||
|
.url("Url/").contextId("TestContext");
|
||||||
|
|
||||||
// then:
|
// then:
|
||||||
assertFactoryBeanField(builder, "applicationContext", applicationContext);
|
assertFactoryBeanField(builder, "applicationContext", this.applicationContext);
|
||||||
assertFactoryBeanField(builder, "type", FeignClientBuilderTests.class);
|
assertFactoryBeanField(builder, "type", TestFeignClient.class);
|
||||||
assertFactoryBeanField(builder, "name", "TestClient");
|
assertFactoryBeanField(builder, "name", "TestClient");
|
||||||
|
assertFactoryBeanField(builder, "contextId", "TestContext");
|
||||||
|
|
||||||
// and:
|
// and:
|
||||||
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, "fallback", Object.class);
|
assertFactoryBeanField(builder, "fallback", TestFeignClientFallback.class);
|
||||||
assertFactoryBeanField(builder, "fallbackFactory", Object.class);
|
assertFactoryBeanField(builder, "fallbackFactory",
|
||||||
|
TestFeignClientFallbackFactory.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void forType_build() {
|
public void forType_build() {
|
||||||
// given:
|
// given:
|
||||||
Mockito.when(applicationContext.getBean(FeignContext.class))
|
Mockito.when(this.applicationContext.getBean(FeignContext.class))
|
||||||
.thenThrow(new ClosedFileSystemException()); // throw an unusual exception
|
.thenThrow(new ClosedFileSystemException()); // throw an unusual exception
|
||||||
// in the
|
// in the
|
||||||
// FeignClientFactoryBean
|
// FeignClientFactoryBean
|
||||||
final FeignClientBuilder.Builder builder = feignClientBuilder
|
final FeignClientBuilder.Builder builder = this.feignClientBuilder
|
||||||
.forType(TestClient.class, "TestClient");
|
.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'
|
||||||
thrown.expect(Matchers.isA(ClosedFileSystemException.class));
|
this.thrown.expect(Matchers.isA(ClosedFileSystemException.class));
|
||||||
builder.build();
|
builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private interface TestFeignClient {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestFeignClientFallback implements TestFeignClient {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestFeignClientFallbackFactory
|
||||||
|
implements FallbackFactory<TestFeignClient> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TestFeignClientFallback create(Throwable throwable) {
|
||||||
|
return new TestFeignClientFallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-14
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2015 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -16,16 +16,14 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
package org.springframework.cloud.openfeign;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.is;
|
|
||||||
import static org.hamcrest.Matchers.notNullValue;
|
|
||||||
import static org.hamcrest.Matchers.nullValue;
|
|
||||||
import static org.junit.Assert.assertThat;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
@@ -42,33 +40,43 @@ public class FeignClientFactoryTests {
|
|||||||
getSpec("bar", BarConfig.class)));
|
getSpec("bar", BarConfig.class)));
|
||||||
|
|
||||||
Foo foo = context.getInstance("foo", Foo.class);
|
Foo foo = context.getInstance("foo", Foo.class);
|
||||||
assertThat("foo was null", foo, is(notNullValue()));
|
assertThat(foo).as("foo was null").isNotNull();
|
||||||
|
|
||||||
Bar bar = context.getInstance("bar", Bar.class);
|
Bar bar = context.getInstance("bar", Bar.class);
|
||||||
assertThat("bar was null", bar, is(notNullValue()));
|
assertThat(bar).as("bar was null").isNotNull();
|
||||||
|
|
||||||
Bar foobar = context.getInstance("foo", Bar.class);
|
Bar foobar = context.getInstance("foo", Bar.class);
|
||||||
assertThat("bar was not null", foobar, is(nullValue()));
|
assertThat(foobar).as("bar was not null").isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
private FeignClientSpecification getSpec(String name, Class<?> configClass) {
|
private FeignClientSpecification getSpec(String name, Class<?> configClass) {
|
||||||
return new FeignClientSpecification(name, new Class[]{configClass});
|
return new FeignClientSpecification(name, new Class[] { configClass });
|
||||||
}
|
}
|
||||||
|
|
||||||
static class FooConfig {
|
static class FooConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
Foo foo() {
|
Foo foo() {
|
||||||
return new Foo();
|
return new Foo();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
static class Foo{}
|
|
||||||
|
static class Foo {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static class BarConfig {
|
static class BarConfig {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
Bar bar() {
|
Bar bar() {
|
||||||
return new Bar();
|
return new Bar();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
static class Bar{}
|
|
||||||
|
static class Bar {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+82
-52
@@ -1,45 +1,25 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2017 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
package org.springframework.cloud.openfeign;
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration;
|
|
||||||
import org.springframework.cloud.openfeign.support.SpringEncoder;
|
|
||||||
import org.springframework.cloud.openfeign.support.SpringMvcContract;
|
|
||||||
import org.springframework.context.annotation.Bean;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.context.annotation.Import;
|
|
||||||
import org.springframework.test.annotation.DirtiesContext;
|
|
||||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
|
|
||||||
import feign.Contract;
|
import feign.Contract;
|
||||||
import feign.Feign;
|
import feign.Feign;
|
||||||
import feign.Logger;
|
import feign.Logger;
|
||||||
|
import feign.QueryMapEncoder;
|
||||||
import feign.Request;
|
import feign.Request;
|
||||||
import feign.RequestInterceptor;
|
import feign.RequestInterceptor;
|
||||||
import feign.RequestLine;
|
import feign.RequestLine;
|
||||||
@@ -51,7 +31,26 @@ import feign.codec.Encoder;
|
|||||||
import feign.codec.ErrorDecoder;
|
import feign.codec.ErrorDecoder;
|
||||||
import feign.hystrix.HystrixFeign;
|
import feign.hystrix.HystrixFeign;
|
||||||
import feign.optionals.OptionalDecoder;
|
import feign.optionals.OptionalDecoder;
|
||||||
|
import feign.querymap.BeanQueryMapEncoder;
|
||||||
import feign.slf4j.Slf4jLogger;
|
import feign.slf4j.Slf4jLogger;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
|
||||||
|
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.SpringMvcContract;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
|
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
@@ -72,8 +71,8 @@ public class FeignClientOverrideDefaultsTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void clientsAvailable() {
|
public void clientsAvailable() {
|
||||||
assertNotNull(this.foo);
|
assertThat(this.foo).isNotNull();
|
||||||
assertNotNull(this.bar);
|
assertThat(this.bar).isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -85,7 +84,7 @@ public class FeignClientOverrideDefaultsTests {
|
|||||||
@Test
|
@Test
|
||||||
public void overrideEncoder() {
|
public void overrideEncoder() {
|
||||||
Encoder.Default.class.cast(this.context.getInstance("foo", Encoder.class));
|
Encoder.Default.class.cast(this.context.getInstance("foo", Encoder.class));
|
||||||
SpringEncoder.class.cast(this.context.getInstance("bar", Encoder.class));
|
PageableSpringEncoder.class.cast(this.context.getInstance("bar", Encoder.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -102,45 +101,72 @@ public class FeignClientOverrideDefaultsTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void overrideLoggerLevel() {
|
public void overrideLoggerLevel() {
|
||||||
assertNull(this.context.getInstance("foo", Logger.Level.class));
|
assertThat(this.context.getInstance("foo", Logger.Level.class)).isNull();
|
||||||
assertEquals(Logger.Level.HEADERS,
|
assertThat(this.context.getInstance("bar", Logger.Level.class))
|
||||||
this.context.getInstance("bar", Logger.Level.class));
|
.isEqualTo(Logger.Level.HEADERS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void overrideRetryer() {
|
public void overrideRetryer() {
|
||||||
assertEquals(Retryer.NEVER_RETRY, this.context.getInstance("foo", Retryer.class));
|
assertThat(this.context.getInstance("foo", Retryer.class))
|
||||||
|
.isEqualTo(Retryer.NEVER_RETRY);
|
||||||
Retryer.Default.class.cast(this.context.getInstance("bar", Retryer.class));
|
Retryer.Default.class.cast(this.context.getInstance("bar", Retryer.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void overrideErrorDecoder() {
|
public void overrideErrorDecoder() {
|
||||||
assertNull(this.context.getInstance("foo", ErrorDecoder.class));
|
assertThat(this.context.getInstance("foo", ErrorDecoder.class)).isNull();
|
||||||
ErrorDecoder.Default.class
|
ErrorDecoder.Default.class
|
||||||
.cast(this.context.getInstance("bar", ErrorDecoder.class));
|
.cast(this.context.getInstance("bar", ErrorDecoder.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void overrideBuilder() {
|
public void overrideBuilder() {
|
||||||
HystrixFeign.Builder.class.cast(this.context.getInstance("foo", Feign.Builder.class));
|
HystrixFeign.Builder.class
|
||||||
Feign.Builder.class
|
.cast(this.context.getInstance("foo", Feign.Builder.class));
|
||||||
.cast(this.context.getInstance("bar", Feign.Builder.class));
|
Feign.Builder.class.cast(this.context.getInstance("bar", Feign.Builder.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void overrideRequestOptions() {
|
public void overrideRequestOptions() {
|
||||||
assertNull(this.context.getInstance("foo", Request.Options.class));
|
assertThat(this.context.getInstance("foo", Request.Options.class)).isNull();
|
||||||
Request.Options options = this.context.getInstance("bar", Request.Options.class);
|
Request.Options options = this.context.getInstance("bar", Request.Options.class);
|
||||||
assertEquals(1, options.connectTimeoutMillis());
|
assertThat(options.connectTimeoutMillis()).isEqualTo(1);
|
||||||
assertEquals(1, options.readTimeoutMillis());
|
assertThat(options.readTimeoutMillis()).isEqualTo(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void overrideQueryMapEncoder() {
|
||||||
|
QueryMapEncoder.Default.class
|
||||||
|
.cast(this.context.getInstance("foo", QueryMapEncoder.class));
|
||||||
|
BeanQueryMapEncoder.class
|
||||||
|
.cast(this.context.getInstance("bar", QueryMapEncoder.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void addRequestInterceptor() {
|
public void addRequestInterceptor() {
|
||||||
assertEquals(1,
|
assertThat(this.context.getInstances("foo", RequestInterceptor.class).size())
|
||||||
this.context.getInstances("foo", RequestInterceptor.class).size());
|
.isEqualTo(1);
|
||||||
assertEquals(2,
|
assertThat(this.context.getInstances("bar", RequestInterceptor.class).size())
|
||||||
this.context.getInstances("bar", RequestInterceptor.class).size());
|
.isEqualTo(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FeignClient(name = "foo", url = "https://foo",
|
||||||
|
configuration = FooConfiguration.class)
|
||||||
|
interface FooClient {
|
||||||
|
|
||||||
|
@RequestLine("GET /")
|
||||||
|
String get();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@FeignClient(name = "bar", url = "https://bar",
|
||||||
|
configuration = BarConfiguration.class)
|
||||||
|
interface BarClient {
|
||||||
|
|
||||||
|
@RequestMapping(value = "/", method = RequestMethod.GET)
|
||||||
|
String get();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@@ -148,6 +174,7 @@ public class FeignClientOverrideDefaultsTests {
|
|||||||
@Import({ PropertyPlaceholderAutoConfiguration.class, ArchaiusAutoConfiguration.class,
|
@Import({ PropertyPlaceholderAutoConfiguration.class, ArchaiusAutoConfiguration.class,
|
||||||
FeignAutoConfiguration.class })
|
FeignAutoConfiguration.class })
|
||||||
protected static class TestConfiguration {
|
protected static class TestConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
RequestInterceptor defaultRequestInterceptor() {
|
RequestInterceptor defaultRequestInterceptor() {
|
||||||
return new RequestInterceptor() {
|
return new RequestInterceptor() {
|
||||||
@@ -156,16 +183,11 @@ public class FeignClientOverrideDefaultsTests {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@FeignClient(name = "foo", url = "http://foo", configuration = FooConfiguration.class)
|
|
||||||
interface FooClient {
|
|
||||||
@RequestLine("GET /")
|
|
||||||
String get();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class FooConfiguration {
|
public static class FooConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public Decoder feignDecoder() {
|
public Decoder feignDecoder() {
|
||||||
return new Decoder.Default();
|
return new Decoder.Default();
|
||||||
@@ -190,15 +212,16 @@ public class FeignClientOverrideDefaultsTests {
|
|||||||
public Feign.Builder feignBuilder() {
|
public Feign.Builder feignBuilder() {
|
||||||
return HystrixFeign.builder();
|
return HystrixFeign.builder();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@FeignClient(name = "bar", url = "http://bar", configuration = BarConfiguration.class)
|
@Bean
|
||||||
interface BarClient {
|
public QueryMapEncoder queryMapEncoder() {
|
||||||
@RequestMapping(value = "/", method = RequestMethod.GET)
|
return new feign.QueryMapEncoder.Default();
|
||||||
String get();
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class BarConfiguration {
|
public static class BarConfiguration {
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
Logger.Level feignLevel() {
|
Logger.Level feignLevel() {
|
||||||
return Logger.Level.HEADERS;
|
return Logger.Level.HEADERS;
|
||||||
@@ -223,5 +246,12 @@ public class FeignClientOverrideDefaultsTests {
|
|||||||
RequestInterceptor feignRequestInterceptor() {
|
RequestInterceptor feignRequestInterceptor() {
|
||||||
return new BasicAuthRequestInterceptor("user", "pass");
|
return new BasicAuthRequestInterceptor("user", "pass");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public QueryMapEncoder queryMapEncoder() {
|
||||||
|
return new BeanQueryMapEncoder();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-34
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2018 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -16,6 +16,13 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
package org.springframework.cloud.openfeign;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import feign.Request;
|
||||||
import feign.RequestInterceptor;
|
import feign.RequestInterceptor;
|
||||||
import feign.RequestTemplate;
|
import feign.RequestTemplate;
|
||||||
import feign.RetryableException;
|
import feign.RetryableException;
|
||||||
@@ -25,13 +32,15 @@ import feign.codec.Encoder;
|
|||||||
import feign.codec.ErrorDecoder;
|
import feign.codec.ErrorDecoder;
|
||||||
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.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.cloud.openfeign.test.NoSecurityConfiguration;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.http.HttpHeaders;
|
import org.springframework.http.HttpHeaders;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.test.annotation.DirtiesContext;
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
@@ -41,19 +50,17 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import java.lang.reflect.Type;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import java.util.Collections;
|
import static org.assertj.core.api.Assertions.fail;
|
||||||
import java.util.Map;
|
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Eko Kurniawan Khannedy
|
* @author Eko Kurniawan Khannedy
|
||||||
*/
|
*/
|
||||||
@RunWith(SpringJUnit4ClassRunner.class)
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
@SpringBootTest(classes = FeignClientUsingPropertiesTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT)
|
@SpringBootTest(classes = FeignClientUsingPropertiesTests.Application.class,
|
||||||
|
webEnvironment = RANDOM_PORT)
|
||||||
@TestPropertySource("classpath:feign-properties.properties")
|
@TestPropertySource("classpath:feign-properties.properties")
|
||||||
@DirtiesContext
|
@DirtiesContext
|
||||||
public class FeignClientUsingPropertiesTests {
|
public class FeignClientUsingPropertiesTests {
|
||||||
@@ -74,38 +81,41 @@ public class FeignClientUsingPropertiesTests {
|
|||||||
private FeignClientFactoryBean formFactoryBean;
|
private FeignClientFactoryBean formFactoryBean;
|
||||||
|
|
||||||
public FeignClientUsingPropertiesTests() {
|
public FeignClientUsingPropertiesTests() {
|
||||||
fooFactoryBean = new FeignClientFactoryBean();
|
this.fooFactoryBean = new FeignClientFactoryBean();
|
||||||
fooFactoryBean.setName("foo");
|
this.fooFactoryBean.setContextId("foo");
|
||||||
fooFactoryBean.setType(FeignClientFactoryBean.class);
|
this.fooFactoryBean.setType(FeignClientFactoryBean.class);
|
||||||
|
|
||||||
barFactoryBean = new FeignClientFactoryBean();
|
this.barFactoryBean = new FeignClientFactoryBean();
|
||||||
barFactoryBean.setName("bar");
|
this.barFactoryBean.setContextId("bar");
|
||||||
barFactoryBean.setType(FeignClientFactoryBean.class);
|
this.barFactoryBean.setType(FeignClientFactoryBean.class);
|
||||||
|
|
||||||
formFactoryBean = new FeignClientFactoryBean();
|
this.formFactoryBean = new FeignClientFactoryBean();
|
||||||
formFactoryBean.setName("form");
|
this.formFactoryBean.setContextId("form");
|
||||||
formFactoryBean.setType(FeignClientFactoryBean.class);
|
this.formFactoryBean.setType(FeignClientFactoryBean.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FooClient fooClient() {
|
public FooClient fooClient() {
|
||||||
fooFactoryBean.setApplicationContext(applicationContext);
|
this.fooFactoryBean.setApplicationContext(this.applicationContext);
|
||||||
return fooFactoryBean.feign(context).target(FooClient.class, "http://localhost:" + this.port);
|
return this.fooFactoryBean.feign(this.context).target(FooClient.class,
|
||||||
|
"http://localhost:" + this.port);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BarClient barClient() {
|
public BarClient barClient() {
|
||||||
barFactoryBean.setApplicationContext(applicationContext);
|
this.barFactoryBean.setApplicationContext(this.applicationContext);
|
||||||
return barFactoryBean.feign(context).target(BarClient.class, "http://localhost:" + this.port);
|
return this.barFactoryBean.feign(this.context).target(BarClient.class,
|
||||||
|
"http://localhost:" + this.port);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FormClient formClient() {
|
public FormClient formClient() {
|
||||||
formFactoryBean.setApplicationContext(applicationContext);
|
this.formFactoryBean.setApplicationContext(this.applicationContext);
|
||||||
return formFactoryBean.feign(context).target(FormClient.class, "http://localhost:" + this.port);
|
return this.formFactoryBean.feign(this.context).target(FormClient.class,
|
||||||
|
"http://localhost:" + this.port);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testFoo() {
|
public void testFoo() {
|
||||||
String response = fooClient().foo();
|
String response = fooClient().foo();
|
||||||
assertEquals("OK", response);
|
assertThat(response).isEqualTo("OK");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = RetryableException.class)
|
@Test(expected = RetryableException.class)
|
||||||
@@ -118,19 +128,21 @@ public class FeignClientUsingPropertiesTests {
|
|||||||
public void testForm() {
|
public void testForm() {
|
||||||
Map<String, String> request = Collections.singletonMap("form", "Data");
|
Map<String, String> request = Collections.singletonMap("form", "Data");
|
||||||
String response = formClient().form(request);
|
String response = formClient().form(request);
|
||||||
assertEquals("Data", response);
|
assertThat(response).isEqualTo("Data");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected interface FooClient {
|
protected interface FooClient {
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.GET, value = "/foo")
|
@RequestMapping(method = RequestMethod.GET, value = "/foo")
|
||||||
String foo();
|
String foo();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected interface BarClient {
|
protected interface BarClient {
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.GET, value = "/bar")
|
@RequestMapping(method = RequestMethod.GET, value = "/bar")
|
||||||
String bar();
|
String bar();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected interface FormClient {
|
protected interface FormClient {
|
||||||
@@ -144,14 +156,16 @@ public class FeignClientUsingPropertiesTests {
|
|||||||
@Configuration
|
@Configuration
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
@RestController
|
@RestController
|
||||||
|
@Import(NoSecurityConfiguration.class)
|
||||||
protected static class Application {
|
protected static class Application {
|
||||||
|
|
||||||
@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 {
|
||||||
throw new IllegalAccessException("It should has Foo and Bar header");
|
throw new IllegalAccessException("It should has Foo and Bar header");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,17 +185,21 @@ public class FeignClientUsingPropertiesTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class FooRequestInterceptor implements RequestInterceptor {
|
public static class FooRequestInterceptor implements RequestInterceptor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(RequestTemplate template) {
|
public void apply(RequestTemplate template) {
|
||||||
template.header("Foo", "Foo");
|
template.header("Foo", "Foo");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class BarRequestInterceptor implements RequestInterceptor {
|
public static class BarRequestInterceptor implements RequestInterceptor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(RequestTemplate template) {
|
public void apply(RequestTemplate template) {
|
||||||
template.header("Bar", "Bar");
|
template.header("Bar", "Bar");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class NoRetryer implements Retryer {
|
public static class NoRetryer implements Retryer {
|
||||||
@@ -195,24 +213,29 @@ public class FeignClientUsingPropertiesTests {
|
|||||||
public Retryer clone() {
|
public Retryer clone() {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class DefaultErrorDecoder extends ErrorDecoder.Default {
|
public static class DefaultErrorDecoder extends ErrorDecoder.Default {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class FormEncoder implements Encoder {
|
public static class FormEncoder implements Encoder {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void encode(Object o, Type type, RequestTemplate requestTemplate) throws EncodeException {
|
public void encode(Object o, Type type, RequestTemplate requestTemplate)
|
||||||
|
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, MediaType.APPLICATION_FORM_URLENCODED_VALUE);
|
requestTemplate.header(HttpHeaders.CONTENT_TYPE,
|
||||||
requestTemplate.body(builder.toString());
|
MediaType.APPLICATION_FORM_URLENCODED_VALUE);
|
||||||
|
requestTemplate.body(Request.Body.bodyTemplate(builder.toString(), UTF_8));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-26
@@ -1,18 +1,17 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2016 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
package org.springframework.cloud.openfeign;
|
||||||
@@ -20,6 +19,7 @@ package org.springframework.cloud.openfeign;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
@@ -27,8 +27,7 @@ import org.springframework.mock.env.MockEnvironment;
|
|||||||
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;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.Assert.assertThat;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
@@ -38,10 +37,9 @@ public class FeignClientsRegistrarTests {
|
|||||||
|
|
||||||
@Test(expected = IllegalStateException.class)
|
@Test(expected = IllegalStateException.class)
|
||||||
public void badNameHttpPrefix() {
|
public void badNameHttpPrefix() {
|
||||||
testGetName("http://bad_hostname");
|
testGetName("https://bad_hostname");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test(expected = IllegalStateException.class)
|
@Test(expected = IllegalStateException.class)
|
||||||
public void badNameHttpsPrefix() {
|
public void badNameHttpsPrefix() {
|
||||||
testGetName("https://bad_hostname");
|
testGetName("https://bad_hostname");
|
||||||
@@ -60,19 +58,19 @@ public class FeignClientsRegistrarTests {
|
|||||||
@Test
|
@Test
|
||||||
public void goodName() {
|
public void goodName() {
|
||||||
String name = testGetName("good-name");
|
String name = testGetName("good-name");
|
||||||
assertThat("name was wrong", name, is("good-name"));
|
assertThat(name).as("name was wrong").isEqualTo("good-name");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void goodNameHttpPrefix() {
|
public void goodNameHttpPrefix() {
|
||||||
String name = testGetName("http://good-name");
|
String name = testGetName("https://good-name");
|
||||||
assertThat("name was wrong", name, is("http://good-name"));
|
assertThat(name).as("name was wrong").isEqualTo("https://good-name");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void goodNameHttpsPrefix() {
|
public void goodNameHttpsPrefix() {
|
||||||
String name = testGetName("https://goodname");
|
String name = testGetName("https://goodname");
|
||||||
assertThat("name was wrong", name, is("https://goodname"));
|
assertThat(name).as("name was wrong").isEqualTo("https://goodname");
|
||||||
}
|
}
|
||||||
|
|
||||||
private String testGetName(String name) {
|
private String testGetName(String name) {
|
||||||
@@ -81,7 +79,6 @@ public class FeignClientsRegistrarTests {
|
|||||||
return registrar.getName(Collections.<String, Object>singletonMap("name", name));
|
return registrar.getName(Collections.<String, Object>singletonMap("name", name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void testFallback() {
|
public void testFallback() {
|
||||||
new AnnotationConfigApplicationContext(FallbackTestConfig.class);
|
new AnnotationConfigApplicationContext(FallbackTestConfig.class);
|
||||||
@@ -92,30 +89,37 @@ public class FeignClientsRegistrarTests {
|
|||||||
new AnnotationConfigApplicationContext(FallbackFactoryTestConfig.class);
|
new AnnotationConfigApplicationContext(FallbackFactoryTestConfig.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@FeignClient(name = "fallbackTestClient", url = "http://localhost:8080/",
|
||||||
|
fallback = FallbackClient.class)
|
||||||
|
protected interface FallbackClient {
|
||||||
|
|
||||||
|
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
||||||
|
String fallbackTest();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@FeignClient(name = "fallbackFactoryTestClient", url = "http://localhost:8081/",
|
||||||
|
fallbackFactory = FallbackFactoryClient.class)
|
||||||
|
protected interface FallbackFactoryClient {
|
||||||
|
|
||||||
|
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
||||||
|
String fallbackFactoryTest();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
@EnableFeignClients(clients = { FeignClientsRegistrarTests.FallbackClient.class})
|
@EnableFeignClients(clients = { FeignClientsRegistrarTests.FallbackClient.class })
|
||||||
protected static class FallbackTestConfig {
|
protected static class FallbackTestConfig {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FeignClient(name = "fallbackTestClient", url = "http://localhost:8080/", fallback = FallbackClient.class)
|
|
||||||
protected interface FallbackClient {
|
|
||||||
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
|
||||||
String fallbackTest();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
@EnableFeignClients(clients = { FeignClientsRegistrarTests.FallbackFactoryClient.class})
|
@EnableFeignClients(
|
||||||
|
clients = { FeignClientsRegistrarTests.FallbackFactoryClient.class })
|
||||||
protected static class FallbackFactoryTestConfig {
|
protected static class FallbackFactoryTestConfig {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FeignClient(name = "fallbackFactoryTestClient", url = "http://localhost:8081/", fallbackFactory = FallbackFactoryClient.class)
|
|
||||||
protected interface FallbackFactoryClient {
|
|
||||||
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
|
||||||
String fallbackFactoryTest();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-9
@@ -1,26 +1,29 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2018 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
package org.springframework.cloud.openfeign;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import feign.Client;
|
||||||
|
import feign.RequestInterceptor;
|
||||||
|
import feign.httpclient.ApacheHttpClient;
|
||||||
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.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;
|
||||||
@@ -35,10 +38,6 @@ import org.springframework.cloud.test.ModifiedClassPathRunner;
|
|||||||
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 feign.Client;
|
|
||||||
import feign.RequestInterceptor;
|
|
||||||
import feign.httpclient.ApacheHttpClient;
|
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -90,7 +89,9 @@ public class FeignCompressionTests {
|
|||||||
* a bean here of type ApacheHttpClient so that the configuration will be
|
* a bean here of type ApacheHttpClient so that the configuration will be
|
||||||
* loaded correctly.
|
* loaded correctly.
|
||||||
*/
|
*/
|
||||||
return (ApacheHttpClient) client;
|
return (ApacheHttpClient) this.client;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
||||||
|
|||||||
+36
-21
@@ -1,25 +1,27 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2018 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
package org.springframework.cloud.openfeign;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
import javax.net.ssl.SSLContextSpi;
|
import javax.net.ssl.SSLContextSpi;
|
||||||
import javax.net.ssl.SSLSocketFactory;
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
import javax.net.ssl.X509TrustManager;
|
import javax.net.ssl.X509TrustManager;
|
||||||
|
|
||||||
import org.apache.http.config.Lookup;
|
import org.apache.http.config.Lookup;
|
||||||
import org.apache.http.conn.HttpClientConnectionManager;
|
import org.apache.http.conn.HttpClientConnectionManager;
|
||||||
import org.apache.http.conn.socket.ConnectionSocketFactory;
|
import org.apache.http.conn.socket.ConnectionSocketFactory;
|
||||||
@@ -28,6 +30,8 @@ import org.junit.After;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import org.springframework.boot.WebApplicationType;
|
||||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
|
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
|
||||||
import org.springframework.cloud.test.ClassPathExclusions;
|
import org.springframework.cloud.test.ClassPathExclusions;
|
||||||
@@ -35,8 +39,7 @@ import org.springframework.cloud.test.ModifiedClassPathRunner;
|
|||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Ryan Baxter
|
* @author Ryan Baxter
|
||||||
@@ -49,35 +52,47 @@ public class FeignHttpClientConfigurationTests {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
context = new SpringApplicationBuilder().properties("debug=true","feign.httpclient.disableSslValidation=true").web(false)
|
this.context = new SpringApplicationBuilder()
|
||||||
.sources(HttpClientConfiguration.class, FeignAutoConfiguration.class).run();
|
.properties("debug=true", "feign.httpclient.disableSslValidation=true")
|
||||||
|
.web(WebApplicationType.NONE)
|
||||||
|
.sources(HttpClientConfiguration.class, FeignAutoConfiguration.class)
|
||||||
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
if(context != null) {
|
if (this.context != null) {
|
||||||
context.close();
|
this.context.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void disableSslTest() throws Exception {
|
public void disableSslTest() throws Exception {
|
||||||
HttpClientConnectionManager connectionManager = context.getBean(HttpClientConnectionManager.class);
|
HttpClientConnectionManager connectionManager = this.context
|
||||||
Lookup<ConnectionSocketFactory> socketFactoryRegistry = getConnectionSocketFactoryLookup(connectionManager);
|
.getBean(HttpClientConnectionManager.class);
|
||||||
assertNotNull(socketFactoryRegistry.lookup("https"));
|
Lookup<ConnectionSocketFactory> socketFactoryRegistry = getConnectionSocketFactoryLookup(
|
||||||
assertNull(this.getX509TrustManager(socketFactoryRegistry).getAcceptedIssuers());
|
connectionManager);
|
||||||
|
assertThat(socketFactoryRegistry.lookup("https")).isNotNull();
|
||||||
|
assertThat(this.getX509TrustManager(socketFactoryRegistry).getAcceptedIssuers())
|
||||||
|
.isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Lookup<ConnectionSocketFactory> getConnectionSocketFactoryLookup(HttpClientConnectionManager connectionManager) {
|
private Lookup<ConnectionSocketFactory> getConnectionSocketFactoryLookup(
|
||||||
DefaultHttpClientConnectionOperator connectionOperator = (DefaultHttpClientConnectionOperator)this.getField(connectionManager, "connectionOperator");
|
HttpClientConnectionManager connectionManager) {
|
||||||
return (Lookup)this.getField(connectionOperator, "socketFactoryRegistry");
|
DefaultHttpClientConnectionOperator connectionOperator = (DefaultHttpClientConnectionOperator) this
|
||||||
|
.getField(connectionManager, "connectionOperator");
|
||||||
|
return (Lookup) this.getField(connectionOperator, "socketFactoryRegistry");
|
||||||
}
|
}
|
||||||
|
|
||||||
private X509TrustManager getX509TrustManager(Lookup<ConnectionSocketFactory> socketFactoryRegistry) {
|
private X509TrustManager getX509TrustManager(
|
||||||
ConnectionSocketFactory connectionSocketFactory = (ConnectionSocketFactory)socketFactoryRegistry.lookup("https");
|
Lookup<ConnectionSocketFactory> socketFactoryRegistry) {
|
||||||
SSLSocketFactory sslSocketFactory = (SSLSocketFactory)this.getField(connectionSocketFactory, "socketfactory");
|
ConnectionSocketFactory connectionSocketFactory = (ConnectionSocketFactory) socketFactoryRegistry
|
||||||
SSLContextSpi sslContext = (SSLContextSpi)this.getField(sslSocketFactory, "context");
|
.lookup("https");
|
||||||
return (X509TrustManager)this.getField(sslContext, "trustManager");
|
SSLSocketFactory sslSocketFactory = (SSLSocketFactory) this
|
||||||
|
.getField(connectionSocketFactory, "socketfactory");
|
||||||
|
SSLContextSpi sslContext = (SSLContextSpi) this.getField(sslSocketFactory,
|
||||||
|
"context");
|
||||||
|
return (X509TrustManager) this.getField(sslContext, "trustManager");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> Object getField(Object target, String name) {
|
protected <T> Object getField(Object target, String name) {
|
||||||
|
|||||||
+71
-54
@@ -1,41 +1,40 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2016 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
package org.springframework.cloud.openfeign;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.instanceOf;
|
|
||||||
import static org.hamcrest.Matchers.is;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertThat;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import feign.Client;
|
||||||
|
import feign.Feign;
|
||||||
|
import feign.Target;
|
||||||
|
import feign.httpclient.ApacheHttpClient;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
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.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.cloud.openfeign.test.NoSecurityConfiguration;
|
||||||
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.test.context.junit4.SpringRunner;
|
import org.springframework.test.context.junit4.SpringRunner;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
@@ -44,23 +43,31 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import feign.Client;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import feign.Feign;
|
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT;
|
||||||
import feign.Target;
|
|
||||||
import feign.httpclient.ApacheHttpClient;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
*/
|
*/
|
||||||
@RunWith(SpringRunner.class)
|
@RunWith(SpringRunner.class)
|
||||||
@SpringBootTest(classes = FeignHttpClientUrlTests.TestConfig.class, webEnvironment = WebEnvironment.DEFINED_PORT, value = {
|
@SpringBootTest(classes = FeignHttpClientUrlTests.TestConfig.class,
|
||||||
"spring.application.name=feignclienturltest", "feign.hystrix.enabled=false",
|
webEnvironment = DEFINED_PORT,
|
||||||
"feign.okhttp.enabled=false" })
|
value = { "spring.application.name=feignclienturltest",
|
||||||
|
"feign.hystrix.enabled=false", "feign.okhttp.enabled=false" })
|
||||||
@DirtiesContext
|
@DirtiesContext
|
||||||
public class FeignHttpClientUrlTests {
|
public class FeignHttpClientUrlTests {
|
||||||
|
|
||||||
static int port;
|
static int port;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
BeanUrlClientNoProtocol beanClientNoProtocol;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private UrlClient urlClient;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private BeanUrlClient beanClient;
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void beforeClass() {
|
public static void beforeClass() {
|
||||||
port = SocketUtils.findAvailableTcpPort();
|
port = SocketUtils.findAvailableTcpPort();
|
||||||
@@ -72,37 +79,62 @@ public class FeignHttpClientUrlTests {
|
|||||||
System.clearProperty("server.port");
|
System.clearProperty("server.port");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Autowired
|
@Test
|
||||||
private UrlClient urlClient;
|
public void testUrlHttpClient() {
|
||||||
|
assertThat(this.urlClient).as("UrlClient was null").isNotNull();
|
||||||
|
Hello hello = this.urlClient.getHello();
|
||||||
|
assertThat(hello).as("hello was null").isNotNull();
|
||||||
|
assertThat(hello).as("first hello didn't match")
|
||||||
|
.isEqualTo(new Hello("hello world 1"));
|
||||||
|
}
|
||||||
|
|
||||||
@Autowired
|
@Test
|
||||||
private BeanUrlClient beanClient;
|
public void testBeanUrl() {
|
||||||
|
Hello hello = this.beanClient.getHello();
|
||||||
|
assertThat(hello).as("hello was null").isNotNull();
|
||||||
|
assertThat(hello).as("first hello didn't match")
|
||||||
|
.isEqualTo(new Hello("hello world 1"));
|
||||||
|
}
|
||||||
|
|
||||||
@Autowired BeanUrlClientNoProtocol beanClientNoProtocol;
|
@Test
|
||||||
|
public void testBeanUrlNoProtocol() {
|
||||||
|
Hello hello = this.beanClientNoProtocol.getHello();
|
||||||
|
assertThat(hello).as("hello was null").isNotNull();
|
||||||
|
assertThat(hello).as("first hello didn't match")
|
||||||
|
.isEqualTo(new Hello("hello world 1"));
|
||||||
|
}
|
||||||
|
|
||||||
// this tests that
|
// this tests that
|
||||||
@FeignClient(name = "localappurl", url = "http://localhost:${server.port}/")
|
@FeignClient(name = "localappurl", url = "http://localhost:${server.port}/")
|
||||||
protected interface UrlClient {
|
protected interface UrlClient {
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
||||||
Hello getHello();
|
Hello getHello();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FeignClient(name = "beanappurl", url = "#{SERVER_URL}path")
|
@FeignClient(name = "beanappurl", url = "#{SERVER_URL}path")
|
||||||
protected interface BeanUrlClient {
|
protected interface BeanUrlClient {
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
||||||
Hello getHello();
|
Hello getHello();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@FeignClient(name = "beanappurlnoprotocol", url = "#{SERVER_URL_NO_PROTOCOL}path")
|
@FeignClient(name = "beanappurlnoprotocol", url = "#{SERVER_URL_NO_PROTOCOL}path")
|
||||||
protected interface BeanUrlClientNoProtocol {
|
protected interface BeanUrlClientNoProtocol {
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
||||||
Hello getHello();
|
Hello getHello();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
@RestController
|
@RestController
|
||||||
@EnableFeignClients(clients = { UrlClient.class, BeanUrlClient.class, BeanUrlClientNoProtocol.class })
|
@EnableFeignClients(clients = { UrlClient.class, BeanUrlClient.class,
|
||||||
|
BeanUrlClientNoProtocol.class })
|
||||||
|
@Import(NoSecurityConfiguration.class)
|
||||||
protected static class TestConfig {
|
protected static class TestConfig {
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
@RequestMapping(method = RequestMethod.GET, value = "/hello")
|
||||||
@@ -115,12 +147,12 @@ public class FeignHttpClientUrlTests {
|
|||||||
return getHello();
|
return getHello();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name="SERVER_URL")
|
@Bean(name = "SERVER_URL")
|
||||||
public String serverUrl() {
|
public String serverUrl() {
|
||||||
return "http://localhost:" + port + "/";
|
return "http://localhost:" + port + "/";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean(name="SERVER_URL_NO_PROTOCOL")
|
@Bean(name = "SERVER_URL_NO_PROTOCOL")
|
||||||
public String serverUrlNoProtocol() {
|
public String serverUrlNoProtocol() {
|
||||||
return "localhost:" + port + "/";
|
return "localhost:" + port + "/";
|
||||||
}
|
}
|
||||||
@@ -136,8 +168,8 @@ public class FeignHttpClientUrlTests {
|
|||||||
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 was wrong type", client,
|
assertThat(client).isInstanceOf(ApacheHttpClient.class)
|
||||||
is(instanceOf(ApacheHttpClient.class)));
|
.as("client was wrong type");
|
||||||
}
|
}
|
||||||
return feign.target(target);
|
return feign.target(target);
|
||||||
}
|
}
|
||||||
@@ -146,29 +178,8 @@ public class FeignHttpClientUrlTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testUrlHttpClient() {
|
|
||||||
assertNotNull("UrlClient was null", this.urlClient);
|
|
||||||
Hello hello = this.urlClient.getHello();
|
|
||||||
assertNotNull("hello was null", hello);
|
|
||||||
assertEquals("first hello didn't match", new Hello("hello world 1"), hello);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBeanUrl() {
|
|
||||||
Hello hello = this.beanClient.getHello();
|
|
||||||
assertNotNull("hello was null", hello);
|
|
||||||
assertEquals("first hello didn't match", new Hello("hello world 1"), hello);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testBeanUrlNoProtocol() {
|
|
||||||
Hello hello = this.beanClientNoProtocol.getHello();
|
|
||||||
assertNotNull("hello was null", hello);
|
|
||||||
assertEquals("first hello didn't match", new Hello("hello world 1"), hello);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Hello {
|
public static class Hello {
|
||||||
|
|
||||||
private String message;
|
private String message;
|
||||||
|
|
||||||
public Hello() {
|
public Hello() {
|
||||||
@@ -179,7 +190,7 @@ public class FeignHttpClientUrlTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
return message;
|
return this.message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMessage(String message) {
|
public void setMessage(String message) {
|
||||||
@@ -188,15 +199,21 @@ public class FeignHttpClientUrlTests {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) {
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
Hello that = (Hello) o;
|
Hello that = (Hello) o;
|
||||||
return Objects.equals(message, that.message);
|
return Objects.equals(this.message, that.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(message);
|
return Objects.hash(this.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-34
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2016 the original author or authors.
|
* Copyright 2016-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -16,9 +16,8 @@
|
|||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
package org.springframework.cloud.openfeign;
|
||||||
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
import feign.Logger;
|
||||||
import static org.junit.Assert.assertTrue;
|
import feign.slf4j.Slf4jLogger;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||||
@@ -26,8 +25,7 @@ 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 feign.Logger;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import feign.slf4j.Slf4jLogger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Venil Noronha
|
* @author Venil Noronha
|
||||||
@@ -36,12 +34,38 @@ public class FeignLoggerFactoryTests {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDefaultLogger() {
|
public void testDefaultLogger() {
|
||||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SampleConfiguration1.class);
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||||
|
SampleConfiguration1.class);
|
||||||
FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class);
|
FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class);
|
||||||
assertNotNull(loggerFactory);
|
assertThat(loggerFactory).isNotNull();
|
||||||
Logger logger = loggerFactory.create(Object.class);
|
Logger logger = loggerFactory.create(Object.class);
|
||||||
assertNotNull(logger);
|
assertThat(logger).isNotNull();
|
||||||
assertTrue(logger instanceof Slf4jLogger);
|
assertThat(logger instanceof Slf4jLogger).isTrue();
|
||||||
|
context.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCustomLogger() {
|
||||||
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||||
|
SampleConfiguration2.class);
|
||||||
|
FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class);
|
||||||
|
assertThat(loggerFactory).isNotNull();
|
||||||
|
Logger logger = loggerFactory.create(Object.class);
|
||||||
|
assertThat(logger).isNotNull();
|
||||||
|
assertThat(logger instanceof LoggerImpl1).isTrue();
|
||||||
|
context.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCustomLoggerFactory() {
|
||||||
|
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(
|
||||||
|
SampleConfiguration3.class);
|
||||||
|
FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class);
|
||||||
|
assertThat(loggerFactory).isNotNull();
|
||||||
|
assertThat(loggerFactory instanceof LoggerFactoryImpl).isTrue();
|
||||||
|
Logger logger = loggerFactory.create(Object.class);
|
||||||
|
assertThat(logger).isNotNull();
|
||||||
|
assertThat(logger instanceof LoggerImpl2).isTrue();
|
||||||
context.close();
|
context.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,17 +75,6 @@ public class FeignLoggerFactoryTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCustomLogger() {
|
|
||||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SampleConfiguration2.class);
|
|
||||||
FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class);
|
|
||||||
assertNotNull(loggerFactory);
|
|
||||||
Logger logger = loggerFactory.create(Object.class);
|
|
||||||
assertNotNull(logger);
|
|
||||||
assertTrue(logger instanceof LoggerImpl1);
|
|
||||||
context.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@Import(FeignClientsConfiguration.class)
|
@Import(FeignClientsConfiguration.class)
|
||||||
protected static class SampleConfiguration2 {
|
protected static class SampleConfiguration2 {
|
||||||
@@ -82,18 +95,6 @@ public class FeignLoggerFactoryTests {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCustomLoggerFactory() {
|
|
||||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SampleConfiguration3.class);
|
|
||||||
FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class);
|
|
||||||
assertNotNull(loggerFactory);
|
|
||||||
assertTrue(loggerFactory instanceof LoggerFactoryImpl);
|
|
||||||
Logger logger = loggerFactory.create(Object.class);
|
|
||||||
assertNotNull(logger);
|
|
||||||
assertTrue(logger instanceof LoggerImpl2);
|
|
||||||
context.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@Import(FeignClientsConfiguration.class)
|
@Import(FeignClientsConfiguration.class)
|
||||||
protected static class SampleConfiguration3 {
|
protected static class SampleConfiguration3 {
|
||||||
|
|||||||
+25
-14
@@ -1,30 +1,32 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2018 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
package org.springframework.cloud.openfeign;
|
||||||
|
|
||||||
import okhttp3.OkHttpClient;
|
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
import javax.net.ssl.HostnameVerifier;
|
import javax.net.ssl.HostnameVerifier;
|
||||||
|
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import org.springframework.boot.WebApplicationType;
|
||||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
|
import org.springframework.cloud.commons.httpclient.HttpClientConfiguration;
|
||||||
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
|
import org.springframework.cloud.commons.httpclient.OkHttpClientFactory;
|
||||||
@@ -33,6 +35,8 @@ import org.springframework.cloud.test.ModifiedClassPathRunner;
|
|||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
import org.springframework.util.ReflectionUtils;
|
import org.springframework.util.ReflectionUtils;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Ryan Baxter
|
* @author Ryan Baxter
|
||||||
*/
|
*/
|
||||||
@@ -44,23 +48,29 @@ public class FeignOkHttpConfigurationTests {
|
|||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
context = new SpringApplicationBuilder().properties("debug=true","feign.httpclient.disableSslValidation=true",
|
this.context = new SpringApplicationBuilder()
|
||||||
"feign.okhttp.enabled=true", "feign.httpclient.enabled=false").web(false)
|
.properties("debug=true", "feign.httpclient.disableSslValidation=true",
|
||||||
.sources(HttpClientConfiguration.class, FeignAutoConfiguration.class).run();
|
"feign.okhttp.enabled=true", "feign.httpclient.enabled=false")
|
||||||
|
.web(WebApplicationType.NONE)
|
||||||
|
.sources(HttpClientConfiguration.class, FeignAutoConfiguration.class)
|
||||||
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
if(context != null) {
|
if (this.context != null) {
|
||||||
context.close();
|
this.context.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void disableSslTest() throws Exception {
|
public void disableSslTest() throws Exception {
|
||||||
OkHttpClient httpClient = context.getBean(OkHttpClient.class);
|
OkHttpClient httpClient = this.context.getBean(OkHttpClient.class);
|
||||||
HostnameVerifier hostnameVerifier = (HostnameVerifier)this.getField(httpClient, "hostnameVerifier");
|
HostnameVerifier hostnameVerifier = (HostnameVerifier) this.getField(httpClient,
|
||||||
Assert.assertTrue(OkHttpClientFactory.TrustAllHostnames.class.isInstance(hostnameVerifier));
|
"hostnameVerifier");
|
||||||
|
assertThat(
|
||||||
|
OkHttpClientFactory.TrustAllHostnames.class.isInstance(hostnameVerifier))
|
||||||
|
.isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <T> Object getField(Object target, String name) {
|
protected <T> Object getField(Object target, String name) {
|
||||||
@@ -69,4 +79,5 @@ public class FeignOkHttpConfigurationTests {
|
|||||||
Object value = ReflectionUtils.getField(field, target);
|
Object value = ReflectionUtils.getField(field, target);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+85
-67
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2015 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -23,12 +23,15 @@ import java.util.Objects;
|
|||||||
|
|
||||||
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.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.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.http.HttpStatus;
|
import org.springframework.http.HttpStatus;
|
||||||
import org.springframework.http.ResponseEntity;
|
import org.springframework.http.ResponseEntity;
|
||||||
import org.springframework.test.annotation.DirtiesContext;
|
import org.springframework.test.annotation.DirtiesContext;
|
||||||
@@ -38,17 +41,15 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
|||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Spencer Gibb
|
* @author Spencer Gibb
|
||||||
*/
|
*/
|
||||||
@RunWith(SpringJUnit4ClassRunner.class)
|
@RunWith(SpringJUnit4ClassRunner.class)
|
||||||
@SpringBootTest(classes = SpringDecoderTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = {
|
@SpringBootTest(classes = SpringDecoderTests.Application.class,
|
||||||
"spring.application.name=springdecodertest", "spring.jmx.enabled=false" })
|
webEnvironment = WebEnvironment.RANDOM_PORT, value = {
|
||||||
|
"spring.application.name=springdecodertest", "spring.jmx.enabled=false" })
|
||||||
@DirtiesContext
|
@DirtiesContext
|
||||||
public class SpringDecoderTests extends FeignClientFactoryBean {
|
public class SpringDecoderTests extends FeignClientFactoryBean {
|
||||||
|
|
||||||
@@ -60,6 +61,7 @@ public class SpringDecoderTests extends FeignClientFactoryBean {
|
|||||||
|
|
||||||
public SpringDecoderTests() {
|
public SpringDecoderTests() {
|
||||||
setName("test");
|
setName("test");
|
||||||
|
setContextId("test");
|
||||||
}
|
}
|
||||||
|
|
||||||
public TestClient testClient() {
|
public TestClient testClient() {
|
||||||
@@ -69,66 +71,73 @@ 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(context).target(TestClient.class, "http://localhost:" + this.port);
|
return feign(this.context).target(TestClient.class,
|
||||||
|
"http://localhost:" + this.port);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResponseEntity() {
|
public void testResponseEntity() {
|
||||||
ResponseEntity<Hello> response = testClient().getHelloResponse();
|
ResponseEntity<Hello> response = testClient().getHelloResponse();
|
||||||
assertNotNull("response was null", response);
|
assertThat(response).as("response was null").isNotNull();
|
||||||
assertEquals("wrong status code", HttpStatus.OK, response.getStatusCode());
|
assertThat(response.getStatusCode()).as("wrong status code")
|
||||||
|
.isEqualTo(HttpStatus.OK);
|
||||||
Hello hello = response.getBody();
|
Hello hello = response.getBody();
|
||||||
assertNotNull("hello was null", hello);
|
assertThat(hello).as("hello was null").isNotNull();
|
||||||
assertEquals("first hello didn't match", new Hello("hello world via response"),
|
assertThat(hello).as("first hello didn't match")
|
||||||
hello);
|
.isEqualTo(new Hello("hello world via response"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleType() {
|
public void testSimpleType() {
|
||||||
Hello hello = testClient().getHello();
|
Hello hello = testClient().getHello();
|
||||||
assertNotNull("hello was null", hello);
|
assertThat(hello).as("hello was null").isNotNull();
|
||||||
assertEquals("first hello didn't match", new Hello("hello world 1"), hello);
|
assertThat(hello).as("first hello didn't match")
|
||||||
|
.isEqualTo(new Hello("hello world 1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testUserParameterizedTypeDecode() {
|
public void testUserParameterizedTypeDecode() {
|
||||||
List<Hello> hellos = testClient().getHellos();
|
List<Hello> hellos = testClient().getHellos();
|
||||||
assertNotNull("hellos was null", hellos);
|
assertThat(hellos).as("hellos was null").isNotNull();
|
||||||
assertEquals("hellos was not the right size", 2, hellos.size());
|
assertThat(hellos.size()).as("hellos was not the right size").isEqualTo(2);
|
||||||
assertEquals("first hello didn't match", new Hello("hello world 1"),
|
assertThat(hellos.get(0)).as("first hello didn't match")
|
||||||
hellos.get(0));
|
.isEqualTo(new Hello("hello world 1"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSimpleParameterizedTypeDecode() {
|
public void testSimpleParameterizedTypeDecode() {
|
||||||
List<String> hellos = testClient().getHelloStrings();
|
List<String> hellos = testClient().getHelloStrings();
|
||||||
assertNotNull("hellos was null", hellos);
|
assertThat(hellos).as("hellos was null").isNotNull();
|
||||||
assertEquals("hellos was not the right size", 2, hellos.size());
|
assertThat(hellos.size()).as("hellos was not the right size").isEqualTo(2);
|
||||||
assertEquals("first hello didn't match", "hello world 1", hellos.get(0));
|
assertThat(hellos.get(0)).as("first hello didn't match")
|
||||||
|
.isEqualTo("hello world 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void testWildcardTypeDecode() {
|
public void testWildcardTypeDecode() {
|
||||||
ResponseEntity<?> wildcard = testClient().getWildcard();
|
ResponseEntity<?> wildcard = testClient().getWildcard();
|
||||||
assertNotNull("wildcard was null", wildcard);
|
assertThat(wildcard).as("wildcard was null").isNotNull();
|
||||||
assertEquals("wrong status code", HttpStatus.OK, wildcard.getStatusCode());
|
assertThat(wildcard.getStatusCode()).as("wrong status code")
|
||||||
|
.isEqualTo(HttpStatus.OK);
|
||||||
Object wildcardBody = wildcard.getBody();
|
Object wildcardBody = wildcard.getBody();
|
||||||
assertNotNull("wildcardBody was null", wildcardBody);
|
assertThat(wildcardBody).as("wildcardBody was null").isNotNull();
|
||||||
assertTrue("wildcard not an instance of Map", wildcardBody instanceof Map);
|
assertThat(wildcardBody instanceof Map).as("wildcard not an instance of Map")
|
||||||
|
.isTrue();
|
||||||
Map<String, String> hello = (Map<String, String>) wildcardBody;
|
Map<String, String> hello = (Map<String, String>) wildcardBody;
|
||||||
assertEquals("first hello didn't match", "wildcard", hello.get("message"));
|
assertThat(hello.get("message")).as("first hello didn't match")
|
||||||
|
.isEqualTo("wildcard");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResponseEntityVoid() {
|
public void testResponseEntityVoid() {
|
||||||
ResponseEntity<Void> response = testClient().getHelloVoid();
|
ResponseEntity<Void> response = testClient().getHelloVoid();
|
||||||
assertNotNull("response was null", response);
|
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");
|
||||||
assertNotNull("headerVals was null", headerVals);
|
assertThat(headerVals).as("headerVals was null").isNotNull();
|
||||||
assertEquals("headerVals size was wrong", 1, headerVals.size());
|
assertThat(headerVals.size()).as("headerVals size was wrong").isEqualTo(1);
|
||||||
String header = headerVals.get(0);
|
String header = headerVals.get(0);
|
||||||
assertEquals("header was wrong", "myval", header);
|
assertThat(header).as("header was wrong").isEqualTo("myval");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = RuntimeException.class)
|
@Test(expected = RuntimeException.class)
|
||||||
@@ -139,43 +148,12 @@ public class SpringDecoderTests extends FeignClientFactoryBean {
|
|||||||
@Test
|
@Test
|
||||||
public void testDecodes404() {
|
public void testDecodes404() {
|
||||||
final ResponseEntity<String> response = testClient(true).getNotFound();
|
final ResponseEntity<String> response = testClient(true).getNotFound();
|
||||||
assertNotNull("response was null", response);
|
assertThat(response).as("response was null").isNotNull();
|
||||||
assertNull("response body was not null", response.getBody());
|
assertThat(response.getBody()).as("response body was not null").isNull();
|
||||||
}
|
|
||||||
|
|
||||||
public static class Hello {
|
|
||||||
private String message;
|
|
||||||
|
|
||||||
public Hello() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public Hello(String message) {
|
|
||||||
this.message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMessage() {
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMessage(String message) {
|
|
||||||
this.message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object o) {
|
|
||||||
if (this == o) return true;
|
|
||||||
if (o == null || getClass() != o.getClass()) return false;
|
|
||||||
Hello that = (Hello) o;
|
|
||||||
return Objects.equals(message, that.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hash(message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected interface TestClient {
|
protected interface TestClient {
|
||||||
|
|
||||||
@RequestMapping(method = RequestMethod.GET, value = "/helloresponse")
|
@RequestMapping(method = RequestMethod.GET, value = "/helloresponse")
|
||||||
ResponseEntity<Hello> getHelloResponse();
|
ResponseEntity<Hello> getHelloResponse();
|
||||||
|
|
||||||
@@ -196,11 +174,51 @@ public class SpringDecoderTests extends FeignClientFactoryBean {
|
|||||||
|
|
||||||
@GetMapping("/helloWildcard")
|
@GetMapping("/helloWildcard")
|
||||||
ResponseEntity<?> getWildcard();
|
ResponseEntity<?> getWildcard();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Hello {
|
||||||
|
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
public Hello() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public Hello(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessage() {
|
||||||
|
return this.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMessage(String message) {
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (o == null || getClass() != o.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Hello that = (Hello) o;
|
||||||
|
return Objects.equals(this.message, that.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(this.message);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableAutoConfiguration
|
@EnableAutoConfiguration
|
||||||
@RestController
|
@RestController
|
||||||
|
@Import(NoSecurityConfiguration.class)
|
||||||
protected static class Application implements TestClient {
|
protected static class Application implements TestClient {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -243,7 +261,7 @@ public class SpringDecoderTests extends FeignClientFactoryBean {
|
|||||||
public ResponseEntity<?> getWildcard() {
|
public ResponseEntity<?> getWildcard() {
|
||||||
return ResponseEntity.ok(new Hello("wildcard"));
|
return ResponseEntity.ok(new Hello("wildcard"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-21
@@ -1,11 +1,11 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013-2017 the original author or authors.
|
* Copyright 2013-2019 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.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
@@ -14,61 +14,64 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
package org.springframework.cloud.openfeign;
|
package org.springframework.cloud.openfeign;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import org.springframework.boot.WebApplicationType;
|
||||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
|
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.CachingSpringLoadBalancerFactory;
|
||||||
import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer;
|
import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer;
|
||||||
import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration;
|
import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration;
|
||||||
import org.springframework.cloud.openfeign.ribbon.RetryableFeignLoadBalancer;
|
import org.springframework.cloud.openfeign.ribbon.RetryableFeignLoadBalancer;
|
||||||
import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration;
|
|
||||||
import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration;
|
|
||||||
import org.springframework.cloud.test.ClassPathExclusions;
|
import org.springframework.cloud.test.ClassPathExclusions;
|
||||||
import org.springframework.cloud.test.ModifiedClassPathRunner;
|
import org.springframework.cloud.test.ModifiedClassPathRunner;
|
||||||
import org.springframework.context.ConfigurableApplicationContext;
|
import org.springframework.context.ConfigurableApplicationContext;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.hamcrest.Matchers.hasSize;
|
|
||||||
import static org.hamcrest.Matchers.instanceOf;
|
|
||||||
import static org.hamcrest.Matchers.not;
|
|
||||||
import static org.hamcrest.core.Is.is;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Ryan Baxter
|
* @author Ryan Baxter
|
||||||
*/
|
*/
|
||||||
@RunWith(ModifiedClassPathRunner.class)
|
@RunWith(ModifiedClassPathRunner.class)
|
||||||
@ClassPathExclusions({"spring-retry-*.jar", "spring-boot-starter-aop-*.jar"})
|
@ClassPathExclusions({ "spring-retry-*.jar", "spring-boot-starter-aop-*.jar" })
|
||||||
public class SpringRetryDisabledTests {
|
public class SpringRetryDisabledTests {
|
||||||
|
|
||||||
private ConfigurableApplicationContext context;
|
private ConfigurableApplicationContext context;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
context = new SpringApplicationBuilder().web(false)
|
this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE)
|
||||||
.sources(RibbonAutoConfiguration.class, LoadBalancerAutoConfiguration.class, RibbonClientConfiguration.class,
|
.sources(RibbonAutoConfiguration.class,
|
||||||
FeignRibbonClientAutoConfiguration.class).run();
|
LoadBalancerAutoConfiguration.class,
|
||||||
|
RibbonClientConfiguration.class,
|
||||||
|
FeignRibbonClientAutoConfiguration.class)
|
||||||
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
if(context != null) {
|
if (this.context != null) {
|
||||||
context.close();
|
this.context.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLoadBalancedRetryFactoryBean() throws Exception {
|
public void testLoadBalancedRetryFactoryBean() throws Exception {
|
||||||
Map<String, CachingSpringLoadBalancerFactory> lbFactorys = context.getBeansOfType(CachingSpringLoadBalancerFactory.class);
|
Map<String, CachingSpringLoadBalancerFactory> lbFactorys = this.context
|
||||||
assertThat(lbFactorys.values(), hasSize(1));
|
.getBeansOfType(CachingSpringLoadBalancerFactory.class);
|
||||||
FeignLoadBalancer lb =lbFactorys.values().iterator().next().create("foo");
|
assertThat(lbFactorys.values()).hasSize(1);
|
||||||
assertThat(lb, instanceOf(FeignLoadBalancer.class));
|
FeignLoadBalancer lb = lbFactorys.values().iterator().next().create("foo");
|
||||||
assertThat(lb, is(not(instanceOf(RetryableFeignLoadBalancer.class))));
|
assertThat(lb).isInstanceOf(FeignLoadBalancer.class);
|
||||||
|
assertThat(lb).isNotInstanceOf(RetryableFeignLoadBalancer.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user