Compare commits

..

12 Commits

Author SHA1 Message Date
Olga Maciaszek-Sharma fd1c364323 Update surefire plugin config. 2 years ago
Olga Maciaszek-Sharma 0cde956b7e User surefire plugin config from Spring Cloud Build. 2 years ago
Olga Maciaszek-Sharma 8d8f82ad0c Add docs and javadocs. 2 years ago
Olga Maciaszek-Sharma b10632f6b5 Fix tests. 2 years ago
Olga Maciaszek-Sharma c44782d8b4 Add native image support. 2 years ago
Olga Maciaszek-Sharma d501773120 Initialize child contexts at creation. 2 years ago
Olga Maciaszek-Sharma ec64c62abb Merge remote-tracking branch 'origin/main' into aot-support 2 years ago
Olga Maciaszek-Sharma b7c43f02c1 Fix checkstyle. Reformat. 2 years ago
Olga Maciaszek-Sharma 77ca6f9535 Eagerly resolve Feign Client property values by default. Fix tests. 2 years ago
Olga Maciaszek-Sharma 6a833cd3db Move changes over to a BeanFactoryInitializationAotProcessor. 2 years ago
Olga Maciaszek-Sharma 577153a40c Create AOT contribution for FeignClient beans. 2 years ago
Olga Maciaszek-Sharma f9cc51c16b Add FeignChildContextInitializer. 2 years ago
  1. 49
      .github/dependabot.yml
  2. 32
      .github/workflows/deploy-docs.yml
  3. 6
      .github/workflows/maven.yml
  4. 16
      .gitignore
  5. 1
      .java-version
  6. 3
      .mvn/maven.config
  7. BIN
      .mvn/wrapper/maven-wrapper.jar
  8. 19
      .mvn/wrapper/maven-wrapper.properties
  9. 292
      README.adoc
  10. 39
      docs/antora-playbook.yml
  11. 12
      docs/antora.yml
  12. 4
      docs/modules/ROOT/nav.adoc
  13. 6
      docs/modules/ROOT/pages/configprops.adoc
  14. 1
      docs/modules/ROOT/pages/index.adoc
  15. 3
      docs/modules/ROOT/partials/_attributes.adoc
  16. 113
      docs/modules/ROOT/partials/_configprops.adoc
  17. 6
      docs/modules/ROOT/partials/_conventions.adoc
  18. 6
      docs/modules/ROOT/partials/_metrics.adoc
  19. 6
      docs/modules/ROOT/partials/_spans.adoc
  20. 65
      docs/pom.xml
  21. 20
      docs/src/main/antora/resources/antora-resources/antora.yml
  22. 15
      docs/src/main/asciidoc/README.adoc
  23. 2
      docs/src/main/asciidoc/_attributes.adoc
  24. 37
      docs/src/main/asciidoc/_configprops.adoc
  25. 7
      docs/src/main/asciidoc/appendix.adoc
  26. 1
      docs/src/main/asciidoc/index.adoc
  27. 3
      docs/src/main/asciidoc/intro.adoc
  28. 71
      docs/src/main/asciidoc/spring-cloud-openfeign.adoc
  29. 303
      mvnw
  30. 96
      mvnw.cmd
  31. 8
      pom.xml
  32. 25
      spring-cloud-openfeign-core/pom.xml
  33. 31
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java
  34. 14
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java
  35. 29
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerTargeter.java
  36. 14
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java
  37. 18
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientProperties.java
  38. 3
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsRegistrar.java
  39. 49
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/PropertyBasedTarget.java
  40. 47
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/aot/FeignClientBeanFactoryInitializationAotProcessor.java
  41. 45
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/clientconfig/Http2ClientFeignConfiguration.java
  42. 9
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/clientconfig/HttpClient5FeignConfiguration.java
  43. 9
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignAcceptGzipEncodingAutoConfiguration.java
  44. 9
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingAutoConfiguration.java
  45. 51
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/OkHttpFeignClientBeanMissingCondition.java
  46. 4
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/FeignLoadBalancerAutoConfiguration.java
  47. 76
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/Http2ClientFeignLoadBalancerConfiguration.java
  48. 7
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClient.java
  49. 91
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FeignHttpClientProperties.java
  50. 36
      spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java
  51. 8
      spring-cloud-openfeign-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json
  52. 1
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientCacheTests.java
  53. 5
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientConfigurationTests.java
  54. 41
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientUsingPropertiesTests.java
  55. 42
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignCompressionTests.java
  56. 70
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignHttp2ClientConfigurationTests.java
  57. 12
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignOkHttpConfigurationTests.java
  58. 5
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/NonRefreshableFeignClientUrlTests.java
  59. 197
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/aot/FeignClientBeanFactoryInitializationAotProcessorTests.java
  60. 134
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/FallbackSupportFactoryBeanTests.java
  61. 36
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/loadbalancer/FeignLoadBalancerAutoConfigurationTests.java
  62. 44
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClientTests.java
  63. 79
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/protocol/FeignOkHttpProtocolsTests.java
  64. 37
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/FeignHttpClientPropertiesTests.java
  65. 5
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageJacksonModuleTests.java
  66. 44
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java
  67. 81
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/Http2ClientConfigurationTests.java
  68. 256
      spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignHttp2ClientTests.java
  69. 1
      spring-cloud-openfeign-core/src/test/resources/feign-properties.properties
  70. 11
      spring-cloud-openfeign-core/src/test/resources/logback-test.xml
  71. 17
      spring-cloud-openfeign-dependencies/pom.xml
  72. 5
      spring-cloud-starter-openfeign/pom.xml

49
.github/dependabot.yml

@ -1,49 +0,0 @@ @@ -1,49 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
target-branch: "3.1.x" # oldest OSS supported branch
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/"
target-branch: "4.0.x" # oldest OSS supported branch
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/"
target-branch: "main"
schedule:
interval: "weekly"
- package-ecosystem: maven
directory: /
schedule:
interval: daily
target-branch: 3.1.x
ignore:
# only upgrade patch versions for maintenance branch
- dependency-name: "*"
update-types:
- version-update:semver-major
- version-update:semver-minor
- package-ecosystem: maven
directory: /
schedule:
interval: daily
target-branch: 4.0.x
ignore:
# only upgrade patch versions for maintenance branch
- dependency-name: "*"
update-types:
- version-update:semver-major
- version-update:semver-minor
- package-ecosystem: maven
directory: /
schedule:
interval: daily
target-branch: main
ignore:
# only upgrade by minor or patch
- dependency-name: "*"
update-types:
- version-update:semver-major

32
.github/workflows/deploy-docs.yml

@ -1,32 +0,0 @@ @@ -1,32 +0,0 @@
name: Deploy Docs
on:
push:
branches-ignore: [ gh-pages ]
tags: '**'
repository_dispatch:
types: request-build-reference # legacy
#schedule:
#- cron: '0 10 * * *' # Once per day at 10am UTC
workflow_dispatch:
permissions:
actions: write
jobs:
build:
runs-on: ubuntu-latest
# if: github.repository_owner == 'spring-cloud'
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: docs-build
fetch-depth: 1
- name: Dispatch (partial build)
if: github.ref_type == 'branch'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD) -f build-refname=${{ github.ref_name }}
- name: Dispatch (full build)
if: github.ref_type == 'tag'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh workflow run deploy-docs.yml -r $(git rev-parse --abbrev-ref HEAD)

6
.github/workflows/maven.yml

@ -19,15 +19,15 @@ jobs: @@ -19,15 +19,15 @@ jobs:
java: ["17"]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- name: Set up JDK ${{ matrix.java }}
uses: actions/setup-java@v3
uses: actions/setup-java@v2
with:
distribution: 'zulu'
java-version: ${{ matrix.java }}
cache: 'maven'
- name: Build with Maven
run: ./mvnw clean install -B -U -P sonar
- uses: codecov/codecov-action@v3
- uses: codecov/codecov-action@v1
with:
fail_ci_if_error: true

16
.gitignore vendored

@ -1,6 +1,3 @@ @@ -1,6 +1,3 @@
/application.yml
/application.properties
asciidoctor.css
*~
#*
*#
@ -8,7 +5,6 @@ asciidoctor.css @@ -8,7 +5,6 @@ asciidoctor.css
.classpath
.project
.settings/
.settings
.springBeans
target/
bin/
@ -22,16 +18,6 @@ _site/ @@ -22,16 +18,6 @@ _site/
.shelf
*.swp
*.swo
/spring-cloud-release-tools*.jar
antrun
.vscode/
.flattened-pom.xml
node
node_modules
build
_configprops.adoc
_spans.adoc
_metrics.adoc
_conventions.adoc
package.json
package-lock.json

1
.java-version

@ -1 +0,0 @@ @@ -1 +0,0 @@
17

3
.mvn/maven.config

@ -1,2 +1 @@ @@ -1,2 +1 @@
-DaltSnapshotDeploymentRepository=repo.spring.io::default::https://repo.spring.io/libs-snapshot-local
-P spring
-DaltSnapshotDeploymentRepository=repo.spring.io::default::https://repo.spring.io/libs-snapshot-local -P spring

BIN
.mvn/wrapper/maven-wrapper.jar vendored

Binary file not shown.

19
.mvn/wrapper/maven-wrapper.properties vendored

@ -1,18 +1 @@ @@ -1,18 +1 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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
#
# 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.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.4/apache-maven-3.9.4-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar
distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip

292
README.adoc

@ -7,21 +7,43 @@ Edit the files in the src/main/asciidoc/ directory instead. @@ -7,21 +7,43 @@ Edit the files in the src/main/asciidoc/ directory instead.
image::https://github.com/spring-cloud/spring-cloud-openfeign/workflows/Build/badge.svg?branch=main&style=svg["Build",link="https://github.com/spring-cloud/spring-cloud-openfeign/actions"]
image:https://codecov.io/gh/spring-cloud/spring-cloud-openfeign/branch/main/graph/badge.svg["Codecov", link="https://app.codecov.io/gh/spring-cloud/spring-cloud-openfeign/tree/main"]
image:https://codecov.io/gh/spring-cloud/spring-cloud-openfeign/branch/main/graph/badge.svg["Codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-openfeign"]
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}
:core_path: {project-root}/spring-cloud-openfeign-core
This project provides OpenFeign integrations for Spring Boot apps through autoconfiguration
and binding to the Spring Environment and other Spring programming model idioms.
[[features]]
== Features
* Declarative REST Client: Feign creates a dynamic implementation of an interface decorated with JAX-RS or Spring MVC annotations
[[building]]
== Building
:jdkversion: 17
[[basic-compile-and-test]]
== Basic Compile and Test
=== Basic Compile and Test
To build the source you will need to install JDK {jdkversion}.
@ -48,36 +70,31 @@ source control. @@ -48,36 +70,31 @@ source control.
The projects that require middleware (i.e. Redis) for testing generally
require that a local instance of [Docker](https://www.docker.com/get-started) is installed and running.
[[documentation]]
== Documentation
The spring-cloud-build module has a "docs" profile, and if you switch
that on it will try to build asciidoc sources using https://docs.antora.org/antora/latest/[Antora] from
`modules/ROOT/`.
=== Documentation
As part of that process it will look for a
`docs/src/main/asciidoc/README.adoc` and process it by loading all the includes, but not
The spring-cloud-build module has a "docs" profile, and if you switch
that on it will try to build asciidoc sources from
`src/main/asciidoc`. As part of that process it will look for a
`README.adoc` and process it by loading all the includes, but not
parsing or rendering it, just copying it to `${main.basedir}`
(defaults to `$\{basedir}`, i.e. the root of the project). If there are
(defaults to `${basedir}`, i.e. the root of the project). If there are
any changes in the README it will then show up after a Maven build as
a modified file in the correct place. Just commit it and push the change.
[[working-with-the-code]]
== Working with the code
=== Working with the code
If you don't have an IDE preference we would recommend that you use
https://www.springsource.com/developer/sts[Spring Tools Suite] or
https://eclipse.org[Eclipse] when working with the code. We use the
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.
[[activate-the-spring-maven-profile]]
=== Activate the Spring Maven profile
==== Activate the Spring Maven profile
Spring Cloud projects require the 'spring' Maven profile to be activated to resolve
the spring milestone and snapshot repositories. Use your preferred IDE to set this
profile to be active, or you may experience build errors.
[[importing-into-eclipse-with-m2eclipse]]
=== Importing into eclipse with m2eclipse
==== Importing into eclipse with m2eclipse
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
marketplace".
@ -91,8 +108,7 @@ add the "spring" profile to your `settings.xml`. Alternatively you can @@ -91,8 +108,7 @@ add the "spring" profile to your `settings.xml`. Alternatively you can
copy the repository settings from the "spring" profile of the parent
pom into your `settings.xml`.
[[importing-into-eclipse-without-m2eclipse]]
=== Importing into eclipse without m2eclipse
==== Importing into eclipse without m2eclipse
If you prefer not to use m2eclipse you can generate eclipse project metadata using the
following command:
@ -105,12 +121,240 @@ The generated eclipse projects can be imported by selecting `import existing pro @@ -105,12 +121,240 @@ The generated eclipse projects can be imported by selecting `import existing pro
from the `file` menu.
[[contributing]]
== Contributing
NOTE: Spring Cloud is released under the non-restrictive Apache 2.0 license. If you would like to contribute to this section of the documentation or if you find an error, please find the source code and issue trackers in the project at {github-project}[github].
:spring-cloud-build-branch: master
Spring Cloud is released under the non-restrictive Apache 2.0 license,
and follows a very standard Github development process, using Github
tracker for issues and merging pull requests into master. If you want
to contribute even something trivial please do not hesitate, but
follow the guidelines below.
=== Sign the Contributor License Agreement
Before we accept a non-trivial patch or pull request we will need you to sign the
https://cla.pivotal.io/sign/spring[Contributor License Agreement].
Signing the contributor's agreement does not grant anyone commit rights to the main
repository, but it does mean that we can accept your contributions, and you will get an
author credit if we do. Active contributors might be asked to join the core team, and
given the ability to merge pull requests.
=== Code of Conduct
This project adheres to the Contributor Covenant https://github.com/spring-cloud/spring-cloud-build/blob/master/docs/src/main/asciidoc/code-of-conduct.adoc[code of
conduct]. By participating, you are expected to uphold this code. Please report
unacceptable behavior to spring-code-of-conduct@pivotal.io.
=== Code Conventions and Housekeeping
None of these is essential for a pull request, but they will all help. They can also be
added after the original pull request but before a merge.
* Use the Spring Framework code format conventions. If you use Eclipse
you can import formatter settings using 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
Cloud Build] project. If using IntelliJ, you can use the
https://plugins.jetbrains.com/plugin/6546[Eclipse Code Formatter
Plugin] to import the same file.
* 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
for.
* Add the ASF license header comment to all new `.java` files (copy from existing files
in the project)
* Add yourself as an `@author` to the .java files that you modify substantially (more
than cosmetic changes).
* Add some Javadocs and, if you change the namespace, some XSD doc elements.
* 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
other target branch in the main project).
* 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
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.
=== Duplicate Finder
Spring Cloud Build brings along the `basepom:duplicate-finder-maven-plugin`, that enables flagging duplicate and conflicting classes and resources on the java classpath.
==== Duplicate Finder configuration
Duplicate finder is *enabled by default* and will run in the `verify` phase of your Maven build, but it will only take effect in your project if you add the `duplicate-finder-maven-plugin` to the `build` section of the projecst's `pom.xml`.
.pom.xml
[source,xml]
----
<build>
<plugins>
<plugin>
<groupId>org.basepom.maven</groupId>
<artifactId>duplicate-finder-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
----
For other properties, we have set defaults as listed in the https://github.com/basepom/duplicate-finder-maven-plugin/wiki[plugin documentation].
You can easily override them but setting the value of the selected property prefixed with `duplicate-finder-maven-plugin`. For example, set `duplicate-finder-maven-plugin.skip` to `true` in order to skip duplicates check in your build.
If you need to add `ignoredClassPatterns` or `ignoredResourcePatterns` to your setup, make sure to add them in the plugin configuration section of your project:
[source,xml]
----
<build>
<plugins>
<plugin>
<groupId>org.basepom.maven</groupId>
<artifactId>duplicate-finder-maven-plugin</artifactId>
<configuration>
<ignoredClassPatterns>
<ignoredClassPattern>org.joda.time.base.BaseDateTime</ignoredClassPattern>
<ignoredClassPattern>.*module-info</ignoredClassPattern>
</ignoredClassPatterns>
<ignoredResourcePatterns>
<ignoredResourcePattern>changelog.txt</ignoredResourcePattern>
</ignoredResourcePatterns>
</configuration>
</plugin>
</plugins>
</build>
----
[[license]]
== License
The project license file is available https://raw.githubusercontent.com/spring-cloud/spring-cloud-openfeign/main/LICENSE.txt[here].

39
docs/antora-playbook.yml

@ -1,39 +0,0 @@ @@ -1,39 +0,0 @@
antora:
extensions:
- '@springio/antora-extensions/partial-build-extension'
- require: '@springio/antora-extensions/latest-version-extension'
- require: '@springio/antora-extensions/inject-collector-cache-config-extension'
- '@antora/collector-extension'
- '@antora/atlas-extension'
- require: '@springio/antora-extensions/root-component-extension'
root_component_name: 'cloud-openfeign'
- '@springio/antora-extensions/static-page-extension'
site:
title: Spring Cloud Openfeign
url: https://docs.spring.io/spring-cloud-openfeign/reference/
content:
sources:
- url: ./..
branches: HEAD
start_path: docs
worktrees: true
asciidoc:
attributes:
page-stackoverflow-url: https://stackoverflow.com/tags/spring-cloud
page-pagination: ''
hide-uri-scheme: '@'
tabs-sync-option: '@'
chomp: 'all'
extensions:
- '@asciidoctor/tabs'
- '@springio/asciidoctor-extensions'
sourcemap: true
urls:
latest_version_segment: ''
runtime:
log:
failure_level: warn
format: pretty
ui:
bundle:
url: https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.2/ui-bundle.zip

12
docs/antora.yml

@ -1,12 +0,0 @@ @@ -1,12 +0,0 @@
name: cloud-openfeign
version: true
title: Spring Cloud OpenFeign
nav:
- modules/ROOT/nav.adoc
ext:
collector:
run:
command: ./mvnw --no-transfer-progress -B process-resources -Pdocs -pl docs -Dantora-maven-plugin.phase=none -Dgenerate-docs.phase=none -Dgenerate-readme.phase=none -Dgenerate-cloud-resources.phase=none -Dmaven-dependency-plugin-for-docs.phase=none -Dmaven-dependency-plugin-for-docs-classes.phase=none -DskipTests
local: true
scan:
dir: ./target/classes/antora-resources/

4
docs/modules/ROOT/nav.adoc

@ -1,4 +0,0 @@ @@ -1,4 +0,0 @@
* xref:index.adoc[Introduction]
* xref:spring-cloud-openfeign.adoc[]
* xref:appendix.adoc[]
** xref:configprops.adoc[]

6
docs/modules/ROOT/pages/configprops.adoc

@ -1,6 +0,0 @@ @@ -1,6 +0,0 @@
[[configuration-properties]]
= Configuration Properties
Below you can find a list of configuration properties.
include::partial$_configprops.adoc[]

1
docs/modules/ROOT/pages/index.adoc

@ -1 +0,0 @@ @@ -1 +0,0 @@
include::intro.adoc[]

3
docs/modules/ROOT/partials/_attributes.adoc

@ -1,3 +0,0 @@ @@ -1,3 +0,0 @@
:sc-ext: java
:project-full-name: Spring Cloud OpenFeign
:all: {asterisk}{asterisk}

113
docs/modules/ROOT/partials/_configprops.adoc

@ -1,113 +0,0 @@ @@ -1,113 +0,0 @@
|===
|Name | Default | Description
|spring.cloud.compatibility-verifier.compatible-boot-versions | `+++3.2.x+++` | Default accepted versions for the Spring Boot dependency. You can set {@code x} for the patch version if you don't want to specify a concrete value. Example: {@code 3.4.x}
|spring.cloud.compatibility-verifier.enabled | `+++false+++` | Enables creation of Spring Cloud compatibility verification.
|spring.cloud.config.allow-override | `+++true+++` | Flag to indicate that {@link #isOverrideSystemProperties() systemPropertiesOverride} can be used. Set to false to prevent users from changing the default accidentally. Default true.
|spring.cloud.config.initialize-on-context-refresh | `+++false+++` | Flag to initialize bootstrap configuration on context refresh event. Default false.
|spring.cloud.config.override-none | `+++false+++` | Flag to indicate that when {@link #setAllowOverride(boolean) allowOverride} is true, external properties should take lowest priority and should not override any existing property sources (including local config files). Default false. This will only have an effect when using config first bootstrap.
|spring.cloud.config.override-system-properties | `+++true+++` | Flag to indicate that the external properties should override system properties. Default true.
|spring.cloud.decrypt-environment-post-processor.enabled | `+++true+++` | Enable the DecryptEnvironmentPostProcessor.
|spring.cloud.discovery.client.composite-indicator.enabled | `+++true+++` | Enables discovery client composite health indicator.
|spring.cloud.discovery.client.health-indicator.enabled | `+++true+++` |
|spring.cloud.discovery.client.health-indicator.include-description | `+++false+++` |
|spring.cloud.discovery.client.health-indicator.use-services-query | `+++true+++` | Whether or not the indicator should use {@link DiscoveryClient#getServices} to check its health. When set to {@code false} the indicator instead uses the lighter {@link DiscoveryClient#probe()}. This can be helpful in large deployments where the number of services returned makes the operation unnecessarily heavy.
|spring.cloud.discovery.client.simple.instances | |
|spring.cloud.discovery.client.simple.local.host | |
|spring.cloud.discovery.client.simple.local.instance-id | |
|spring.cloud.discovery.client.simple.local.metadata | |
|spring.cloud.discovery.client.simple.local.port | `+++0+++` |
|spring.cloud.discovery.client.simple.local.secure | `+++false+++` |
|spring.cloud.discovery.client.simple.local.service-id | |
|spring.cloud.discovery.client.simple.local.uri | |
|spring.cloud.discovery.client.simple.order | |
|spring.cloud.discovery.enabled | `+++true+++` | Enables discovery client health indicators.
|spring.cloud.features.enabled | `+++true+++` | Enables the features endpoint.
|spring.cloud.httpclientfactories.apache.enabled | `+++true+++` | Enables creation of Apache Http Client factory beans.
|spring.cloud.httpclientfactories.ok.enabled | `+++true+++` | Enables creation of OK Http Client factory beans.
|spring.cloud.hypermedia.refresh.fixed-delay | `+++5000+++` |
|spring.cloud.hypermedia.refresh.initial-delay | `+++10000+++` |
|spring.cloud.inetutils.default-hostname | `+++localhost+++` | The default hostname. Used in case of errors.
|spring.cloud.inetutils.default-ip-address | `+++127.0.0.1+++` | The default IP address. Used in case of errors.
|spring.cloud.inetutils.ignored-interfaces | | List of Java regular expressions for network interfaces that will be ignored.
|spring.cloud.inetutils.preferred-networks | | List of Java regular expressions for network addresses that will be preferred.
|spring.cloud.inetutils.timeout-seconds | `+++1+++` | Timeout, in seconds, for calculating hostname.
|spring.cloud.inetutils.use-only-site-local-interfaces | `+++false+++` | Whether to use only interfaces with site local addresses. See {@link InetAddress#isSiteLocalAddress()} for more details.
|spring.cloud.loadbalancer.call-get-with-request-on-delegates | `+++true+++` | If this flag is set to {@code true}, {@code ServiceInstanceListSupplier#get(Request request)} method will be implemented to call {@code delegate.get(request)} in classes assignable from {@code DelegatingServiceInstanceListSupplier} that don't already implement that method, with the exclusion of {@code CachingServiceInstanceListSupplier} and {@code HealthCheckServiceInstanceListSupplier}, which should be placed in the instance supplier hierarchy directly after the supplier performing instance retrieval over the network, before any request-based filtering is done, {@code true} by default.
|spring.cloud.loadbalancer.clients | |
|spring.cloud.loadbalancer.eager-load.clients | |
|spring.cloud.loadbalancer.health-check.initial-delay | `+++0+++` | Initial delay value for the HealthCheck scheduler.
|spring.cloud.loadbalancer.health-check.interval | `+++25s+++` | Interval for rerunning the HealthCheck scheduler.
|spring.cloud.loadbalancer.health-check.interval | `+++25s+++` | Interval for rerunning the HealthCheck scheduler.
|spring.cloud.loadbalancer.health-check.path | | Path at which the health-check request should be made. Can be set up per `serviceId`. A `default` value can be set up as well. If none is set up, `/actuator/health` will be used.
|spring.cloud.loadbalancer.health-check.port | | Path at which the health-check request should be made. If none is set, the port under which the requested service is available at the service instance.
|spring.cloud.loadbalancer.health-check.refetch-instances | `+++false+++` | Indicates whether the instances should be refetched by the `HealthCheckServiceInstanceListSupplier`. This can be used if the instances can be updated and the underlying delegate does not provide an ongoing flux.
|spring.cloud.loadbalancer.health-check.refetch-instances-interval | `+++25s+++` | Interval for refetching available service instances.
|spring.cloud.loadbalancer.health-check.repeat-health-check | `+++true+++` | Indicates whether health checks should keep repeating. It might be useful to set it to `false` if periodically refetching the instances, as every refetch will also trigger a healthcheck.
|spring.cloud.loadbalancer.health-check.update-results-list | `+++true+++` | Indicates whether the {@code healthCheckFlux} should emit on each alive {@link ServiceInstance} that has been retrieved. If set to {@code false}, the entire alive instances sequence is first collected into a list and only then emitted.
|spring.cloud.loadbalancer.hint | | Allows setting the value of <code>hint</code> that is passed on to the LoadBalancer request and can subsequently be used in {@link ReactiveLoadBalancer} implementations.
|spring.cloud.loadbalancer.hint-header-name | `+++X-SC-LB-Hint+++` | Allows setting the name of the header used for passing the hint for hint-based service instance filtering.
|spring.cloud.loadbalancer.retry.backoff.enabled | `+++false+++` | Indicates whether Reactor Retry backoffs should be applied.
|spring.cloud.loadbalancer.retry.backoff.jitter | `+++0.5+++` | Used to set `RetryBackoffSpec.jitter`.
|spring.cloud.loadbalancer.retry.backoff.max-backoff | `+++Long.MAX ms+++` | Used to set `RetryBackoffSpec.maxBackoff`.
|spring.cloud.loadbalancer.retry.backoff.min-backoff | `+++5 ms+++` | Used to set `RetryBackoffSpec#minBackoff`.
|spring.cloud.loadbalancer.retry.enabled | `+++true+++` | Enables LoadBalancer retries.
|spring.cloud.loadbalancer.retry.max-retries-on-next-service-instance | `+++1+++` | Number of retries to be executed on the next `ServiceInstance`. A `ServiceInstance` is chosen before each retry call.
|spring.cloud.loadbalancer.retry.max-retries-on-same-service-instance | `+++0+++` | Number of retries to be executed on the same `ServiceInstance`.
|spring.cloud.loadbalancer.retry.retry-on-all-exceptions | `+++false+++` | Indicates retries should be attempted for all exceptions, not only those specified in `retryableExceptions`.
|spring.cloud.loadbalancer.retry.retry-on-all-operations | `+++false+++` | Indicates retries should be attempted on operations other than `HttpMethod.GET`.
|spring.cloud.loadbalancer.retry.retryable-exceptions | `+++{}+++` | A `Set` of `Throwable` classes that should trigger a retry.
|spring.cloud.loadbalancer.retry.retryable-status-codes | `+++{}+++` | A `Set` of status codes that should trigger a retry.
|spring.cloud.loadbalancer.sticky-session.add-service-instance-cookie | `+++false+++` | Indicates whether a cookie with the newly selected instance should be added by LoadBalancer.
|spring.cloud.loadbalancer.sticky-session.instance-id-cookie-name | `+++sc-lb-instance-id+++` | The name of the cookie holding the preferred instance id.
|spring.cloud.loadbalancer.x-forwarded.enabled | `+++false+++` | To Enable X-Forwarded Headers.
|spring.cloud.openfeign.autoconfiguration.jackson.enabled | `+++true+++` | If true, PageJacksonModule and SortJacksonModule bean will be provided for Jackson page decoding.
|spring.cloud.openfeign.circuitbreaker.alphanumeric-ids.enabled | `+++false+++` | If true, Circuit Breaker ids will only contain alphanumeric characters to allow for configuration via configuration properties.
|spring.cloud.openfeign.circuitbreaker.enabled | `+++false+++` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker.
|spring.cloud.openfeign.circuitbreaker.group.enabled | `+++false+++` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker with with group.
|spring.cloud.openfeign.client.config | |
|spring.cloud.openfeign.client.decode-slash | `+++true+++` | Feign clients do not encode slash `/` characters by default. To change this behavior, set the `decodeSlash` to `false`.
|spring.cloud.openfeign.client.default-config | `+++default+++` |
|spring.cloud.openfeign.client.default-to-properties | `+++true+++` |
|spring.cloud.openfeign.client.refresh-enabled | `+++false+++` | Enables options value refresh capability for Feign.
|spring.cloud.openfeign.compression.request.enabled | `+++false+++` | Enables the request sent by Feign to be compressed.
|spring.cloud.openfeign.compression.request.mime-types | `+++[text/xml, application/xml, application/json]+++` | The list of supported mime types.
|spring.cloud.openfeign.compression.request.min-request-size | `+++2048+++` | The minimum threshold content size.
|spring.cloud.openfeign.compression.response.enabled | `+++false+++` | Enables the response from Feign to be compressed.
|spring.cloud.openfeign.encoder.charset-from-content-type | `+++false+++` | Indicates whether the charset should be derived from the {@code Content-Type} header.
|spring.cloud.openfeign.http2client.enabled | `+++false+++` | Enables the use of the Java11 HTTP 2 Client by Feign.
|spring.cloud.openfeign.httpclient.connection-timeout | `+++2000+++` |
|spring.cloud.openfeign.httpclient.connection-timer-repeat | `+++3000+++` |
|spring.cloud.openfeign.httpclient.disable-ssl-validation | `+++false+++` |
|spring.cloud.openfeign.httpclient.enabled | `+++true+++` | Enables the use of the Apache HTTP Client by Feign.
|spring.cloud.openfeign.httpclient.follow-redirects | `+++true+++` |
|spring.cloud.openfeign.httpclient.hc5.connection-request-timeout | `+++3+++` | Default value for connection request timeout.
|spring.cloud.openfeign.httpclient.hc5.connection-request-timeout-unit | | Default value for connection request timeout unit.
|spring.cloud.openfeign.httpclient.hc5.enabled | `+++false+++` | Enables the use of the Apache HTTP Client 5 by Feign.
|spring.cloud.openfeign.httpclient.hc5.pool-concurrency-policy | | Pool concurrency policies.
|spring.cloud.openfeign.httpclient.hc5.pool-reuse-policy | | Pool connection re-use policies.
|spring.cloud.openfeign.httpclient.hc5.socket-timeout | `+++5+++` | Default value for socket timeout.
|spring.cloud.openfeign.httpclient.hc5.socket-timeout-unit | | Default value for socket timeout unit.
|spring.cloud.openfeign.httpclient.http2.version | `+++HTTP_2+++` | Configure the protocols used by this client to communicate with remote servers. Uses {@link String} value of {@link HttpClient.Version}.
|spring.cloud.openfeign.httpclient.max-connections | `+++200+++` |
|spring.cloud.openfeign.httpclient.max-connections-per-route | `+++50+++` |
|spring.cloud.openfeign.httpclient.ok-http.protocols | | Configure the protocols used by this client to communicate with remote servers. Uses {@link String} values of {@link Protocol}.
|spring.cloud.openfeign.httpclient.ok-http.read-timeout | `+++60s+++` | {@link OkHttpClient} read timeout; defaults to 60 seconds.
|spring.cloud.openfeign.httpclient.time-to-live | `+++900+++` |
|spring.cloud.openfeign.httpclient.time-to-live-unit | |
|spring.cloud.openfeign.lazy-attributes-resolution | `+++false+++` | Switches @FeignClient attributes resolution mode to lazy.
|spring.cloud.openfeign.micrometer.enabled | `+++true+++` | Enables Micrometer capabilities for Feign.
|spring.cloud.openfeign.oauth2.clientRegistrationId | | Provides a clientId to be used with OAuth2.
|spring.cloud.openfeign.oauth2.enabled | `+++false+++` | Enables feign interceptor for managing oauth2 access token.
|spring.cloud.openfeign.okhttp.enabled | `+++false+++` | Enables the use of the OK HTTP Client by Feign.
|spring.cloud.refresh.additional-property-sources-to-retain | | Additional property sources to retain during a refresh. Typically only system property sources are retained. This property allows property sources, such as property sources created by EnvironmentPostProcessors to be retained as well.
|spring.cloud.refresh.enabled | `+++true+++` | Enables autoconfiguration for the refresh scope and associated features.
|spring.cloud.refresh.extra-refreshable | `+++true+++` | Additional class names for beans to post process into refresh scope.
|spring.cloud.refresh.never-refreshable | `+++true+++` | Comma separated list of class names for beans to never be refreshed or rebound.
|spring.cloud.refresh.on-restart.enabled | `+++true+++` | Enable refreshing context on start.
|spring.cloud.service-registry.auto-registration.enabled | `+++true+++` | Whether service auto-registration is enabled. Defaults to true.
|spring.cloud.service-registry.auto-registration.fail-fast | `+++false+++` | Whether startup fails if there is no AutoServiceRegistration. Defaults to false.
|spring.cloud.service-registry.auto-registration.register-management | `+++true+++` | Whether to register the management as a service. Defaults to true.
|spring.cloud.util.enabled | `+++true+++` | Enables creation of Spring Cloud utility beans.
|===

6
docs/modules/ROOT/partials/_conventions.adoc

@ -1,6 +0,0 @@ @@ -1,6 +0,0 @@
[[observability-conventions]]
=== Observability - Conventions
Below you can find a list of all `GlobalObservationConvention` and `ObservationConvention` declared by this project.

6
docs/modules/ROOT/partials/_metrics.adoc

@ -1,6 +0,0 @@ @@ -1,6 +0,0 @@
[[observability-metrics]]
=== Observability - Metrics
Below you can find a list of all metrics declared by this project.

6
docs/modules/ROOT/partials/_spans.adoc

@ -1,6 +0,0 @@ @@ -1,6 +0,0 @@
[[observability-spans]]
=== Observability - Spans
Below you can find a list of all spans declared by this project.

65
docs/pom.xml

@ -3,32 +3,22 @@ @@ -3,32 +3,22 @@
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>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign-docs</artifactId>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId>
<version>4.1.0-SNAPSHOT</version>
<relativePath>..</relativePath>
<version>4.0.0-SNAPSHOT</version>
</parent>
<scm>
<url>https://github.com/spring-cloud/spring-cloud-openfeign</url>
</scm>
<artifactId>spring-cloud-openfeign-docs</artifactId>
<packaging>jar</packaging>
<name>Spring Cloud OpenFeign Docs</name>
<description>Spring Cloud OpenFeign Docs</description>
<description>Spring Cloud Docs</description>
<properties>
<docs.main>spring-cloud-openfeign</docs.main>
<main.basedir>${basedir}/..</main.basedir>
<configprops.inclusionPattern>spring.cloud.*</configprops.inclusionPattern>
<configprops.inclusionPattern>feign.*</configprops.inclusionPattern>
<upload-docs-zip.phase>deploy</upload-docs-zip.phase>
<!-- Don't upload docs jar to central / repo.spring.io -->
<maven-deploy-plugin-default.phase>none</maven-deploy-plugin-default.phase>
<!-- Observability -->
<micrometer-docs-generator.version>1.0.2</micrometer-docs-generator.version>
<micrometer-docs-generator.inputPath>${maven.multiModuleProjectDirectory}/</micrometer-docs-generator.inputPath>
<micrometer-docs-generator.inclusionPattern>.*</micrometer-docs-generator.inclusionPattern>
<micrometer-docs-generator.outputPath>${maven.multiModuleProjectDirectory}/docs/modules/ROOT/partials/</micrometer-docs-generator.outputPath>
</properties>
<dependencies>
<dependency>
@ -43,12 +33,6 @@ @@ -43,12 +33,6 @@
<profile>
<id>docs</id>
<build>
<resources>
<resource>
<directory>src/main/antora/resources/antora-resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>pl.project13.maven</groupId>
@ -59,43 +43,16 @@ @@ -59,43 +43,16 @@
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<!-- TODO: Remove this execution if you have no observability -->
<executions>
<execution>
<id>generate-observability-docs</id>
<phase>${generate-docs.phase}</phase>
<goals>
<goal>java</goal>
</goals>
<configuration>
<mainClass>io.micrometer.docs.DocsGeneratorCommand</mainClass>
<includePluginDependencies>true</includePluginDependencies>
<arguments>
<argument>${micrometer-docs-generator.inputPath}</argument>
<argument>${micrometer-docs-generator.inclusionPattern}</argument>
<argument>${micrometer-docs-generator.outputPath}</argument>
</arguments>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-docs-generator</artifactId>
<version>${micrometer-docs-generator.version}</version>
<type>jar</type>
</dependency>
</dependencies>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
</plugin>
<plugin>
<groupId>io.spring.maven.antora</groupId>
<artifactId>antora-component-version-maven-plugin</artifactId>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>io.spring.maven.antora</groupId>
<artifactId>antora-maven-plugin</artifactId>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>

20
docs/src/main/antora/resources/antora-resources/antora.yml

@ -1,20 +0,0 @@ @@ -1,20 +0,0 @@
version: @antora-component.version@
prerelease: @antora-component.prerelease@
asciidoc:
attributes:
attribute-missing: 'warn'
chomp: 'all'
project-root: @maven.multiModuleProjectDirectory@
github-repo: @docs.main@
github-raw: https://raw.githubusercontent.com/spring-cloud/@docs.main@/@github-tag@
github-code: https://github.com/spring-cloud/@docs.main@/tree/@github-tag@
github-issues: https://github.com/spring-cloud/@docs.main@/issues/
github-wiki: https://github.com/spring-cloud/@docs.main@/wiki
spring-cloud-version: @project.version@
github-tag: @github-tag@
version-type: @version-type@
docs-url: https://docs.spring.io/@docs.main@/docs/@project.version@
raw-docs-url: https://raw.githubusercontent.com/spring-cloud/@docs.main@/@github-tag@
project-version: @project.version@
project-name: @docs.main@

15
docs/src/main/asciidoc/README.adoc

@ -1,24 +1,25 @@ @@ -1,24 +1,25 @@
image::https://github.com/spring-cloud/spring-cloud-openfeign/workflows/Build/badge.svg?branch=main&style=svg["Build",link="https://github.com/spring-cloud/spring-cloud-openfeign/actions"]
image:https://codecov.io/gh/spring-cloud/spring-cloud-openfeign/branch/main/graph/badge.svg["Codecov", link="https://app.codecov.io/gh/spring-cloud/spring-cloud-openfeign/tree/main"]
image:https://codecov.io/gh/spring-cloud/spring-cloud-openfeign/branch/main/graph/badge.svg["Codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-openfeign"]
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[]
[[features]]
== Features
* Declarative REST Client: Feign creates a dynamic implementation of an interface decorated with JAX-RS or Spring MVC annotations
[[building]]
== Building
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/docs/modules/ROOT/partials/building.adoc[]
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/docs/src/main/asciidoc/building-jdk8.adoc[]
[[contributing]]
== Contributing
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/docs/modules/ROOT/partials/contributing-docs.adoc[]
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/docs/src/main/asciidoc/contributing.adoc[]
[[license]]
== License
The project license file is available https://raw.githubusercontent.com/spring-cloud/spring-cloud-openfeign/main/LICENSE.txt[here].

2
docs/modules/ROOT/pages/_attributes.adoc → docs/src/main/asciidoc/_attributes.adoc

@ -1,6 +1,8 @@ @@ -1,6 +1,8 @@
:doctype: book
:idprefix:
:idseparator: -
:toc: left
:toclevels: 4
:tabsize: 4
:numbered:
:sectanchors:

37
docs/src/main/asciidoc/_configprops.adoc

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
|===
|Name | Default | Description
|spring.cloud.openfeign.autoconfiguration.jackson.enabled | `false` | If true, PageJacksonModule and SortJacksonModule bean will be provided for Jackson page decoding.
|spring.cloud.openfeign.circuitbreaker.enabled | `false` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker.
|spring.cloud.openfeign.circuitbreaker.group.enabled | `false` | If true, an OpenFeign client will be wrapped with a Spring Cloud CircuitBreaker circuit breaker with with group.
|spring.cloud.openfeign.client.config | |
|spring.cloud.openfeign.client.decode-slash | `true` | Feign clients do not encode slash `/` characters by default. To change this behavior, set the `decodeSlash` to `false`.
|spring.cloud.openfeign.client.default-config | `default` |
|spring.cloud.openfeign.client.default-to-properties | `true` |
|spring.cloud.openfeign.client.refresh-enabled | `false` | Enables options value refresh capability for Feign.
|spring.cloud.openfeign.compression.request.enabled | `false` | Enables the request sent by Feign to be compressed.
|spring.cloud.openfeign.compression.request.mime-types | `[text/xml, application/xml, application/json]` | The list of supported mime types.
|spring.cloud.openfeign.compression.request.min-request-size | `2048` | The minimum threshold content size.
|spring.cloud.openfeign.compression.response.enabled | `false` | Enables the response from Feign to be compressed.
|spring.cloud.openfeign.encoder.charset-from-content-type | `false` | Indicates whether the charset should be derived from the {@code Content-Type} header.
|spring.cloud.openfeign.httpclient.connection-timeout | `2000` |
|spring.cloud.openfeign.httpclient.connection-timer-repeat | `3000` |
|spring.cloud.openfeign.httpclient.disable-ssl-validation | `false` |
|spring.cloud.openfeign.httpclient.enabled | `true` | Enables the use of the Apache HTTP Client by Feign.
|spring.cloud.openfeign.httpclient.follow-redirects | `true` |
|spring.cloud.openfeign.httpclient.hc5.enabled | `false` | Enables the use of the Apache HTTP Client 5 by Feign.
|spring.cloud.openfeign.httpclient.hc5.pool-concurrency-policy | | Pool concurrency policies.
|spring.cloud.openfeign.httpclient.hc5.pool-reuse-policy | | Pool connection re-use policies.
|spring.cloud.openfeign.httpclient.hc5.socket-timeout | `5` | Default value for socket timeout.
|spring.cloud.openfeign.httpclient.hc5.socket-timeout-unit | | Default value for socket timeout unit.
|spring.cloud.openfeign.httpclient.max-connections | `200` |
|spring.cloud.openfeign.httpclient.max-connections-per-route | `50` |
|spring.cloud.openfeign.httpclient.ok-http.read-timeout | `60s` | {@link OkHttpClient} read timeout; defaults to 60 seconds.
|spring.cloud.openfeign.httpclient.time-to-live | `900` |
|spring.cloud.openfeign.httpclient.time-to-live-unit | |
|spring.cloud.openfeign.micrometer.enabled | `true` | Enables Micrometer capabilities for Feign.
|spring.cloud.openfeign.oauth2.enabled | `false` | Enables feign interceptor for managing oauth2 access token.
|spring.cloud.openfeign.oauth2.load-balanced | `false` | Enables load balancing for oauth2 access token provider.
|spring.cloud.openfeign.okhttp.enabled | `false` | Enables the use of the OK HTTP Client by Feign.
|===

7
docs/modules/ROOT/pages/appendix.adoc → docs/src/main/asciidoc/appendix.adoc

@ -1,13 +1,14 @@ @@ -1,13 +1,14 @@
:numbered!:
[appendix]
[[common-application-properties]]
= Common application properties
:page-section-summary-toc: 1
== Common application properties
include::_attributes.adoc[]
Various properties can be specified inside your `application.properties` file, inside your `application.yml` file, or as command line switches.
This appendix provides a list of common Spring Cloud OpenFeign properties and references to the underlying classes that consume them.
This appendix provides a list of common {project-full-name} properties and references to the underlying classes that consume them.
NOTE: Property contributions can come from additional jar files on your classpath, so you should not consider this an exhaustive list.
Also, you can define your own properties.
include::_configprops.adoc[]

1
docs/src/main/asciidoc/index.adoc

@ -0,0 +1 @@ @@ -0,0 +1 @@
include::spring-cloud-openfeign.adoc[]

3
docs/modules/ROOT/pages/intro.adoc → docs/src/main/asciidoc/intro.adoc

@ -1,6 +1,3 @@ @@ -1,6 +1,3 @@
[[introduction]]
= Spring Cloud OpenFeign
This project provides OpenFeign integrations for Spring Boot apps through autoconfiguration
and binding to the Spring Environment and other Spring programming model idioms.

71
docs/modules/ROOT/pages/spring-cloud-openfeign.adoc → docs/src/main/asciidoc/spring-cloud-openfeign.adoc

@ -1,5 +1,10 @@ @@ -1,5 +1,10 @@
[[features]]
= Spring Cloud OpenFeign Features
= Spring Cloud OpenFeign
include::_attributes.adoc[]
*{spring-cloud-version}*
include::intro.adoc[]
[[spring-cloud-feign]]
== Declarative REST Client: Feign
@ -133,12 +138,12 @@ Spring Cloud OpenFeign provides the following beans by default for feign (`BeanT @@ -133,12 +138,12 @@ Spring Cloud OpenFeign provides the following beans by default for feign (`BeanT
* `Client` feignClient: If Spring Cloud LoadBalancer is on the classpath, `FeignBlockingLoadBalancerClient` is used.
If none of them is on the classpath, the default feign client is used.
NOTE: `spring-cloud-starter-openfeign` supports `spring-cloud-starter-loadbalancer`. However, as is an optional dependency, you need to make sure it has been added to your project if you want to use it.
NOTE: `spring-cloud-starter-openfeign` supports `spring-cloud-starter-loadbalancer`. However, as is an optional dependency, you need to make sure it been added to your project if you want to use it.
The OkHttpClient, Apache HttpClient 5 and Http2Client Feign clients can be used by setting `spring.cloud.openfeign.okhttp.enabled` or `spring.cloud.openfeign.httpclient.hc5.enabled` or `spring.cloud.openfeign.http2client.enabled` to `true`, respectively, and having them on the classpath.
The OkHttpClient and Apache HttpClient 5 Feign clients can be used by setting `spring.cloud.openfeign.okhttp.enabled` or `spring.cloud.openfeign.httpclient.hc5.enabled` to `true`, respectively, and having them on the classpath.
You can customize the HTTP client used by providing a bean of either `org.apache.hc.client5.http.impl.classic.CloseableHttpClient` when using Apache HC5.
You can further customise http clients by setting values in the `spring.cloud.openfeign.httpclient.xxx` properties. The ones prefixed just with `httpclient` will work for all the clients, the ones prefixed with `httpclient.hc5` to Apache HttpClient 5, the ones prefixed with `httpclient.okhttp` to OkHttpClient and the ones prefixed with `httpclient.http2` to Http2Client. You can find a full list of properties you can customise in the appendix.
You can further customise http clients by setting values in the `spring.cloud.openfeign.httpclient.xxx` properties. The ones prefixed just with `httpclient` will work for all the clients, the ones prefixed with `httpclient.hc5` to Apache HttpClient 5 and the ones prefixed with `httpclient.okhttp` to OkHttpClient. You can find a full list of properties you can customise in the appendix.
TIP: Starting with Spring Cloud OpenFeign 4, the Feign Apache HttpClient 4 is no longer supported. We suggest using Apache HttpClient 5 instead.
@ -201,7 +206,6 @@ spring: @@ -201,7 +206,6 @@ spring:
requestInterceptors:
- com.example.FooRequestInterceptor
- com.example.BarRequestInterceptor
responseInterceptor: com.example.BazResponseInterceptor
dismiss404: false
encoder: com.example.SimpleEncoder
decoder: com.example.SimpleDecoder
@ -212,8 +216,6 @@ spring: @@ -212,8 +216,6 @@ spring:
queryMapEncoder: com.example.SimpleQueryMapEncoder
micrometer.enabled: false
----
`feignName` in this example refers to `@FeignClient` `value`, that is also aliased with `@FeignClient` `name` and `@FeignClient` `contextId`. In a load-balanced scenario, it also corresponds to the `serviceId` of the server app that will be used to retrieve the instances. The specified classes for decoders, retryer and other ones must have a bean in the Spring context or have a default constructor.
Default configurations can be specified in the `@EnableFeignClients` attribute `defaultConfiguration` in a similar manner as described above. The difference is that this configuration will apply to _all_ feign clients.
@ -285,7 +287,6 @@ public FeignClientConfigurer feignClientConfigurer() { @@ -285,7 +287,6 @@ public FeignClientConfigurer feignClientConfigurer() {
TIP: By default, Feign clients do not encode slash `/` characters. You can change this behaviour, by setting the value of `spring.cloud.openfeign.client.decodeSlash` to `false`.
[[springencoder-configuration]]
==== `SpringEncoder` configuration
In the `SpringEncoder` that we provide, we set `null` charset for binary content types and `UTF-8` for all the other ones.
@ -302,7 +303,6 @@ We can configure timeouts on both the default and the named client. OpenFeign wo @@ -302,7 +303,6 @@ We can configure timeouts on both the default and the named client. OpenFeign wo
NOTE: In case the server is not running or available a packet results in _connection refused_. The communication ends either with an error message or in a fallback. This can happen _before_ the `connectTimeout` if it is set very low. The time taken to perform a lookup and to receive such a packet causes a significant part of this delay. It is subject to change based on the remote host that involves a DNS lookup.
[[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
@ -412,13 +412,11 @@ You could configure it using configuration properties by doing the following @@ -412,13 +412,11 @@ You could configure it using configuration properties by doing the following
[source,yaml,indent=0]
----
spring:
cloud:
openfeign:
circuitbreaker:
enabled: true
alphanumeric-ids:
enabled: true
feign:
circuitbreaker:
enabled: true
alphanumeric-ids:
enabled: true
resilience4j:
circuitbreaker:
instances:
@ -508,7 +506,6 @@ If one needs access to the cause that made the fallback trigger, one can use the @@ -508,7 +506,6 @@ If one needs access to the cause that made the fallback trigger, one can use the
}
----
[[feign-and-primary]]
=== Feign and `@Primary`
When using Feign with Spring Cloud CircuitBreaker fallbacks, there are multiple beans in the `ApplicationContext` of the same type. This will cause `@Autowired` to not work because there isn't exactly one bean, or one marked as primary. To work around this, Spring Cloud OpenFeign marks all Feign instances as `@Primary`, so Spring Framework will know which bean to inject. In some cases, this may not be desirable. To turn off this behavior set the `primary` attribute of `@FeignClient` to false.
@ -559,7 +556,6 @@ public interface UserClient extends UserService { @@ -559,7 +556,6 @@ public interface UserClient extends UserService {
WARNING: `@FeignClient` interfaces should not be shared between server and client and annotating `@FeignClient` interfaces with `@RequestMapping` on class level is no longer supported.
[[feign-request/response-compression]]
=== Feign request/response compression
You may consider enabling the request or response GZIP compression for your
@ -582,12 +578,9 @@ spring.cloud.openfeign.compression.request.min-request-size=2048 @@ -582,12 +578,9 @@ spring.cloud.openfeign.compression.request.min-request-size=2048
These properties allow you to be selective about the compressed media types and minimum request threshold length.
TIP: Since the OkHttpClient uses "transparent" compression, that is disabled if the `content-encoding` or `accept-encoding` header is present, we do not enable compression when `feign.okhttp.OkHttpClient` is present on the classpath and `spring.cloud.openfeign.okhttp.enabled` is set to `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.
.application.yml
@ -616,11 +609,10 @@ public class FooConfiguration { @@ -616,11 +609,10 @@ public class FooConfiguration {
}
----
[[feign-capability-support]]
=== Feign Capability support
The Feign capabilities expose core Feign components so that these components can be modified. For example, the capabilities can take the `Client`, _decorate_ it, and give the decorated instance back to Feign.
The support for Micrometer is a good real-life example for this. See xref:spring-cloud-openfeign.adoc#micrometer-support[Micrometer Support].
The support for Micrometer is a good real-life example for this. See <<micrometer-support>>.
Creating one or more `Capability` beans and placing them in a `@FeignClient` configuration lets you register them and modify the behavior of the involved client.
@ -635,7 +627,6 @@ public class FooConfiguration { @@ -635,7 +627,6 @@ public class FooConfiguration {
}
----
[[micrometer-support]]
=== Micrometer Support
If all of the following conditions are true, a `MicrometerObservationCapability` bean is created and registered so that your Feign client is observable by Micrometer:
@ -684,7 +675,6 @@ public class FooConfiguration { @@ -684,7 +675,6 @@ public class FooConfiguration {
}
----
[[feign-caching]]
=== Feign Caching
If `@EnableCaching` annotation is used, a `CachingCapability` bean is created and registered so that your Feign client recognizes `@Cache*` annotations on its interface:
@ -701,7 +691,6 @@ public interface DemoClient { @@ -701,7 +691,6 @@ public interface DemoClient {
You can also disable the feature via property `spring.cloud.openfeign.cache.enabled=false`.
[[feign-querymap-support]]
=== Feign @QueryMap support
Spring Cloud OpenFeign provides an equivalent `@SpringQueryMap` annotation, which
@ -734,7 +723,6 @@ public interface DemoTemplate { @@ -734,7 +723,6 @@ public interface DemoTemplate {
If you need more control over the generated query parameter map, you can implement a custom `QueryMapEncoder` bean.
[[hateoas-support]]
=== 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].
@ -755,7 +743,6 @@ public interface DemoTemplate { @@ -755,7 +743,6 @@ public interface DemoTemplate {
}
----
[[spring-matrixvariable-support]]
=== Spring @MatrixVariable Support
Spring Cloud OpenFeign provides support for the Spring `@MatrixVariable` annotation.
@ -786,7 +773,6 @@ public interface DemoTemplate { @@ -786,7 +773,6 @@ public interface DemoTemplate {
}
----
[[feign-collectionformat-support]]
=== Feign `CollectionFormat` support
We support `feign.CollectionFormat` by providing the `@CollectionFormat` annotation.
You can annotate a Feign client method (or the whole class to affect all methods) with it by passing the desired `feign.CollectionFormat` as annotation value.
@ -796,22 +782,22 @@ In the following example, the `CSV` format is used instead of the default `EXPLO @@ -796,22 +782,22 @@ In the following example, the `CSV` format is used instead of the default `EXPLO
[source,java,indent=0]
----
@FeignClient(name = "demo")
protected interface DemoFeignClient {
protected interface PageableFeignClient {
@CollectionFormat(feign.CollectionFormat.CSV)
@GetMapping(path = "/test")
ResponseEntity performRequest(String test);
@GetMapping(path = "/page")
ResponseEntity performRequest(Pageable page);
}
----
[[reactive-support]]
TIP: Set the `CSV` format while sending `Pageable` as a query parameter in order for it to be encoded correctly.
=== Reactive Support
As the https://github.com/OpenFeign/feign[OpenFeign project] does not currently support reactive clients, such as https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.html[Spring WebClient], neither does Spring Cloud OpenFeign.We will add support for it here as soon as it becomes available in the core project.
Until that is done, we recommend using https://github.com/Playtika/feign-reactive[feign-reactive] for Spring WebClient support.
[[early-initialization-errors]]
==== Early Initialization Errors
Depending on how you are using your Feign clients you may see initialization errors when starting your application.
@ -823,7 +809,6 @@ To work around this problem you can use an `ObjectProvider` when autowiring your @@ -823,7 +809,6 @@ To work around this problem you can use an `ObjectProvider` when autowiring your
ObjectProvider<TestFeignClient> testFeignClient;
----
[[spring-data-support]]
=== Spring Data Support
If Jackson Databind and Spring Data Commons are on the classpath, converters for `org.springframework.data.domain.Page` and `org.springframework.data.domain.Sort` will be added automatically.
@ -836,13 +821,12 @@ spring.cloud.openfeign.autoconfiguration.jackson.enabled=false @@ -836,13 +821,12 @@ spring.cloud.openfeign.autoconfiguration.jackson.enabled=false
See `org.springframework.cloud.openfeign.FeignAutoConfiguration.FeignJacksonConfiguration` for details.
[[spring-refreshscope-support]]
=== Spring `@RefreshScope` Support
If Feign client refresh is enabled, each Feign client is created with:
* `feign.Request.Options` as a refresh-scoped bean. This means properties such as `connectTimeout` and `readTimeout` can be refreshed against any Feign client instance.
* A url wrapped under `org.springframework.cloud.openfeign.RefreshableUrl`. This means the URL of Feign client, if defined
with `spring.cloud.openfeign.client.config.\{feignName}.url` property, can be refreshed against any Feign client instance.
with `spring.cloud.openfeign.client.config.{feignName}.url` property, can be refreshed against any Feign client instance.
You can refresh these properties through `POST /actuator/refresh`.
@ -853,7 +837,6 @@ spring.cloud.openfeign.client.refresh-enabled=true @@ -853,7 +837,6 @@ spring.cloud.openfeign.client.refresh-enabled=true
----
TIP: DO NOT annotate the `@FeignClient` interface with the `@RefreshScope` annotation.
[[oauth2-support]]
=== OAuth2 Support
OAuth2 support can be enabled by setting following flag:
----
@ -866,7 +849,6 @@ TIP:: Using the `serviceId` as OAuth2 client registrationId is convenient for lo @@ -866,7 +849,6 @@ TIP:: Using the `serviceId` as OAuth2 client registrationId is convenient for lo
TIP:: If you do not want to use the default setup for the `OAuth2AuthorizedClientManager`, you can just instantiate a bean of this type in your configuration.
[[transform-the-load-balanced-http-request]]
=== Transform the load-balanced HTTP request
You can use the selected `ServiceInstance` to transform the load-balanced HTTP Request.
@ -894,7 +876,6 @@ For `Request`, you need to implement and define `LoadBalancerFeignRequestTransfo @@ -894,7 +876,6 @@ For `Request`, you need to implement and define `LoadBalancerFeignRequestTransfo
If multiple transformers are defined, they are applied in the order in which beans are defined.
Alternatively, you can use `LoadBalancerFeignRequestTransformer.DEFAULT_ORDER` to specify the order.
[[x-forwarded-headers-support]]
=== X-Forwarded Headers Support
`X-Forwarded-Host` and `X-Forwarded-Proto` support can be enabled by setting following flag:
@ -904,7 +885,6 @@ Alternatively, you can use `LoadBalancerFeignRequestTransformer.DEFAULT_ORDER` t @@ -904,7 +885,6 @@ Alternatively, you can use `LoadBalancerFeignRequestTransformer.DEFAULT_ORDER` t
spring.cloud.loadbalancer.x-forwarded.enabled=true
----
[[supported-ways-to-provide-url-to-a-feign-client]]
=== Supported Ways To Provide URL To A Feign Client
You can provide a URL to a Feign client in any of the following ways:
@ -934,10 +914,9 @@ The URL provided in the configuration properties remains unused. @@ -934,10 +914,9 @@ The URL provided in the configuration properties remains unused.
|===
[[aot-and-native-image-support]]
=== AOT and Native Image Support
Spring Cloud OpenFeign supports Spring AOT transformations and native images, however, only with refresh mode disabled, Feign clients refresh disabled (default setting) and xref:spring-cloud-openfeign.adoc#attribute-resolution-mode[lazy `@FeignClient` attribute resolution] disabled (default setting).
Spring Cloud OpenFeign supports Spring AOT transformations and native images, however, only with refresh mode disabled, Feign clients refresh disabled (default setting) and <<attribute-resolution-mode,lazy `@FeignClient` attribute resolution>> disabled (default setting).
WARNING: If you want to run Spring Cloud OpenFeign clients in AOT or native image modes, make sure to set `spring.cloud.refresh.enabled` to `false`.
@ -945,9 +924,7 @@ TIP: If you want to run Spring Cloud OpenFeign clients in AOT or native image mo @@ -945,9 +924,7 @@ TIP: If you want to run Spring Cloud OpenFeign clients in AOT or native image mo
TIP: If you want to run Spring Cloud OpenFeign clients in AOT or native image modes, ensure `spring.cloud.openfeign.lazy-attributes-resolution` has not been set to `true`.
TIP: However, if you set the `url` value via properties, it is possible to override the `@FeignClient` `url` value by running the image with `-Dspring.cloud.openfeign.client.config.[clientId].url=[url]` flag. In order to enable overriding, a `url` value also has to be set via properties and not `@FeignClient` attribute during buildtime.
[[configuration-properties]]
== Configuration properties
To see the list of all Spring Cloud OpenFeign related configuration properties please check link:appendix.html[the Appendix page].

303
mvnw vendored

@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
# "License"); 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
# 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
@ -19,7 +19,7 @@ @@ -19,7 +19,7 @@
# ----------------------------------------------------------------------------
# ----------------------------------------------------------------------------
# Apache Maven Wrapper startup batch script, version 3.2.0
# Maven2 Start Up Batch script
#
# Required ENV vars:
# ------------------
@ -27,6 +27,7 @@ @@ -27,6 +27,7 @@
#
# Optional ENV vars
# -----------------
# M2_HOME - location of maven2's installed home dir
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
# e.g. to debug Maven itself, use
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@ -35,10 +36,6 @@ @@ -35,10 +36,6 @@
if [ -z "$MAVEN_SKIP_RC" ] ; then
if [ -f /usr/local/etc/mavenrc ] ; then
. /usr/local/etc/mavenrc
fi
if [ -f /etc/mavenrc ] ; then
. /etc/mavenrc
fi
@ -53,56 +50,109 @@ fi @@ -53,56 +50,109 @@ fi
cygwin=false;
darwin=false;
mingw=false
case "$(uname)" in
case "`uname`" in
CYGWIN*) cygwin=true ;;
MINGW*) mingw=true;;
Darwin*) darwin=true
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html
if [ -z "$JAVA_HOME" ]; then
if [ -x "/usr/libexec/java_home" ]; then
JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME
else
JAVA_HOME="/Library/Java/Home"; export JAVA_HOME
fi
fi
;;
#
# Look for the Apple JDKs first to preserve the existing behaviour, and then look
# for the new JDKs provided by Oracle.
#
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
#
# Apple JDKs
#
export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
fi
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
#
# Apple JDKs
#
export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
fi
if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
#
# Oracle JDKs
#
export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
fi
if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
#
# Apple JDKs
#
export JAVA_HOME=`/usr/libexec/java_home`
fi
;;
esac
if [ -z "$JAVA_HOME" ] ; then
if [ -r /etc/gentoo-release ] ; then
JAVA_HOME=$(java-config --jre-home)
JAVA_HOME=`java-config --jre-home`
fi
fi
if [ -z "$M2_HOME" ] ; then
## resolve links - $0 may be a link to maven's home
PRG="$0"
# need this for relative symlinks
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG="`dirname "$PRG"`/$link"
fi
done
saveddir=`pwd`
M2_HOME=`dirname "$PRG"`/..
# make it fully qualified
M2_HOME=`cd "$M2_HOME" && pwd`
cd "$saveddir"
# echo Using m2 at $M2_HOME
fi
# For Cygwin, ensure paths are in UNIX format before anything is touched
if $cygwin ; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --unix "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
fi
# For Mingw, ensure paths are in UNIX format before anything is touched
# For Migwn, ensure paths are in UNIX format before anything is touched
if $mingw ; then
[ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] &&
JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)"
[ -n "$M2_HOME" ] &&
M2_HOME="`(cd "$M2_HOME"; pwd)`"
[ -n "$JAVA_HOME" ] &&
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
# TODO classpath?
fi
if [ -z "$JAVA_HOME" ]; then
javaExecutable="$(which javac)"
if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then
javaExecutable="`which javac`"
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
# readlink(1) is not available as standard on Solaris 10.
readLink=$(which readlink)
if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then
readLink=`which readlink`
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
if $darwin ; then
javaHome="$(dirname "\"$javaExecutable\"")"
javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac"
javaHome="`dirname \"$javaExecutable\"`"
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
else
javaExecutable="$(readlink -f "\"$javaExecutable\"")"
javaExecutable="`readlink -f \"$javaExecutable\"`"
fi
javaHome="$(dirname "\"$javaExecutable\"")"
javaHome=$(expr "$javaHome" : '\(.*\)/bin')
javaHome="`dirname \"$javaExecutable\"`"
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
JAVA_HOME="$javaHome"
export JAVA_HOME
fi
@ -118,7 +168,7 @@ if [ -z "$JAVACMD" ] ; then @@ -118,7 +168,7 @@ if [ -z "$JAVACMD" ] ; then
JAVACMD="$JAVA_HOME/bin/java"
fi
else
JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)"
JAVACMD="`which java`"
fi
fi
@ -132,177 +182,72 @@ if [ -z "$JAVA_HOME" ] ; then @@ -132,177 +182,72 @@ if [ -z "$JAVA_HOME" ] ; then
echo "Warning: JAVA_HOME environment variable is not set."
fi
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$M2_HOME" ] &&
M2_HOME=`cygpath --path --windows "$M2_HOME"`
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
[ -n "$CLASSPATH" ] &&
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
fi
# traverses directory structure from process work directory to filesystem root
# first directory with .mvn subdirectory is considered project base directory
find_maven_basedir() {
if [ -z "$1" ]
then
echo "Path not specified to find_maven_basedir"
return 1
fi
basedir="$1"
wdir="$1"
local basedir=$(pwd)
local wdir=$(pwd)
while [ "$wdir" != '/' ] ; do
if [ -d "$wdir"/.mvn ] ; then
basedir=$wdir
break
fi
# workaround for JBEAP-8937 (on Solaris 10/Sparc)
if [ -d "${wdir}" ]; then
wdir=$(cd "$wdir/.." || exit 1; pwd)
fi
# end of workaround
wdir=$(cd "$wdir/.."; pwd)
done
printf '%s' "$(cd "$basedir" || exit 1; pwd)"
echo "${basedir}"
}
# concatenates all lines of a file
concat_lines() {
if [ -f "$1" ]; then
# Remove \r in case we run on Windows within Git Bash
# and check out the repository with auto CRLF management
# enabled. Otherwise, we may read lines that are delimited with
# \r\n and produce $'-Xarg\r' rather than -Xarg due to word
# splitting rules.
tr -s '\r\n' ' ' < "$1"
echo "$(tr -s '\n' ' ' < "$1")"
fi
}
log() {
if [ "$MVNW_VERBOSE" = true ]; then
printf '%s\n' "$1"
fi
}
BASE_DIR=$(find_maven_basedir "$(dirname "$0")")
if [ -z "$BASE_DIR" ]; then
exit 1;
fi
MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR
log "$MAVEN_PROJECTBASEDIR"
##########################################################################################
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
# This allows using the maven wrapper in projects that prohibit checking in binary data.
##########################################################################################
wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar"
if [ -r "$wrapperJarPath" ]; then
log "Found $wrapperJarPath"
else
log "Couldn't find $wrapperJarPath, downloading it ..."
if [ -n "$MVNW_REPOURL" ]; then
wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
else
wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
fi
while IFS="=" read -r key value; do
# Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' )
safeValue=$(echo "$value" | tr -d '\r')
case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;;
esac
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
log "Downloading from: $wrapperUrl"
if $cygwin; then
wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
fi
if command -v wget > /dev/null; then
log "Found wget ... using wget"
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
else
wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath"
fi
elif command -v curl > /dev/null; then
log "Found curl ... using curl"
[ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent"
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
else
curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath"
fi
else
log "Falling back to using Java to download"
javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java"
javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class"
# For Cygwin, switch paths to Windows format before running javac
if $cygwin; then
javaSource=$(cygpath --path --windows "$javaSource")
javaClass=$(cygpath --path --windows "$javaClass")
fi
if [ -e "$javaSource" ]; then
if [ ! -e "$javaClass" ]; then
log " - Compiling MavenWrapperDownloader.java ..."
("$JAVA_HOME/bin/javac" "$javaSource")
fi
if [ -e "$javaClass" ]; then
log " - Running MavenWrapperDownloader.java ..."
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath"
fi
fi
fi
fi
##########################################################################################
# End of extension
##########################################################################################
# If specified, validate the SHA-256 sum of the Maven wrapper jar file
wrapperSha256Sum=""
while IFS="=" read -r key value; do
case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;;
esac
done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties"
if [ -n "$wrapperSha256Sum" ]; then
wrapperSha256Result=false
if command -v sha256sum > /dev/null; then
if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then
wrapperSha256Result=true
fi
elif command -v shasum > /dev/null; then
if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then
wrapperSha256Result=true
fi
else
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available."
echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties."
exit 1
fi
if [ $wrapperSha256Result = false ]; then
echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2
echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2
echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2
exit 1
fi
fi
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)}
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
# For Cygwin, switch paths to Windows format before running java
if $cygwin; then
[ -n "$JAVA_HOME" ] &&
JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
[ -n "$CLASSPATH" ] &&
CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
[ -n "$MAVEN_PROJECTBASEDIR" ] &&
MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
fi
# Provide a "standardized" way to retrieve the CLI args that will
# work with both Windows and non-Windows executions.
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*"
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
export MAVEN_CMD_LINE_ARGS
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
# shellcheck disable=SC2086 # safe args
echo "Running version check"
VERSION=$( sed '\!<parent!,\!</parent!d' `dirname $0`/pom.xml | grep '<version' | head -1 | sed -e 's/.*<version>//' -e 's!</version>.*$!!' )
echo "The found version is [${VERSION}]"
if echo $VERSION | egrep -q 'M|RC'; then
echo Activating \"milestone\" profile for version=\"$VERSION\"
echo $MAVEN_ARGS | grep -q milestone || MAVEN_ARGS="$MAVEN_ARGS -Pmilestone"
else
echo Deactivating \"milestone\" profile for version=\"$VERSION\"
echo $MAVEN_ARGS | grep -q milestone && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pmilestone//')
fi
if echo $VERSION | egrep -q 'RELEASE'; then
echo Activating \"central\" profile for version=\"$VERSION\"
echo $MAVEN_ARGS | grep -q milestone || MAVEN_ARGS="$MAVEN_ARGS -Pcentral"
else
echo Deactivating \"central\" profile for version=\"$VERSION\"
echo $MAVEN_ARGS | grep -q central && MAVEN_ARGS=$(echo $MAVEN_ARGS | sed -e 's/-Pcentral//')
fi
exec "$JAVACMD" \
$MAVEN_OPTS \
$MAVEN_DEBUG_OPTS \
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
${WRAPPER_LAUNCHER} ${MAVEN_ARGS} "$@"

96
mvnw.cmd vendored

@ -7,7 +7,7 @@ @@ -7,7 +7,7 @@
@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
@REM http://www.apache.org/licenses/LICENSE-2.0
@REM https://www.apache.org/licenses/LICENSE-2.0
@REM
@REM Unless required by applicable law or agreed to in writing,
@REM software distributed under the License is distributed on an
@ -18,14 +18,15 @@ @@ -18,14 +18,15 @@
@REM ----------------------------------------------------------------------------
@REM ----------------------------------------------------------------------------
@REM Apache Maven Wrapper startup batch script, version 3.2.0
@REM Maven2 Start Up Batch script
@REM
@REM Required ENV vars:
@REM JAVA_HOME - location of a JDK home dir
@REM
@REM Optional ENV vars
@REM M2_HOME - location of maven2's installed home dir
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
@REM e.g. to debug Maven itself, use
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
@ -34,9 +35,7 @@ @@ -34,9 +35,7 @@
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
@echo off
@REM set title of command window
title %0
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
@REM set %HOME% to equivalent of $HOME
@ -45,8 +44,8 @@ if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") @@ -45,8 +44,8 @@ if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
@REM Execute a user defined script before this one
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %*
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %*
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
:skipRcPre
@setlocal
@ -81,6 +80,8 @@ goto error @@ -81,6 +80,8 @@ goto error
:init
set MAVEN_CMD_LINE_ARGS=%*
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
@REM Fallback to current working directory if not found.
@ -116,72 +117,11 @@ for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do s @@ -116,72 +117,11 @@ for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do s
:endReadAdditionalConfig
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B
)
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
if exist %WRAPPER_JAR% (
if "%MVNW_VERBOSE%" == "true" (
echo Found %WRAPPER_JAR%
)
) else (
if not "%MVNW_REPOURL%" == "" (
SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar"
)
if "%MVNW_VERBOSE%" == "true" (
echo Couldn't find %WRAPPER_JAR%, downloading it ...
echo Downloading from: %WRAPPER_URL%
)
powershell -Command "&{"^
"$webclient = new-object System.Net.WebClient;"^
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
"}"^
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^
"}"
if "%MVNW_VERBOSE%" == "true" (
echo Finished downloading %WRAPPER_JAR%
)
)
@REM End of extension
@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file
SET WRAPPER_SHA_256_SUM=""
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B
)
IF NOT %WRAPPER_SHA_256_SUM%=="" (
powershell -Command "&{"^
"$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^
"If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^
" Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^
" Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^
" Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^
" exit 1;"^
"}"^
"}"
if ERRORLEVEL 1 goto error
)
@REM Provide a "standardized" way to retrieve the CLI args that will
@REM work with both Windows and non-Windows executions.
set MAVEN_CMD_LINE_ARGS=%*
set WRAPPER_JAR="".\.mvn\wrapper\maven-wrapper.jar""
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
%MAVEN_JAVA_EXE% ^
%JVM_CONFIG_MAVEN_PROPS% ^
%MAVEN_OPTS% ^
%MAVEN_DEBUG_OPTS% ^
-classpath %WRAPPER_JAR% ^
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
if ERRORLEVEL 1 goto error
goto end
@ -191,15 +131,15 @@ set ERROR_CODE=1 @@ -191,15 +131,15 @@ set ERROR_CODE=1
:end
@endlocal & set ERROR_CODE=%ERROR_CODE%
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
@REM check for post script, once with legacy .bat ending and once with .cmd ending
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat"
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd"
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
:skipRcPost
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
if "%MAVEN_BATCH_PAUSE%"=="on" pause
if "%MAVEN_BATCH_PAUSE%" == "on" pause
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE%
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
cmd /C exit /B %ERROR_CODE%
exit /B %ERROR_CODE%

8
pom.xml

@ -4,14 +4,14 @@ @@ -4,14 +4,14 @@
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>
<artifactId>spring-cloud-openfeign</artifactId>
<version>4.1.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>Spring Cloud OpenFeign</name>
<description>Spring Cloud OpenFeign</description>
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-build</artifactId>
<version>4.1.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<relativePath/>
</parent>
<scm>
@ -25,8 +25,8 @@ @@ -25,8 +25,8 @@
</scm>
<properties>
<main.basedir>${basedir}</main.basedir>
<jackson.version>2.15.2</jackson.version>
<spring-cloud-commons.version>4.1.0-SNAPSHOT</spring-cloud-commons.version>
<jackson.version>2.11.3</jackson.version>
<spring-cloud-commons.version>4.0.0-SNAPSHOT</spring-cloud-commons.version>
<!-- Plugin versions -->
<maven-eclipse-plugin.version>2.10</maven-eclipse-plugin.version>

25
spring-cloud-openfeign-core/pom.xml

@ -6,12 +6,9 @@ @@ -6,12 +6,9 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId>
<version>4.1.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<relativePath>..</relativePath> <!-- lookup parent from repository -->
</parent>
<scm>
<url>https://github.com/spring-cloud/spring-cloud-openfeign</url>
</scm>
<artifactId>spring-cloud-openfeign-core</artifactId>
<packaging>jar</packaging>
<name>Spring Cloud OpenFeign Core</name>
@ -103,16 +100,8 @@ @@ -103,16 +100,8 @@
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</exclusion>
<exclusion>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-slf4j</artifactId>
@ -133,11 +122,6 @@ @@ -133,11 +122,6 @@
<artifactId>feign-okhttp</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-java11</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
@ -204,7 +188,7 @@ @@ -204,7 +188,7 @@
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.13.0</version>
<version>2.11.0</version>
<scope>test</scope>
</dependency>
<dependency>
@ -212,11 +196,6 @@ @@ -212,11 +196,6 @@
<artifactId>spring-core-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-oauth2-client</artifactId>

31
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2013-2023 the original author or authors.
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,7 +17,6 @@ @@ -17,7 +17,6 @@
package org.springframework.cloud.openfeign;
import java.lang.reflect.Method;
import java.net.http.HttpClient;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
@ -25,7 +24,6 @@ import java.time.Duration; @@ -25,7 +24,6 @@ import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
@ -40,11 +38,9 @@ import feign.Client; @@ -40,11 +38,9 @@ import feign.Client;
import feign.Feign;
import feign.Target;
import feign.hc5.ApacheHttp5Client;
import feign.http2client.Http2Client;
import feign.okhttp.OkHttpClient;
import jakarta.annotation.PreDestroy;
import okhttp3.ConnectionPool;
import okhttp3.Protocol;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -96,7 +92,6 @@ import org.springframework.util.ClassUtils; @@ -96,7 +92,6 @@ import org.springframework.util.ClassUtils;
* @author Sam Kruglov
* @author Wojciech Mąka
* @author Dangzhicairang(小水牛)
* @author changjin wei(魏昌进)
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Feign.class)
@ -264,14 +259,11 @@ public class FeignAutoConfiguration { @@ -264,14 +259,11 @@ public class FeignAutoConfiguration {
int connectTimeout = httpClientProperties.getConnectionTimeout();
boolean disableSslValidation = httpClientProperties.isDisableSslValidation();
Duration readTimeout = httpClientProperties.getOkHttp().getReadTimeout();
List<Protocol> protocols = httpClientProperties.getOkHttp().getProtocols().stream().map(Protocol::valueOf)
.collect(Collectors.toList());
if (disableSslValidation) {
disableSsl(builder);
}
this.okHttpClient = builder.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
.followRedirects(followRedirects).readTimeout(readTimeout).connectionPool(connectionPool)
.protocols(protocols).build();
.followRedirects(followRedirects).readTimeout(readTimeout).connectionPool(connectionPool).build();
return this.okHttpClient;
}
@ -385,25 +377,6 @@ public class FeignAutoConfiguration { @@ -385,25 +377,6 @@ public class FeignAutoConfiguration {
}
// the following configuration is for alternate feign clients if
// SC loadbalancer is not on the class path.
// see corresponding configurations in FeignLoadBalancerAutoConfiguration
// for load-balanced clients.
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Http2Client.class, HttpClient.class })
@ConditionalOnMissingBean(HttpClient.class)
@ConditionalOnProperty("spring.cloud.openfeign.http2client.enabled")
@Import(org.springframework.cloud.openfeign.clientconfig.Http2ClientFeignConfiguration.class)
protected static class Http2ClientFeignConfiguration {
@Bean
@ConditionalOnMissingBean(Client.class)
public Client feignClient(HttpClient httpClient) {
return new Http2Client(httpClient);
}
}
}
class FeignHints implements RuntimeHintsRegistrar {

14
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerInvocationHandler.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2013-2023 the original author or authors.
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -154,12 +154,12 @@ class FeignCircuitBreakerInvocationHandler implements InvocationHandler { @@ -154,12 +154,12 @@ class FeignCircuitBreakerInvocationHandler implements InvocationHandler {
}
/**
* If the method param of {@link InvocationHandler#invoke(Object, Method, Object[])}
* is not accessible, i.e in a package-private interface, the fallback call will cause
* of access restrictions. But methods in dispatch are copied methods. So setting
* access to dispatch method doesn't take effect to the method in
* InvocationHandler.invoke. Use map to store a copy of method to invoke the fallback
* to bypass this and reducing the count of reflection calls.
* If the method param of InvocationHandler.invoke is not accessible, i.e in a
* package-private interface, the fallback call will cause of access restrictions. But
* methods in dispatch are copied methods. So setting access to dispatch method
* doesn't take effect to the method in InvocationHandler.invoke. Use map to store a
* copy of method to invoke the fallback to bypass this and reducing the count of
* reflection calls.
* @return cached methods map for fallback invoking
*/
static Map<Method, Method> toFallbackMethod(Map<Method, InvocationHandlerFactory.MethodHandler> dispatch) {

29
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignCircuitBreakerTargeter.java

@ -19,13 +19,9 @@ package org.springframework.cloud.openfeign; @@ -19,13 +19,9 @@ package org.springframework.cloud.openfeign;
import feign.Feign;
import feign.Target;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.util.StringUtils;
/**
* @author 黄学敏huangxuemin)
*/
@SuppressWarnings("unchecked")
class FeignCircuitBreakerTargeter implements Targeter {
@ -82,27 +78,10 @@ class FeignCircuitBreakerTargeter implements Targeter { @@ -82,27 +78,10 @@ class FeignCircuitBreakerTargeter implements Targeter {
beanType, feignClientName));
}
if (fallbackInstance instanceof FactoryBean<?> factoryBean) {
try {
fallbackInstance = factoryBean.getObject();
}
catch (Exception e) {
throw new IllegalStateException(fallbackMechanism + " create fail", e);
}
if (!targetType.isAssignableFrom(fallbackInstance.getClass())) {
throw new IllegalStateException(String.format("Incompatible " + fallbackMechanism
+ " instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
fallbackInstance.getClass(), targetType, feignClientName));
}
}
else {
if (!targetType.isAssignableFrom(beanType)) {
throw new IllegalStateException(String.format("Incompatible " + fallbackMechanism
+ " instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
beanType, targetType, feignClientName));
}
if (!targetType.isAssignableFrom(beanType)) {
throw new IllegalStateException(String.format("Incompatible " + fallbackMechanism
+ " instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s",
beanType, targetType, feignClientName));
}
return (T) fallbackInstance;
}

14
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2013-2023 the original author or authors.
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,7 +32,6 @@ import feign.Logger; @@ -32,7 +32,6 @@ import feign.Logger;
import feign.QueryMapEncoder;
import feign.Request;
import feign.RequestInterceptor;
import feign.ResponseInterceptor;
import feign.Retryer;
import feign.Target.HardCodedTarget;
import feign.codec.Decoder;
@ -71,7 +70,6 @@ import org.springframework.util.StringUtils; @@ -71,7 +70,6 @@ import org.springframework.util.StringUtils;
* @author Jasbir Singh
* @author Hyeonmin Park
* @author Felix Dittrich
* @author Dominique Villard
*/
public class FeignClientFactoryBean
implements FactoryBean<Object>, InitializingBean, ApplicationContextAware, BeanFactoryAware {
@ -221,10 +219,6 @@ public class FeignClientFactoryBean @@ -221,10 +219,6 @@ public class FeignClientFactoryBean
AnnotationAwareOrderComparator.sort(interceptors);
builder.requestInterceptors(interceptors);
}
ResponseInterceptor responseInterceptor = getInheritedAwareOptional(context, ResponseInterceptor.class);
if (responseInterceptor != null) {
builder.responseInterceptor(responseInterceptor);
}
QueryMapEncoder queryMapEncoder = getInheritedAwareOptional(context, QueryMapEncoder.class);
if (queryMapEncoder != null) {
builder.queryMapEncoder(queryMapEncoder);
@ -283,10 +277,6 @@ public class FeignClientFactoryBean @@ -283,10 +277,6 @@ public class FeignClientFactoryBean
}
}
if (config.getResponseInterceptor() != null) {
builder.responseInterceptor(getOrInstantiate(config.getResponseInterceptor()));
}
if (config.getDismiss404() != null) {
if (config.getDismiss404()) {
builder.dismiss404();
@ -505,7 +495,7 @@ public class FeignClientFactoryBean @@ -505,7 +495,7 @@ public class FeignClientFactoryBean
"Provide Feign client URL either in @FeignClient() or in config properties.");
}
return new PropertyBasedTarget(type, name, config);
return new HardCodedTarget(type, name, FeignClientsRegistrar.getUrl(config.getUrl()));
}
private boolean isUrlAvailableInConfig(String contextId) {

18
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientProperties.java

@ -28,7 +28,6 @@ import feign.ExceptionPropagationPolicy; @@ -28,7 +28,6 @@ import feign.ExceptionPropagationPolicy;
import feign.Logger;
import feign.QueryMapEncoder;
import feign.RequestInterceptor;
import feign.ResponseInterceptor;
import feign.Retryer;
import feign.codec.Decoder;
import feign.codec.Encoder;
@ -44,7 +43,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @@ -44,7 +43,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Olga Maciaszek-Sharma
* @author Hyeonmin Park
* @author Jasbir Singh
* @author Dominique Villard
*/
@ConfigurationProperties("spring.cloud.openfeign.client")
public class FeignClientProperties {
@ -128,8 +126,6 @@ public class FeignClientProperties { @@ -128,8 +126,6 @@ public class FeignClientProperties {
private List<Class<RequestInterceptor>> requestInterceptors;
private Class<ResponseInterceptor> responseInterceptor;
private Map<String, Collection<String>> defaultRequestHeaders;
private Map<String, Collection<String>> defaultQueryParameters;
@ -206,14 +202,6 @@ public class FeignClientProperties { @@ -206,14 +202,6 @@ public class FeignClientProperties {
this.requestInterceptors = requestInterceptors;
}
public Class<ResponseInterceptor> getResponseInterceptor() {
return responseInterceptor;
}
public void setResponseInterceptor(Class<ResponseInterceptor> responseInterceptor) {
this.responseInterceptor = responseInterceptor;
}
public Map<String, Collection<String>> getDefaultRequestHeaders() {
return defaultRequestHeaders;
}
@ -323,7 +311,6 @@ public class FeignClientProperties { @@ -323,7 +311,6 @@ public class FeignClientProperties {
&& Objects.equals(readTimeout, that.readTimeout) && Objects.equals(retryer, that.retryer)
&& Objects.equals(errorDecoder, that.errorDecoder)
&& Objects.equals(requestInterceptors, that.requestInterceptors)
&& Objects.equals(responseInterceptor, that.responseInterceptor)
&& Objects.equals(dismiss404, that.dismiss404) && Objects.equals(encoder, that.encoder)
&& Objects.equals(decoder, that.decoder) && Objects.equals(contract, that.contract)
&& Objects.equals(exceptionPropagationPolicy, that.exceptionPropagationPolicy)
@ -338,9 +325,8 @@ public class FeignClientProperties { @@ -338,9 +325,8 @@ public class FeignClientProperties {
@Override
public int hashCode() {
return Objects.hash(loggerLevel, connectTimeout, readTimeout, retryer, errorDecoder, requestInterceptors,
responseInterceptor, dismiss404, encoder, decoder, contract, exceptionPropagationPolicy,
defaultQueryParameters, defaultRequestHeaders, capabilities, queryMapEncoder, micrometer,
followRedirects, url);
dismiss404, encoder, decoder, contract, exceptionPropagationPolicy, defaultQueryParameters,
defaultRequestHeaders, capabilities, queryMapEncoder, micrometer, followRedirects, url);
}
}

3
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsRegistrar.java

@ -32,6 +32,7 @@ import java.util.Set; @@ -32,6 +32,7 @@ import java.util.Set;
import feign.Request;
import org.springframework.aop.scope.ScopedProxyUtils;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.annotation.AnnotatedGenericBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
@ -247,6 +248,7 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLo @@ -247,6 +248,7 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLo
// code
definition.addPropertyValue("qualifiers", qualifiers);
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
// has a default, won't be null
boolean primary = (Boolean) attributes.get("primary");
beanDefinition.setPrimary(primary);
@ -290,6 +292,7 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLo @@ -290,6 +292,7 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, ResourceLo
validate(attributes);
AbstractBeanDefinition beanDefinition = definition.getBeanDefinition();
beanDefinition.setAttribute(FactoryBean.OBJECT_TYPE_ATTRIBUTE, className);
beanDefinition.setAttribute("feignClientsRegistrarFactoryBean", factoryBean);
// has a default, won't be null

49
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/PropertyBasedTarget.java

@ -1,49 +0,0 @@ @@ -1,49 +0,0 @@
/*
* Copyright 2013-2023 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.Target;
/**
* A {@link HardCodedTarget} implementation that resolves url from properties when the
* initial call is made. Using it allows specifying the url at runtime in an AOT-packaged
* application or a native image by setting the value of the
* `spring.cloud.openfeign.client.config.[clientId].url`.
*
* @author Olga Maciaszek-Sharma
* @see FeignClientProperties.FeignClientConfiguration#getUrl()
*/
public class PropertyBasedTarget<T> extends Target.HardCodedTarget<T> {
private String url;
private final FeignClientProperties.FeignClientConfiguration config;
public PropertyBasedTarget(Class<T> type, String name, FeignClientProperties.FeignClientConfiguration config) {
super(type, name, config.getUrl());
this.config = config;
}
@Override
public String url() {
if (url == null) {
url = config.getUrl();
}
return url;
}
}

47
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/aot/FeignClientBeanFactoryInitializationAotProcessor.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2022-2023 the original author or authors.
* Copyright 2022-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,8 +16,6 @@ @@ -16,8 +16,6 @@
package org.springframework.cloud.openfeign.aot;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@ -27,9 +25,7 @@ import javax.lang.model.element.Modifier; @@ -27,9 +25,7 @@ import javax.lang.model.element.Modifier;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.generate.MethodReference;
import org.springframework.aot.hint.BindingReflectionHintsRegistrar;
import org.springframework.aot.hint.ReflectionHints;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.ProxyHints;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.FactoryBean;
@ -51,7 +47,6 @@ import org.springframework.cloud.openfeign.FeignClientFactory; @@ -51,7 +47,6 @@ import org.springframework.cloud.openfeign.FeignClientFactory;
import org.springframework.cloud.openfeign.FeignClientFactoryBean;
import org.springframework.cloud.openfeign.FeignClientSpecification;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.MethodParameter;
import org.springframework.javapoet.MethodSpec;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@ -59,7 +54,7 @@ import org.springframework.util.ClassUtils; @@ -59,7 +54,7 @@ import org.springframework.util.ClassUtils;
/**
* A {@link BeanFactoryInitializationAotProcessor} that creates an
* {@link BeanFactoryInitializationAotContribution} that registers bean definitions and
* proxy and reflection hints for Feign client beans.
* proxy hints for Feign client beans.
*
* @author Olga Maciaszek-Sharma
* @since 4.0.0
@ -71,8 +66,6 @@ public class FeignClientBeanFactoryInitializationAotProcessor @@ -71,8 +66,6 @@ public class FeignClientBeanFactoryInitializationAotProcessor
private final Map<String, BeanDefinition> feignClientBeanDefinitions;
private final BindingReflectionHintsRegistrar bindingRegistrar = new BindingReflectionHintsRegistrar();
public FeignClientBeanFactoryInitializationAotProcessor(GenericApplicationContext context,
FeignClientFactory feignClientFactory) {
this.context = context;
@ -93,7 +86,6 @@ public class FeignClientBeanFactoryInitializationAotProcessor @@ -93,7 +86,6 @@ public class FeignClientBeanFactoryInitializationAotProcessor
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
}
@SuppressWarnings("NullableProblems")
@Override
public BeanFactoryInitializationAotContribution processAheadOfTime(ConfigurableListableBeanFactory beanFactory) {
BeanFactory applicationBeanFactory = context.getBeanFactory();
@ -103,26 +95,7 @@ public class FeignClientBeanFactoryInitializationAotProcessor @@ -103,26 +95,7 @@ public class FeignClientBeanFactoryInitializationAotProcessor
return new AotContribution(feignClientBeanDefinitions);
}
private void registerMethodHints(ReflectionHints hints, Class<?> clazz) {
for (Method method : clazz.getDeclaredMethods()) {
registerMethodHints(hints, method);
}
}
private void registerMethodHints(ReflectionHints hints, Method method) {
for (Parameter parameter : method.getParameters()) {
bindingRegistrar.registerReflectionHints(hints,
MethodParameter.forParameter(parameter).getGenericParameterType());
}
MethodParameter returnTypeParameter = MethodParameter.forExecutable(method, -1);
if (!void.class.equals(returnTypeParameter.getParameterType())) {
bindingRegistrar.registerReflectionHints(hints, returnTypeParameter.getGenericParameterType());
}
}
// Visible for tests
final class AotContribution implements BeanFactoryInitializationAotContribution {
private static final class AotContribution implements BeanFactoryInitializationAotContribution {
private final Map<String, BeanDefinition> feignClientBeanDefinitions;
@ -133,7 +106,7 @@ public class FeignClientBeanFactoryInitializationAotProcessor @@ -133,7 +106,7 @@ public class FeignClientBeanFactoryInitializationAotProcessor
@Override
public void applyTo(GenerationContext generationContext,
BeanFactoryInitializationCode beanFactoryInitializationCode) {
RuntimeHints hints = generationContext.getRuntimeHints();
ProxyHints proxyHints = generationContext.getRuntimeHints().proxies();
Set<String> feignClientRegistrationMethods = feignClientBeanDefinitions.values().stream()
.map(beanDefinition -> {
Assert.notNull(beanDefinition, "beanDefinition cannot be null");
@ -142,9 +115,8 @@ public class FeignClientBeanFactoryInitializationAotProcessor @@ -142,9 +115,8 @@ public class FeignClientBeanFactoryInitializationAotProcessor
MutablePropertyValues feignClientProperties = registeredBeanDefinition.getPropertyValues();
String className = (String) feignClientProperties.get("type");
Assert.notNull(className, "className cannot be null");
Class<?> clazz = ClassUtils.resolveClassName(className, null);
hints.proxies().registerJdkProxy(clazz);
registerMethodHints(hints.reflection(), clazz);
Class clazz = ClassUtils.resolveClassName(className, null);
proxyHints.registerJdkProxy(clazz);
return beanFactoryInitializationCode.getMethods()
.add(buildMethodName(className), method -> generateFeignClientRegistrationMethod(method,
feignClientProperties, registeredBeanDefinition))
@ -205,11 +177,6 @@ public class FeignClientBeanFactoryInitializationAotProcessor @@ -205,11 +177,6 @@ public class FeignClientBeanFactoryInitializationAotProcessor
.addStatement("$T.registerBeanDefinition(holder, registry) ", BeanDefinitionReaderUtils.class);
}
// Visible for tests
Map<String, BeanDefinition> getFeignClientBeanDefinitions() {
return feignClientBeanDefinitions;
}
}
}

45
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/clientconfig/Http2ClientFeignConfiguration.java

@ -1,45 +0,0 @@ @@ -1,45 +0,0 @@
/*
* Copyright 2013-2023 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.net.http.HttpClient;
import java.time.Duration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.openfeign.support.FeignHttpClientProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Default configuration for {@link HttpClient}.
*
* @author changjin wei(魏昌进)
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(HttpClient.class)
public class Http2ClientFeignConfiguration {
@Bean
public HttpClient httpClient(FeignHttpClientProperties httpClientProperties) {
return HttpClient.newBuilder()
.followRedirects(httpClientProperties.isFollowRedirects() ? HttpClient.Redirect.ALWAYS
: HttpClient.Redirect.NEVER)
.version(HttpClient.Version.valueOf(httpClientProperties.getHttp2().getVersion()))
.connectTimeout(Duration.ofMillis(httpClientProperties.getConnectionTimeout())).build();
}
}

9
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/clientconfig/HttpClient5FeignConfiguration.java

@ -35,7 +35,6 @@ import org.apache.hc.client5.http.impl.classic.HttpClients; @@ -35,7 +35,6 @@ import org.apache.hc.client5.http.impl.classic.HttpClients;
import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManagerBuilder;
import org.apache.hc.client5.http.io.HttpClientConnectionManager;
import org.apache.hc.client5.http.socket.LayeredConnectionSocketFactory;
import org.apache.hc.client5.http.ssl.NoopHostnameVerifier;
import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactoryBuilder;
import org.apache.hc.core5.http.io.SocketConfig;
import org.apache.hc.core5.http.ssl.TLS;
@ -55,7 +54,6 @@ import org.springframework.context.annotation.Configuration; @@ -55,7 +54,6 @@ import org.springframework.context.annotation.Configuration;
* Default configuration for {@link CloseableHttpClient}.
*
* @author Nguyen Ky Thanh
* @author changjin wei(魏昌进)
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(CloseableHttpClient.class)
@ -91,11 +89,7 @@ public class HttpClient5FeignConfiguration { @@ -91,11 +89,7 @@ public class HttpClient5FeignConfiguration {
.setDefaultRequestConfig(RequestConfig.custom()
.setConnectTimeout(
Timeout.of(httpClientProperties.getConnectionTimeout(), TimeUnit.MILLISECONDS))
.setRedirectsEnabled(httpClientProperties.isFollowRedirects())
.setConnectionRequestTimeout(
Timeout.of(httpClientProperties.getHc5().getConnectionRequestTimeout(),
httpClientProperties.getHc5().getConnectionRequestTimeoutUnit()))
.build())
.setRedirectsEnabled(httpClientProperties.isFollowRedirects()).build())
.build();
return httpClient5;
}
@ -116,7 +110,6 @@ public class HttpClient5FeignConfiguration { @@ -116,7 +110,6 @@ public class HttpClient5FeignConfiguration {
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[] { new DisabledValidationTrustManager() }, new SecureRandom());
sslConnectionSocketFactoryBuilder.setSslContext(sslContext);
sslConnectionSocketFactoryBuilder.setHostnameVerifier(NoopHostnameVerifier.INSTANCE);
}
catch (NoSuchAlgorithmException | KeyManagementException e) {
LOG.warn("Error creating SSLContext", e);

9
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignAcceptGzipEncodingAutoConfiguration.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2013-2023 the original author or authors.
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,18 +22,17 @@ import feign.Feign; @@ -22,18 +22,17 @@ import feign.Feign;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
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.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
/**
* Configures the Feign response compression.
*
* @author Jakub Narloch
* @author Olga Maciaszek-Sharma
* @see FeignAcceptGzipEncodingInterceptor
*/
@Configuration(proxyBeanMethods = false)
@ -42,8 +41,8 @@ import org.springframework.context.annotation.Configuration; @@ -42,8 +41,8 @@ import org.springframework.context.annotation.Configuration;
@ConditionalOnBean(Client.class)
@ConditionalOnProperty("spring.cloud.openfeign.compression.response.enabled")
// The OK HTTP client uses "transparent" compression.
// If the accept-encoding header is present, it disables transparent compression.
@Conditional(OkHttpFeignClientBeanMissingCondition.class)
// If the accept-encoding header is present it disable transparent compression
@ConditionalOnMissingBean(type = "okhttp3.OkHttpClient")
@AutoConfigureAfter(FeignAutoConfiguration.class)
public class FeignAcceptGzipEncodingAutoConfiguration {

9
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingAutoConfiguration.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2013-2023 the original author or authors.
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,26 +20,25 @@ import feign.Feign; @@ -20,26 +20,25 @@ import feign.Feign;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
/**
* Configures the Feign request compression.
*
* @author Jakub Narloch
* @author Olga Maciaszek-Sharma
* @see FeignContentGzipEncodingInterceptor
*/
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(FeignClientEncodingProperties.class)
@ConditionalOnClass(Feign.class)
// The OK HTTP client uses "transparent" compression.
// If the content-encoding header is present, it disables transparent compression.
@Conditional(OkHttpFeignClientBeanMissingCondition.class)
// If the content-encoding header is present it disable transparent compression
@ConditionalOnMissingBean(type = "okhttp3.OkHttpClient")
@ConditionalOnProperty("spring.cloud.openfeign.compression.request.enabled")
@AutoConfigureAfter(FeignAutoConfiguration.class)
public class FeignContentGzipEncodingAutoConfiguration {

51
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/OkHttpFeignClientBeanMissingCondition.java

@ -1,51 +0,0 @@ @@ -1,51 +0,0 @@
/*
* Copyright 2013-2023 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.encoding;
import feign.Client;
import feign.okhttp.OkHttpClient;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Condition;
/**
* A {@link Condition} that verifies whether the conditions for creating Feign
* {@link Client} beans that either are of type {@link OkHttpClient} or have a delegate of
* type {@link OkHttpClient} are not met.
*
* @author Olga Maciaszek-Sharma
* @since 4.0.2
*/
public class OkHttpFeignClientBeanMissingCondition extends AnyNestedCondition {
public OkHttpFeignClientBeanMissingCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnMissingClass("feign.okhttp.OkHttpClient")
static class FeignOkHttpClientPresent {
}
@ConditionalOnProperty(value = "spring.cloud.openfeign.okhttp.enabled", havingValue = "false")
static class FeignOkHttpClientEnabled {
}
}

4
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/FeignLoadBalancerAutoConfiguration.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2013-2023 the original author or authors.
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -54,7 +54,7 @@ import org.springframework.context.annotation.Import; @@ -54,7 +54,7 @@ import org.springframework.context.annotation.Import;
// see
// https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({ OkHttpFeignLoadBalancerConfiguration.class, HttpClient5FeignLoadBalancerConfiguration.class,
Http2ClientFeignLoadBalancerConfiguration.class, DefaultFeignLoadBalancerConfiguration.class })
DefaultFeignLoadBalancerConfiguration.class })
public class FeignLoadBalancerAutoConfiguration {
@Bean

76
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/Http2ClientFeignLoadBalancerConfiguration.java

@ -1,76 +0,0 @@ @@ -1,76 +0,0 @@
/*
* Copyright 2013-2023 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.net.http.HttpClient;
import java.util.List;
import feign.Client;
import feign.http2client.Http2Client;
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.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClientsProperties;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
/**
* Configuration instantiating a {@link LoadBalancerClient}-based {@link Client} object
* that uses {@link Http2Client} under the hood.
*
* @author changjin wei(魏昌进)
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Http2Client.class, HttpClient.class })
@ConditionalOnBean({ LoadBalancerClient.class, LoadBalancerClientFactory.class })
@ConditionalOnProperty("spring.cloud.openfeign.http2client.enabled")
@EnableConfigurationProperties(LoadBalancerClientsProperties.class)
class Http2ClientFeignLoadBalancerConfiguration {
@Bean
@ConditionalOnMissingBean
@Conditional(OnRetryNotEnabledCondition.class)
public Client feignClient(LoadBalancerClient loadBalancerClient, HttpClient httpClient,
LoadBalancerClientFactory loadBalancerClientFactory,
List<LoadBalancerFeignRequestTransformer> transformers) {
Client delegate = new Http2Client(httpClient);
return new FeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancerClientFactory,
transformers);
}
@Bean
@ConditionalOnMissingBean
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
@ConditionalOnBean(LoadBalancedRetryFactory.class)
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "true",
matchIfMissing = true)
public Client feignRetryClient(LoadBalancerClient loadBalancerClient, HttpClient httpClient,
LoadBalancedRetryFactory loadBalancedRetryFactory, LoadBalancerClientFactory loadBalancerClientFactory,
List<LoadBalancerFeignRequestTransformer> transformers) {
Client delegate = new Http2Client(httpClient);
return new RetryableFeignBlockingLoadBalancerClient(delegate, loadBalancerClient, loadBalancedRetryFactory,
loadBalancerClientFactory, transformers);
}
}

7
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClient.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2013-2023 the original author or authors.
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -68,7 +68,6 @@ import static org.springframework.cloud.openfeign.loadbalancer.LoadBalancerUtils @@ -68,7 +68,6 @@ import static org.springframework.cloud.openfeign.loadbalancer.LoadBalancerUtils
* @author Olga Maciaszek-Sharma
* @author changjin wei(魏昌进)
* @author Wonsik Cheung
* @author Andriy Pikozh
* @since 2.2.6
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@ -146,8 +145,8 @@ public class RetryableFeignBlockingLoadBalancerClient implements Client { @@ -146,8 +145,8 @@ public class RetryableFeignBlockingLoadBalancerClient implements Client {
// On retries the policy will choose the server and set it in the context
// and extract the server and update the request being made
if (context instanceof LoadBalancedRetryContext lbContext) {
retrievedServiceInstance = lbContext.getServiceInstance();
if (retrievedServiceInstance == null) {
ServiceInstance serviceInstance = lbContext.getServiceInstance();
if (serviceInstance == null) {
if (LOG.isDebugEnabled()) {
LOG.debug("Service instance retrieved from LoadBalancedRetryContext: was null. "
+ "Reattempting service instance selection");

91
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FeignHttpClientProperties.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2013-2023 the original author or authors.
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,14 +16,10 @@ @@ -16,14 +16,10 @@
package org.springframework.cloud.openfeign.support;
import java.net.http.HttpClient;
import java.time.Duration;
import java.util.List;
import java.util.concurrent.TimeUnit;
import feign.http2client.Http2Client;
import feign.okhttp.OkHttpClient;
import okhttp3.Protocol;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ -31,7 +27,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @@ -31,7 +27,6 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* @author Ryan Baxter
* @author Nguyen Ky Thanh
* @author Olga Maciaszek-Sharma
* @author changjin wei(魏昌进)
*/
@ConfigurationProperties(prefix = "spring.cloud.openfeign.httpclient")
public class FeignHttpClientProperties {
@ -102,11 +97,6 @@ public class FeignHttpClientProperties { @@ -102,11 +97,6 @@ public class FeignHttpClientProperties {
*/
private OkHttp okHttp = new OkHttp();
/**
* Additional {@link Http2Client}-specific properties.
*/
private Http2Properties http2 = new Http2Properties();
public int getConnectionTimerRepeat() {
return connectionTimerRepeat;
}
@ -187,14 +177,6 @@ public class FeignHttpClientProperties { @@ -187,14 +177,6 @@ public class FeignHttpClientProperties {
this.okHttp = okHttp;
}
public Http2Properties getHttp2() {
return http2;
}
public void setHttp2(Http2Properties http2) {
this.http2 = http2;
}
public static class Hc5Properties {
/**
@ -217,16 +199,6 @@ public class FeignHttpClientProperties { @@ -217,16 +199,6 @@ public class FeignHttpClientProperties {
*/
public static final TimeUnit DEFAULT_SOCKET_TIMEOUT_UNIT = TimeUnit.SECONDS;
/**
* Default value for connection request timeout.
*/
public static final int DEFAULT_CONNECTION_REQUEST_TIMEOUT = 3;
/**
* Default value for connection request timeout unit.
*/
public static final TimeUnit DEFAULT_CONNECTION_REQUEST_TIMEOUT_UNIT = TimeUnit.MINUTES;
/**
* Pool concurrency policies.
*/
@ -247,16 +219,6 @@ public class FeignHttpClientProperties { @@ -247,16 +219,6 @@ public class FeignHttpClientProperties {
*/
private TimeUnit socketTimeoutUnit = DEFAULT_SOCKET_TIMEOUT_UNIT;
/**
* Default value for connection request timeout.
*/
private int connectionRequestTimeout = DEFAULT_CONNECTION_REQUEST_TIMEOUT;
/**
* Default value for connection request timeout unit.
*/
private TimeUnit connectionRequestTimeoutUnit = DEFAULT_CONNECTION_REQUEST_TIMEOUT_UNIT;
public PoolConcurrencyPolicy getPoolConcurrencyPolicy() {
return poolConcurrencyPolicy;
}
@ -289,22 +251,6 @@ public class FeignHttpClientProperties { @@ -289,22 +251,6 @@ public class FeignHttpClientProperties {
this.socketTimeout = socketTimeout;
}
public int getConnectionRequestTimeout() {
return connectionRequestTimeout;
}
public void setConnectionRequestTimeout(int connectionRequestTimeout) {
this.connectionRequestTimeout = connectionRequestTimeout;
}
public TimeUnit getConnectionRequestTimeoutUnit() {
return connectionRequestTimeoutUnit;
}
public void setConnectionRequestTimeoutUnit(TimeUnit connectionRequestTimeoutUnit) {
this.connectionRequestTimeoutUnit = connectionRequestTimeoutUnit;
}
/**
* Enumeration of pool concurrency policies.
*/
@ -353,12 +299,6 @@ public class FeignHttpClientProperties { @@ -353,12 +299,6 @@ public class FeignHttpClientProperties {
*/
private Duration readTimeout = Duration.ofSeconds(60);
/**
* Configure the protocols used by this client to communicate with remote servers.
* Uses {@link String} values of {@link Protocol}.
*/
private List<String> protocols = List.of("HTTP_2", "HTTP_1_1");
public Duration getReadTimeout() {
return readTimeout;
}
@ -367,35 +307,6 @@ public class FeignHttpClientProperties { @@ -367,35 +307,6 @@ public class FeignHttpClientProperties {
this.readTimeout = readTimeout;
}
public List<String> getProtocols() {
return protocols;
}
public void setProtocols(List<String> protocols) {
this.protocols = protocols;
}
}
/**
* {@link Http2Client}-specific properties.
*/
public static class Http2Properties {
/**
* Configure the protocols used by this client to communicate with remote servers.
* Uses {@link String} value of {@link HttpClient.Version}.
*/
private String version = "HTTP_2";
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
}
}

36
spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2013-2023 the original author or authors.
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -34,14 +34,12 @@ import feign.Contract; @@ -34,14 +34,12 @@ import feign.Contract;
import feign.Feign;
import feign.MethodMetadata;
import feign.Param;
import feign.QueryMap;
import feign.Request;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.openfeign.AnnotatedParameterProcessor;
import org.springframework.cloud.openfeign.CollectionFormat;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.cloud.openfeign.annotation.CookieValueParameterProcessor;
import org.springframework.cloud.openfeign.annotation.MatrixVariableParameterProcessor;
import org.springframework.cloud.openfeign.annotation.PathVariableParameterProcessor;
@ -62,14 +60,12 @@ import org.springframework.core.convert.TypeDescriptor; @@ -62,14 +60,12 @@ import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;
import org.springframework.data.domain.Pageable;
import org.springframework.http.InvalidMediaTypeException;
import org.springframework.http.MediaType;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import static feign.Util.checkState;
import static feign.Util.emptyToNull;
@ -161,7 +157,7 @@ public class SpringMvcContract extends Contract.BaseContract implements Resource @@ -161,7 +157,7 @@ public class SpringMvcContract extends Contract.BaseContract implements Resource
private static TypeDescriptor getElementTypeDescriptor(TypeDescriptor typeDescriptor) {
TypeDescriptor elementTypeDescriptor = typeDescriptor.getElementTypeDescriptor();
// that means it's not a collection, but it is iterable, gh-135
// that means it's not a collection but it is iterable, gh-135
if (elementTypeDescriptor == null && Iterable.class.isAssignableFrom(typeDescriptor.getType())) {
ResolvableType type = typeDescriptor.getResolvableType().as(Iterable.class).getGeneric(0);
if (type.resolve() == null) {
@ -269,20 +265,6 @@ public class SpringMvcContract extends Contract.BaseContract implements Resource @@ -269,20 +265,6 @@ public class SpringMvcContract extends Contract.BaseContract implements Resource
protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations, int paramIndex) {
boolean isHttpAnnotation = false;
try {
if (Pageable.class.isAssignableFrom(data.method().getParameterTypes()[paramIndex])) {
// do not set a Pageable as QueryMap if there's an actual QueryMap param
// present
if (!queryMapParamPresent(data)) {
data.queryMapIndex(paramIndex);
return false;
}
}
}
catch (NoClassDefFoundError ignored) {
// Do nothing; added to avoid exceptions if optional dependency not present
}
AnnotatedParameterProcessor.AnnotatedParameterContext context = new SimpleAnnotatedParameterContext(data,
paramIndex);
Method method = processedMethods.get(data.configKey());
@ -311,20 +293,6 @@ public class SpringMvcContract extends Contract.BaseContract implements Resource @@ -311,20 +293,6 @@ public class SpringMvcContract extends Contract.BaseContract implements Resource
return isHttpAnnotation;
}
private boolean queryMapParamPresent(MethodMetadata data) {
Annotation[][] paramsAnnotations = data.method().getParameterAnnotations();
for (int i = 0; i < paramsAnnotations.length; i++) {
Annotation[] paramAnnotations = paramsAnnotations[i];
Class<?> parameterType = data.method().getParameterTypes()[i];
if (Arrays.stream(paramAnnotations).anyMatch(
annotation -> Map.class.isAssignableFrom(parameterType) && annotation instanceof RequestParam
|| annotation instanceof SpringQueryMap || annotation instanceof QueryMap)) {
return true;
}
}
return false;
}
private void parseProduces(MethodMetadata md, Method method, RequestMapping annotation) {
String[] serverProduces = annotation.produces();
String clientAccepts = serverProduces.length == 0 ? null : emptyToNull(serverProduces[0]);

8
spring-cloud-openfeign-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json

@ -44,12 +44,6 @@ @@ -44,12 +44,6 @@
"description": "Enables the use of the OK HTTP Client by Feign.",
"defaultValue": "false"
},
{
"name": "spring.cloud.openfeign.http2client.enabled",
"type": "java.lang.Boolean",
"description": "Enables the use of the Java11 HTTP 2 Client by Feign.",
"defaultValue": "false"
},
{
"name": "spring.cloud.openfeign.compression.response.enabled",
"type": "java.lang.Boolean",
@ -81,7 +75,7 @@ @@ -81,7 +75,7 @@
"defaultValue": "false"
},
{
"name": "spring.cloud.openfeign.oauth2.clientRegistrationId",
"name": "spring.cloud.openfeign.oauth2.registrationClientId",
"type": "java.lang.String",
"description": "Provides a clientId to be used with OAuth2.",
"defaultValue": ""

1
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientCacheTests.java

@ -41,7 +41,6 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; @@ -41,7 +41,6 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
/**
* @author Sam Kruglov
* @author Dominique Villard
*/
@SpringBootTest(classes = FeignClientCacheTests.TestConfiguration.class)
@DirtiesContext

5
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientConfigurationTests.java

@ -27,7 +27,6 @@ import feign.ExceptionPropagationPolicy; @@ -27,7 +27,6 @@ import feign.ExceptionPropagationPolicy;
import feign.Logger;
import feign.QueryMapEncoder;
import feign.RequestInterceptor;
import feign.ResponseInterceptor;
import feign.Retryer;
import feign.codec.Decoder;
import feign.codec.Encoder;
@ -61,7 +60,6 @@ class FeignClientConfigurationTests { @@ -61,7 +60,6 @@ class FeignClientConfigurationTests {
assertThat(config.getRetryer()).isNull();
assertThat(config.getErrorDecoder()).isNull();
assertThat(config.getRequestInterceptors()).isNull();
assertThat(config.getResponseInterceptor()).isNull();
assertThat(config.getDefaultRequestHeaders()).isNull();
assertThat(config.getDefaultQueryParameters()).isNull();
assertThat(config.getDismiss404()).isNull();
@ -84,8 +82,6 @@ class FeignClientConfigurationTests { @@ -84,8 +82,6 @@ class FeignClientConfigurationTests {
config.setErrorDecoder(ErrorDecoder.class);
List<Class<RequestInterceptor>> requestInterceptors = Lists.list(RequestInterceptor.class);
config.setRequestInterceptors(requestInterceptors);
Class<ResponseInterceptor> responseInterceptor = ResponseInterceptor.class;
config.setResponseInterceptor(responseInterceptor);
Map<String, Collection<String>> defaultRequestHeaders = Maps.newHashMap("default", Collections.emptyList());
config.setDefaultRequestHeaders(defaultRequestHeaders);
Map<String, Collection<String>> defaultQueryParameters = Maps.newHashMap("default", Collections.emptyList());
@ -107,7 +103,6 @@ class FeignClientConfigurationTests { @@ -107,7 +103,6 @@ class FeignClientConfigurationTests {
assertThat(config.getRetryer()).isSameAs(Retryer.class);
assertThat(config.getErrorDecoder()).isSameAs(ErrorDecoder.class);
assertThat(config.getRequestInterceptors()).isSameAs(requestInterceptors);
assertThat(config.getResponseInterceptor()).isSameAs(responseInterceptor);
assertThat(config.getDefaultRequestHeaders()).isSameAs(defaultRequestHeaders);
assertThat(config.getDefaultQueryParameters()).isSameAs(defaultQueryParameters);
assertThat(config.getDismiss404()).isTrue();

41
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientUsingPropertiesTests.java

@ -34,13 +34,11 @@ import java.util.stream.Stream; @@ -34,13 +34,11 @@ import java.util.stream.Stream;
import feign.Capability;
import feign.Feign;
import feign.InvocationContext;
import feign.InvocationHandlerFactory;
import feign.QueryMapEncoder;
import feign.Request;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.ResponseInterceptor;
import feign.RetryableException;
import feign.Retryer;
import feign.codec.EncodeException;
@ -81,7 +79,6 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen @@ -81,7 +79,6 @@ import static org.springframework.boot.test.context.SpringBootTest.WebEnvironmen
* @author Ilia Ilinykh
* @author Jonatan Ivanov
* @author Hyeonmin Park
* @author Dominique Villard
*/
@SuppressWarnings("FieldMayBeFinal")
@SpringBootTest(classes = FeignClientUsingPropertiesTests.Application.class, webEnvironment = RANDOM_PORT)
@ -102,8 +99,6 @@ public class FeignClientUsingPropertiesTests { @@ -102,8 +99,6 @@ public class FeignClientUsingPropertiesTests {
private FeignClientFactoryBean barFactoryBean;
private FeignClientFactoryBean bazFactoryBean;
private FeignClientFactoryBean unwrapFactoryBean;
private FeignClientFactoryBean formFactoryBean;
@ -121,10 +116,6 @@ public class FeignClientUsingPropertiesTests { @@ -121,10 +116,6 @@ public class FeignClientUsingPropertiesTests {
barFactoryBean.setContextId("bar");
barFactoryBean.setType(FeignClientFactoryBean.class);
bazFactoryBean = new FeignClientFactoryBean();
bazFactoryBean.setContextId("baz");
bazFactoryBean.setType(FeignClientFactoryBean.class);
unwrapFactoryBean = new FeignClientFactoryBean();
unwrapFactoryBean.setContextId("unwrap");
unwrapFactoryBean.setType(FeignClientFactoryBean.class);
@ -152,11 +143,6 @@ public class FeignClientUsingPropertiesTests { @@ -152,11 +143,6 @@ public class FeignClientUsingPropertiesTests {
return barFactoryBean.feign(context).target(BarClient.class, "http://localhost:" + port);
}
public PingClient pingClient() {
bazFactoryBean.setApplicationContext(applicationContext);
return bazFactoryBean.feign(context).target(PingClient.class, "http://localhost:" + port);
}
public UnwrapClient unwrapClient() {
unwrapFactoryBean.setApplicationContext(applicationContext);
return unwrapFactoryBean.feign(context).target(UnwrapClient.class, "http://localhost:" + port);
@ -178,12 +164,6 @@ public class FeignClientUsingPropertiesTests { @@ -178,12 +164,6 @@ public class FeignClientUsingPropertiesTests {
assertThatThrownBy(() -> barClient().bar()).isInstanceOf(RetryableException.class);
}
@Test
public void testBaz() {
String response = pingClient().ping();
assertThat(response).isEqualTo("baz");
}
@Test
public void testUnwrap() throws Exception {
assertThatThrownBy(() -> unwrapClient().unwrap()).isInstanceOf(SocketTimeoutException.class);
@ -318,13 +298,6 @@ public class FeignClientUsingPropertiesTests { @@ -318,13 +298,6 @@ public class FeignClientUsingPropertiesTests {
}
protected interface PingClient {
@GetMapping(path = "/ping")
String ping();
}
protected interface UnwrapClient {
@GetMapping(path = "/bar") // intentionally /bar
@ -382,11 +355,6 @@ public class FeignClientUsingPropertiesTests { @@ -382,11 +355,6 @@ public class FeignClientUsingPropertiesTests {
return "OK";
}
@GetMapping(path = "/ping")
public String ping() {
return "pong";
}
@PostMapping(path = "/form", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public String form(HttpServletRequest request) {
return request.getParameter("form");
@ -426,15 +394,6 @@ public class FeignClientUsingPropertiesTests { @@ -426,15 +394,6 @@ public class FeignClientUsingPropertiesTests {
}
public static class BazResponseInterceptor implements ResponseInterceptor {
@Override
public Object aroundDecode(InvocationContext invocationContext) {
return "baz";
}
}
public static class NoRetryer implements Retryer {
@Override

42
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignCompressionTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2013-2023 the original author or authors.
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,20 +14,19 @@ @@ -14,20 +14,19 @@
* limitations under the License.
*/
package org.springframework.cloud.openfeign.encoding;
package org.springframework.cloud.openfeign;
import java.util.Map;
import feign.RequestInterceptor;
import okhttp3.OkHttpClient;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.FeignClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingAutoConfiguration;
import org.springframework.cloud.openfeign.encoding.FeignAcceptGzipEncodingInterceptor;
import org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingAutoConfiguration;
import org.springframework.cloud.openfeign.encoding.FeignContentGzipEncodingInterceptor;
import static org.assertj.core.api.Assertions.assertThat;
@ -39,7 +38,7 @@ import static org.assertj.core.api.Assertions.assertThat; @@ -39,7 +38,7 @@ import static org.assertj.core.api.Assertions.assertThat;
class FeignCompressionTests {
@Test
void shouldAddCompressionInterceptors() {
void testInterceptors() {
new ApplicationContextRunner()
.withPropertyValues("spring.cloud.openfeign.compression.response.enabled=true",
"spring.cloud.openfeign.compression.request.enabled=true",
@ -59,31 +58,4 @@ class FeignCompressionTests { @@ -59,31 +58,4 @@ class FeignCompressionTests {
});
}
@Test
void shouldNotAddInterceptorsIfFeignOkHttpClientPresent() {
new ApplicationContextRunner()
.withPropertyValues("spring.cloud.openfeign.compression.response.enabled=true",
"spring.cloud.openfeign.compression.request.enabled=true",
"spring.cloud.openfeign.okhttp.enabled=true", "spring.cloud.openfeign.httpclient.hc5.enabled")
.withConfiguration(AutoConfigurations.of(FeignAutoConfiguration.class,
FeignContentGzipEncodingAutoConfiguration.class,
FeignAcceptGzipEncodingAutoConfiguration.class))
.run(context -> {
FeignClientFactory feignClientFactory = context.getBean(FeignClientFactory.class);
Map<String, RequestInterceptor> interceptors = feignClientFactory.getInstances("foo",
RequestInterceptor.class);
assertThat(interceptors).isEmpty();
});
}
@Configuration
static class OkHttpClientConfiguration {
@Bean
OkHttpClient okHttpClient() {
return new OkHttpClient();
}
}
}

70
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignHttp2ClientConfigurationTests.java

@ -1,70 +0,0 @@ @@ -1,70 +0,0 @@
/*
* Copyright 2013-2023 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.net.http.HttpClient;
import java.time.Duration;
import java.util.Optional;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author changjin wei(魏昌进)
*/
class FeignHttp2ClientConfigurationTests {
private ConfigurableApplicationContext context;
@BeforeEach
void setUp() {
context = new SpringApplicationBuilder()
.properties("debug=true", "spring.cloud.openfeign.http2client.enabled=true",
"spring.cloud.openfeign.httpclient.http2.version=HTTP_1_1",
"spring.cloud.openfeign.httpclient.connectionTimeout=15")
.web(WebApplicationType.NONE).sources(FeignAutoConfiguration.class).run();
}
@AfterEach
void tearDown() {
if (context != null) {
context.close();
}
}
@Test
void shouldConfigureConnectTimeout() {
HttpClient httpClient = context.getBean(HttpClient.class);
assertThat(httpClient.connectTimeout()).isEqualTo(Optional.ofNullable(Duration.ofMillis(15)));
}
@Test
void shouldResolveVersionFromProperties() {
HttpClient httpClient = context.getBean(HttpClient.class);
assertThat(httpClient.version()).isEqualTo(HttpClient.Version.HTTP_1_1);
}
}

12
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignOkHttpConfigurationTests.java

@ -21,7 +21,6 @@ import java.lang.reflect.Field; @@ -21,7 +21,6 @@ import java.lang.reflect.Field;
import javax.net.ssl.HostnameVerifier;
import okhttp3.OkHttpClient;
import okhttp3.Protocol;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -35,7 +34,6 @@ import static org.assertj.core.api.Assertions.assertThat; @@ -35,7 +34,6 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Ryan Baxter
* @author changjin wei(魏昌进)
*/
class FeignOkHttpConfigurationTests {
@ -47,8 +45,7 @@ class FeignOkHttpConfigurationTests { @@ -47,8 +45,7 @@ class FeignOkHttpConfigurationTests {
.properties("debug=true", "spring.cloud.openfeign.httpclient.disableSslValidation=true",
"spring.cloud.openfeign.okhttp.enabled=true",
"spring.cloud.openfeign.httpclient.hc5.enabled=false",
"spring.cloud.openfeign.httpclient.okhttp.read-timeout=9s",
"spring.cloud.openfeign.httpclient.okhttp.protocols=H2_PRIOR_KNOWLEDGE")
"spring.cloud.openfeign.httpclient.okhttp.read-timeout=9s")
.web(WebApplicationType.NONE).sources(FeignAutoConfiguration.class).run();
}
@ -74,13 +71,6 @@ class FeignOkHttpConfigurationTests { @@ -74,13 +71,6 @@ class FeignOkHttpConfigurationTests {
assertThat(httpClient.readTimeoutMillis()).isEqualTo(9000);
}
@Test
void shouldResolveProtocolFromProperties() {
OkHttpClient httpClient = context.getBean(OkHttpClient.class);
assertThat(httpClient.protocols()).containsExactly(Protocol.H2_PRIOR_KNOWLEDGE);
}
protected Object getField(Object target, String name) {
Field field = ReflectionUtils.findField(target.getClass(), name);
ReflectionUtils.makeAccessible(field);

5
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/NonRefreshableFeignClientUrlTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2013-2023 the original author or authors.
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -33,7 +33,6 @@ import static org.assertj.core.api.Assertions.assertThat; @@ -33,7 +33,6 @@ import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Jasbir Singh
* @author Olga Maciaszek-Sharma
*/
@SpringBootTest
@TestPropertySource("classpath:feign-properties.properties")
@ -66,7 +65,7 @@ class NonRefreshableFeignClientUrlTests { @@ -66,7 +65,7 @@ class NonRefreshableFeignClientUrlTests {
public void shouldInstantiateFeignClientWhenUrlFromProperties() {
UrlTestClient.UrlResponseForTests response = configBasedClient.test();
assertThat(response.getUrl()).isEqualTo("http://localhost:9999/test");
assertThat(response.getTargetType()).isEqualTo(PropertyBasedTarget.class);
assertThat(response.getTargetType()).isEqualTo(Target.HardCodedTarget.class);
}
@Test

197
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/aot/FeignClientBeanFactoryInitializationAotProcessorTests.java

@ -1,197 +0,0 @@ @@ -1,197 +0,0 @@
/*
* Copyright 2022-2023 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.aot;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.aot.generate.GeneratedClass;
import org.springframework.aot.generate.GeneratedMethods;
import org.springframework.aot.generate.GenerationContext;
import org.springframework.aot.generate.MethodReference;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.test.generate.TestGenerationContext;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.aot.BeanFactoryInitializationAotContribution;
import org.springframework.beans.factory.aot.BeanFactoryInitializationCode;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.FeignClientFactory;
import org.springframework.cloud.openfeign.FeignClientSpecification;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.javapoet.TypeSpec;
import org.springframework.web.bind.annotation.PostMapping;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.aot.hint.predicate.RuntimeHintsPredicates.proxies;
import static org.springframework.aot.hint.predicate.RuntimeHintsPredicates.reflection;
/**
* Tests for {@link FeignClientBeanFactoryInitializationAotProcessor}.
*
* @author Olga Maciaszek-Sharma
*/
class FeignClientBeanFactoryInitializationAotProcessorTests {
private static final String BEAN_DEFINITION_CLASS_NAME = "org.springframework.cloud.openfeign.aot.FeignClientBeanFactoryInitializationAotProcessorTests$TestClient";
private final TestGenerationContext generationContext = new TestGenerationContext();
private final FeignClientFactory feignClientFactory = mock(FeignClientFactory.class);
private final BeanFactoryInitializationCode beanFactoryInitializationCode = new MockBeanFactoryInitializationCode(
generationContext);
@BeforeEach
void setUp() {
Map<String, FeignClientSpecification> configurations = Map.of("test",
new FeignClientSpecification("test", TestClient.class.getCanonicalName(), new Class<?>[] {}));
when(feignClientFactory.getConfigurations()).thenReturn(configurations);
}
@Test
void shouldGenerateBeanInitializationCodeAndRegisterHints() {
DefaultListableBeanFactory beanFactory = beanFactory();
GenericApplicationContext context = new GenericApplicationContext(beanFactory);
FeignClientBeanFactoryInitializationAotProcessor processor = new FeignClientBeanFactoryInitializationAotProcessor(
context, feignClientFactory);
BeanFactoryInitializationAotContribution contribution = processor.processAheadOfTime(beanFactory);
assertThat(contribution).isNotNull();
verifyContribution((FeignClientBeanFactoryInitializationAotProcessor.AotContribution) contribution);
contribution.applyTo(generationContext, beanFactoryInitializationCode);
RuntimeHints hints = generationContext.getRuntimeHints();
assertThat(reflection().onType(TestReturnType.class)).accepts(hints);
assertThat(reflection().onType(TestArgType.class)).accepts(hints);
assertThat(proxies().forInterfaces(TestClient.class)).accepts(hints);
}
private static void verifyContribution(
FeignClientBeanFactoryInitializationAotProcessor.AotContribution contribution) {
BeanDefinition contributionBeanDefinition = contribution.getFeignClientBeanDefinitions()
.get(TestClient.class.getCanonicalName());
assertThat(contributionBeanDefinition.getBeanClassName()).isEqualTo(BEAN_DEFINITION_CLASS_NAME);
PropertyValues propertyValues = contributionBeanDefinition.getPropertyValues();
assertThat(propertyValues).isNotEmpty();
assertThat(((String[]) propertyValues.getPropertyValue("qualifiers").getValue())).containsExactly("test");
assertThat(propertyValues.getPropertyValue("type").getValue()).isEqualTo(TestClient.class.getCanonicalName());
assertThat(propertyValues.getPropertyValue("fallback").getValue()).isEqualTo(String.class);
}
private DefaultListableBeanFactory beanFactory() {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition(new RootBeanDefinition(TestClient.class,
new ConstructorArgumentValues(),
new MutablePropertyValues(List.of(new PropertyValue("type", TestClient.class.getCanonicalName()),
new PropertyValue("qualifiers", new String[] { "test" }),
new PropertyValue("fallback", String.class),
new PropertyValue("fallbackFactory", Void.class)))));
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
beanFactory.registerBeanDefinition(TestClient.class.getCanonicalName(), beanDefinition);
return beanFactory;
}
@FeignClient
interface TestClient {
@PostMapping("/test")
TestReturnType test(TestArgType arg);
}
@SuppressWarnings("NullableProblems")
static class MockBeanFactoryInitializationCode implements BeanFactoryInitializationCode {
private static final Consumer<TypeSpec.Builder> emptyTypeCustomizer = type -> {
};
private final GeneratedClass generatedClass;
MockBeanFactoryInitializationCode(GenerationContext generationContext) {
generatedClass = generationContext.getGeneratedClasses().addForFeature("Test", emptyTypeCustomizer);
}
@Override
public GeneratedMethods getMethods() {
return generatedClass.getMethods();
}
@Override
public void addInitializer(MethodReference methodReference) {
new ArrayList<>();
}
}
static class TestReturnType {
private String test;
TestReturnType(String test) {
this.test = test;
}
TestReturnType() {
}
String getTest() {
return test;
}
void setTest(String test) {
this.test = test;
}
}
static class TestArgType {
private String test;
TestArgType(String test) {
this.test = test;
}
TestArgType() {
}
String getTest() {
return test;
}
void setTest(String test) {
this.test = test;
}
}
}

134
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/circuitbreaker/FallbackSupportFactoryBeanTests.java

@ -1,134 +0,0 @@ @@ -1,134 +0,0 @@
/*
* Copyright 2013-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.openfeign.circuitbreaker;
import java.util.function.Function;
import java.util.function.Supplier;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.cloud.client.circuitbreaker.CircuitBreaker;
import org.springframework.cloud.client.circuitbreaker.CircuitBreakerFactory;
import org.springframework.cloud.client.circuitbreaker.ConfigBuilder;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignAutoConfiguration;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.GetMapping;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author 黄学敏huangxuemin)
*/
class FallbackSupportFactoryBeanTests {
private static final String FACTORY_BEAN_FALLBACK_MESSAGE = "factoryBean fallback message";
private static final String ORIGINAL_FALLBACK_MESSAGE = "OriginalFeign fallback message";
private final ApplicationContextRunner runner = new ApplicationContextRunner()
.withBean(FactoryBeanFallbackFeignFallback.class).withBean(OriginalFeignFallback.class)
.withConfiguration(AutoConfigurations.of(TestConfiguration.class, FeignAutoConfiguration.class))
.withBean(CircuitBreakerFactory.class, () -> new CircuitBreakerFactory() {
@Override
public CircuitBreaker create(String id) {
return new CircuitBreaker() {
@Override
public <T> T run(Supplier<T> toRun, Function<Throwable, T> fallback) {
try {
return toRun.get();
}
catch (Throwable t) {
return fallback.apply(t);
}
}
};
}
@Override
protected ConfigBuilder configBuilder(String id) {
return null;
}
@Override
public void configureDefault(Function defaultConfiguration) {
}
}).withPropertyValues("spring.cloud.openfeign.circuitbreaker.enabled=true");
@Test
void shouldRunFallbackFromBeanOrFactoryBean() {
runner.run(ctx -> {
assertThat(ctx.getBean(OriginalFeign.class).get().equals(ORIGINAL_FALLBACK_MESSAGE)).isTrue();
assertThat(ctx.getBean(FactoryBeanFallbackFeign.class).get().equals(FACTORY_BEAN_FALLBACK_MESSAGE))
.isTrue();
});
}
@Configuration(proxyBeanMethods = false)
@EnableFeignClients(clients = { FallbackSupportFactoryBeanTests.OriginalFeign.class,
FallbackSupportFactoryBeanTests.FactoryBeanFallbackFeign.class })
@EnableAutoConfiguration
private static class TestConfiguration {
}
@FeignClient(name = "original", url = "https://original", fallback = OriginalFeignFallback.class)
interface OriginalFeign {
@GetMapping("/")
String get();
}
@FeignClient(name = "factoryBean", url = "https://factoryBean", fallback = FactoryBeanFallbackFeignFallback.class)
interface FactoryBeanFallbackFeign {
@GetMapping("/")
String get();
}
private static class FactoryBeanFallbackFeignFallback implements FactoryBean<FactoryBeanFallbackFeign> {
@Override
public FactoryBeanFallbackFeign getObject() {
return () -> FACTORY_BEAN_FALLBACK_MESSAGE;
}
@Override
public Class<?> getObjectType() {
return FactoryBeanFallbackFeign.class;
}
}
private static class OriginalFeignFallback implements OriginalFeign {
@Override
public String get() {
return ORIGINAL_FALLBACK_MESSAGE;
}
}
}

36
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/loadbalancer/FeignLoadBalancerAutoConfigurationTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2013-2023 the original author or authors.
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,12 +16,10 @@ @@ -16,12 +16,10 @@
package org.springframework.cloud.openfeign.loadbalancer;
import java.net.http.HttpClient;
import java.util.Map;
import feign.Client;
import feign.hc5.ApacheHttp5Client;
import feign.http2client.Http2Client;
import feign.okhttp.OkHttpClient;
import org.junit.jupiter.api.Test;
@ -39,7 +37,6 @@ import static org.springframework.test.util.ReflectionTestUtils.getField; @@ -39,7 +37,6 @@ import static org.springframework.test.util.ReflectionTestUtils.getField;
/**
* @author Olga Maciaszek-Sharma
* @author Nguyen Ky Thanh
* @author changjin wei(魏昌进)
*/
class FeignLoadBalancerAutoConfigurationTests {
@ -68,21 +65,6 @@ class FeignLoadBalancerAutoConfigurationTests { @@ -68,21 +65,6 @@ class FeignLoadBalancerAutoConfigurationTests {
}
@Test
void shouldInstantiateHttp2ClientFeignClientWhenEnabled() {
ConfigurableApplicationContext context = initContext("spring.cloud.openfeign.httpclient.hc5.enabled=false",
"spring.cloud.openfeign.http2client.enabled=true", "spring.cloud.loadbalancer.retry.enabled=false");
assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
Map<String, FeignBlockingLoadBalancerClient> beans = context
.getBeansOfType(FeignBlockingLoadBalancerClient.class);
assertThat(beans).as("Missing bean of type %s", Http2Client.class).hasSize(1);
Client client = beans.get("feignClient").getDelegate();
assertThat(client).isInstanceOf(Http2Client.class);
Http2Client http2Client = (Http2Client) client;
HttpClient httpClient = (HttpClient) getField(http2Client, "client");
assertThat(httpClient).isInstanceOf(HttpClient.class);
}
@Test
void shouldInstantiateHttpFeignClient5WhenAvailableAndOkHttpDisabled() {
ConfigurableApplicationContext context = initContext("spring.cloud.openfeign.okhttp.enabled=false",
@ -91,14 +73,6 @@ class FeignLoadBalancerAutoConfigurationTests { @@ -91,14 +73,6 @@ class FeignLoadBalancerAutoConfigurationTests {
assertLoadBalanced(context, ApacheHttp5Client.class);
}
@Test
void shouldInstantiateHttpFeignClient5WhenAvailableAndHttp2ClientDisabled() {
ConfigurableApplicationContext context = initContext("spring.cloud.openfeign.http2client.enabled=false",
"spring.cloud.loadbalancer.retry.enabled=false");
assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
assertLoadBalanced(context, ApacheHttp5Client.class);
}
@Test
void shouldInstantiateRetryableDefaultFeignBlockingLoadBalancerClientWhenHttpClientDisabled() {
ConfigurableApplicationContext context = initContext("spring.cloud.openfeign.httpclient.hc5.enabled=false");
@ -114,14 +88,6 @@ class FeignLoadBalancerAutoConfigurationTests { @@ -114,14 +88,6 @@ class FeignLoadBalancerAutoConfigurationTests {
assertLoadBalancedWithRetries(context, OkHttpClient.class);
}
@Test
void shouldInstantiateRetryableHttp2ClientFeignClientWhenEnabled() {
ConfigurableApplicationContext context = initContext("spring.cloud.openfeign.httpclient.hc5.enabled=false",
"spring.cloud.openfeign.http2client.enabled=true");
assertThatOneBeanPresent(context, BlockingLoadBalancerClient.class);
assertLoadBalancedWithRetries(context, Http2Client.class);
}
@Test
void shouldInstantiateRetryableHttpFeignClient5WhenEnabled() {
ConfigurableApplicationContext context = initContext();

44
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/loadbalancer/RetryableFeignBlockingLoadBalancerClientTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2013-2023 the original author or authors.
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -75,7 +75,6 @@ import static org.mockito.Mockito.when; @@ -75,7 +75,6 @@ import static org.mockito.Mockito.when;
* @author Olga Maciaszek-Sharma
* @author changjin wei(魏昌进)
* @author Wonsik Cheung
* @author Andriy Pikozh
* @see <a href=
* "https://github.com/spring-cloud/spring-cloud-commons/blob/main/spring-cloud-loadbalancer/src/test/java/org/springframework/cloud/loadbalancer/blocking/client/BlockingLoadBalancerClientTests.java">BlockingLoadBalancerClientTests</a>
*/
@ -165,51 +164,10 @@ class RetryableFeignBlockingLoadBalancerClientTests { @@ -165,51 +164,10 @@ class RetryableFeignBlockingLoadBalancerClientTests {
feignBlockingLoadBalancerClient.execute(request, new Request.Options());
verify(loadBalancerClient, times(2)).choose(eq("test"), any());
verify(loadBalancerClient, times(2)).reconstructURI(serviceInstance, URI.create("http://test/path"));
verify(delegate, times(2)).execute(any(), any());
}
@Test
void shouldReuseServerInstanceOnSameInstanceRetry() throws IOException {
properties.getRetry().setMaxRetriesOnSameServiceInstance(1);
properties.getRetry().setMaxRetriesOnNextServiceInstance(0);
properties.getRetry().getRetryableStatusCodes().add(503);
Request request = testRequest();
Response response = testResponse(503);
when(delegate.execute(any(), any())).thenReturn(response);
when(retryFactory.createRetryPolicy(any(), eq(loadBalancerClient)))
.thenReturn(new BlockingLoadBalancedRetryPolicy(properties));
when(loadBalancerClient.reconstructURI(serviceInstance, URI.create("http://test/path")))
.thenReturn(URI.create("http://testhost:80/path"));
feignBlockingLoadBalancerClient.execute(request, new Request.Options());
verify(loadBalancerClient, times(1)).choose(eq("test"), any());
verify(loadBalancerClient, times(2)).reconstructURI(serviceInstance, URI.create("http://test/path"));
verify(delegate, times(2)).execute(any(), any());
}
@Test
void shouldReuseServerInstanceOnSameInstanceRetryWithBothSameAndNextRetries() throws IOException {
properties.getRetry().setMaxRetriesOnSameServiceInstance(1);
properties.getRetry().setMaxRetriesOnNextServiceInstance(1);
properties.getRetry().getRetryableStatusCodes().add(503);
Request request = testRequest();
Response response = testResponse(503);
when(delegate.execute(any(), any())).thenReturn(response);
when(retryFactory.createRetryPolicy(any(), eq(loadBalancerClient)))
.thenReturn(new BlockingLoadBalancedRetryPolicy(properties));
when(loadBalancerClient.reconstructURI(serviceInstance, URI.create("http://test/path")))
.thenReturn(URI.create("http://testhost:80/path"));
feignBlockingLoadBalancerClient.execute(request, new Request.Options());
verify(loadBalancerClient, times(2)).choose(eq("test"), any());
verify(loadBalancerClient, times(4)).reconstructURI(serviceInstance, URI.create("http://test/path"));
verify(delegate, times(4)).execute(any(), any());
}
@Test
void shouldNotRetryOnDisabled() throws IOException {
properties.getRetry().setEnabled(false);

79
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/protocol/FeignOkHttpProtocolsTests.java

@ -1,79 +0,0 @@ @@ -1,79 +0,0 @@
/*
* Copyright 2013-2023 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.protocol;
import java.lang.reflect.Field;
import feign.Client;
import okhttp3.Protocol;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.RestController;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author changjin wei(魏昌进)
*/
@SpringBootTest(classes = FeignOkHttpProtocolsTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT,
value = { "spring.application.name=feignclienttest", "spring.cloud.openfeign.circuitbreaker.enabled=false",
"spring.cloud.openfeign.httpclient.hc5.enabled=false", "spring.cloud.openfeign.okhttp.enabled=true",
"spring.cloud.httpclientfactories.ok.enabled=true", "spring.cloud.loadbalancer.retry.enabled=false",
"server.http2.enabled=true", "spring.cloud.openfeign.httpclient.okhttp.protocols=H2_PRIOR_KNOWLEDGE" })
@DirtiesContext
class FeignOkHttpProtocolsTests {
@Autowired
private Client feignClient;
@Test
void shouldCreateCorrectFeignClientBeanWithProtocolFromProperties() {
assertThat(feignClient).isInstanceOf(FeignBlockingLoadBalancerClient.class);
FeignBlockingLoadBalancerClient client = (FeignBlockingLoadBalancerClient) feignClient;
Client delegate = client.getDelegate();
assertThat(delegate).isInstanceOf(feign.okhttp.OkHttpClient.class);
okhttp3.OkHttpClient OkHttpClient = (okhttp3.OkHttpClient) getField(delegate, "delegate");
assertThat(OkHttpClient.protocols()).containsExactly(Protocol.H2_PRIOR_KNOWLEDGE);
}
protected Object getField(Object target, String name) {
Field field = ReflectionUtils.findField(target.getClass(), name);
ReflectionUtils.makeAccessible(field);
return ReflectionUtils.getField(field, target);
}
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@RestController
@LoadBalancerClients
@Import(NoSecurityConfiguration.class)
protected static class Application {
}
}

37
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/FeignHttpClientPropertiesTests.java

@ -32,15 +32,12 @@ import org.springframework.context.annotation.Configuration; @@ -32,15 +32,12 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.openfeign.support.FeignHttpClientProperties.Hc5Properties.DEFAULT_CONNECTION_REQUEST_TIMEOUT;
import static org.springframework.cloud.openfeign.support.FeignHttpClientProperties.Hc5Properties.DEFAULT_CONNECTION_REQUEST_TIMEOUT_UNIT;
import static org.springframework.cloud.openfeign.support.FeignHttpClientProperties.Hc5Properties.DEFAULT_SOCKET_TIMEOUT;
import static org.springframework.cloud.openfeign.support.FeignHttpClientProperties.Hc5Properties.DEFAULT_SOCKET_TIMEOUT_UNIT;
/**
* @author Ryan Baxter
* @author Nguyen Ky Thanh
* @author changjin wei(魏昌进)
*/
@DirtiesContext
class FeignHttpClientPropertiesTests {
@ -70,30 +67,22 @@ class FeignHttpClientPropertiesTests { @@ -70,30 +67,22 @@ class FeignHttpClientPropertiesTests {
assertThat(getProperties().getHc5().getPoolReusePolicy()).isEqualTo(PoolReusePolicy.FIFO);
assertThat(getProperties().getHc5().getSocketTimeout()).isEqualTo(DEFAULT_SOCKET_TIMEOUT);
assertThat(getProperties().getHc5().getSocketTimeoutUnit()).isEqualTo(DEFAULT_SOCKET_TIMEOUT_UNIT);
assertThat(getProperties().getHc5().getConnectionRequestTimeout())
.isEqualTo(DEFAULT_CONNECTION_REQUEST_TIMEOUT);
assertThat(getProperties().getHc5().getConnectionRequestTimeoutUnit())
.isEqualTo(DEFAULT_CONNECTION_REQUEST_TIMEOUT_UNIT);
}
@Test
void testCustomization() {
TestPropertyValues
.of("spring.cloud.openfeign.httpclient.maxConnections=2",
"spring.cloud.openfeign.httpclient.connectionTimeout=2",
"spring.cloud.openfeign.httpclient.maxConnectionsPerRoute=2",
"spring.cloud.openfeign.httpclient.timeToLive=2",
"spring.cloud.openfeign.httpclient.disableSslValidation=true",
"spring.cloud.openfeign.httpclient.followRedirects=false",
"spring.cloud.openfeign.httpclient.disableSslValidation=true",
"spring.cloud.openfeign.httpclient.followRedirects=false",
"spring.cloud.openfeign.httpclient.hc5.poolConcurrencyPolicy=lax",
"spring.cloud.openfeign.httpclient.hc5.poolReusePolicy=lifo",
"spring.cloud.openfeign.httpclient.hc5.socketTimeout=200",
"spring.cloud.openfeign.httpclient.hc5.socketTimeoutUnit=milliseconds",
"spring.cloud.openfeign.httpclient.hc5.connectionRequestTimeout=200",
"spring.cloud.openfeign.httpclient.hc5.connectionRequestTimeoutUnit=milliseconds")
.applyTo(this.context);
TestPropertyValues.of("spring.cloud.openfeign.httpclient.maxConnections=2",
"spring.cloud.openfeign.httpclient.connectionTimeout=2",
"spring.cloud.openfeign.httpclient.maxConnectionsPerRoute=2",
"spring.cloud.openfeign.httpclient.timeToLive=2",
"spring.cloud.openfeign.httpclient.disableSslValidation=true",
"spring.cloud.openfeign.httpclient.followRedirects=false",
"spring.cloud.openfeign.httpclient.disableSslValidation=true",
"spring.cloud.openfeign.httpclient.followRedirects=false",
"spring.cloud.openfeign.httpclient.hc5.poolConcurrencyPolicy=lax",
"spring.cloud.openfeign.httpclient.hc5.poolReusePolicy=lifo",
"spring.cloud.openfeign.httpclient.hc5.socketTimeout=200",
"spring.cloud.openfeign.httpclient.hc5.socketTimeoutUnit=milliseconds").applyTo(this.context);
setupContext();
assertThat(getProperties().getMaxConnections()).isEqualTo(2);
assertThat(getProperties().getConnectionTimeout()).isEqualTo(2);
@ -105,8 +94,6 @@ class FeignHttpClientPropertiesTests { @@ -105,8 +94,6 @@ class FeignHttpClientPropertiesTests {
assertThat(getProperties().getHc5().getPoolReusePolicy()).isEqualTo(PoolReusePolicy.LIFO);
assertThat(getProperties().getHc5().getSocketTimeout()).isEqualTo(200);
assertThat(getProperties().getHc5().getSocketTimeoutUnit()).isEqualTo(TimeUnit.MILLISECONDS);
assertThat(getProperties().getHc5().getConnectionRequestTimeout()).isEqualTo(200);
assertThat(getProperties().getHc5().getConnectionRequestTimeoutUnit()).isEqualTo(TimeUnit.MILLISECONDS);
}
private void setupContext() {

5
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/PageJacksonModuleTests.java

@ -28,7 +28,6 @@ import org.junit.jupiter.params.provider.ValueSource; @@ -28,7 +28,6 @@ import org.junit.jupiter.params.provider.ValueSource;
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 static org.assertj.core.api.Assertions.assertThat;
@ -70,7 +69,7 @@ public class PageJacksonModuleTests { @@ -70,7 +69,7 @@ public class PageJacksonModuleTests {
@Test
public void serializeAndDeserializeEmpty() throws JsonProcessingException {
// Given
PageImpl<Object> objects = new PageImpl<>(new ArrayList<>(), Pageable.ofSize(1), 0);
PageImpl<Object> objects = new PageImpl<>(new ArrayList<>());
String pageJson = objectMapper.writeValueAsString(objects);
// When
Page<?> result = objectMapper.readValue(pageJson, Page.class);
@ -106,7 +105,7 @@ public class PageJacksonModuleTests { @@ -106,7 +105,7 @@ public class PageJacksonModuleTests {
@Test
public void serializeAndDeserializeEmptyCascade() throws JsonProcessingException {
// Given
PageImpl<Object> objects = new PageImpl<>(new ArrayList<>(), Pageable.ofSize(1), 0);
PageImpl<Object> objects = new PageImpl<>(new ArrayList<>());
String pageJson = objectMapper.writeValueAsString(objects);
// When
Page<?> result = objectMapper.readValue(pageJson, Page.class);

44
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java

@ -1,5 +1,5 @@ @@ -1,5 +1,5 @@
/*
* Copyright 2013-2023 the original author or authors.
* Copyright 2013-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -38,8 +38,6 @@ import org.junit.jupiter.api.Test; @@ -38,8 +38,6 @@ import org.junit.jupiter.api.Test;
import org.springframework.cloud.openfeign.CollectionFormat;
import org.springframework.cloud.openfeign.SpringQueryMap;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.NumberFormat;
import org.springframework.format.number.NumberStyleFormatter;
@ -628,26 +626,6 @@ class SpringMvcContractTests { @@ -628,26 +626,6 @@ class SpringMvcContractTests {
.isEqualTo("cookie1={cookie1}; cookie2={cookie2}");
}
@Test
void shouldNotFailWhenBothPageableAndRequestBodyParamsInPostRequest() {
List<MethodMetadata> data = contract.parseAndValidateMetadata(TestTemplate_PageablePost.class);
assertThat(data.get(0).queryMapIndex()).isEqualTo(0);
assertThat(data.get(0).bodyIndex()).isEqualTo(1);
assertThat(data.get(1).queryMapIndex()).isEqualTo(1);
assertThat(data.get(1).bodyIndex()).isEqualTo(0);
}
@Test
void shouldSetPageableAsBodyWhenQueryMapParamPresent() {
List<MethodMetadata> data = contract.parseAndValidateMetadata(TestTemplate_PageablePostWithQueryMap.class);
assertThat(data.get(0).queryMapIndex()).isEqualTo(0);
assertThat(data.get(0).bodyIndex()).isEqualTo(1);
assertThat(data.get(1).queryMapIndex()).isEqualTo(1);
assertThat(data.get(1).bodyIndex()).isEqualTo(0);
}
private ConversionService getConversionService() {
FormattingConversionServiceFactoryBean conversionServiceFactoryBean = new FormattingConversionServiceFactoryBean();
conversionServiceFactoryBean.afterPropertiesSet();
@ -849,26 +827,6 @@ class SpringMvcContractTests { @@ -849,26 +827,6 @@ class SpringMvcContractTests {
}
interface TestTemplate_PageablePost {
@PostMapping
Page<String> getPage(Pageable pageable, @RequestBody String body);
@PostMapping
Page<String> getPage(@RequestBody String body, Pageable pageable);
}
interface TestTemplate_PageablePostWithQueryMap {
@PostMapping
Page<String> getPage(@SpringQueryMap TestObject pojo, Pageable pageable);
@PostMapping
Page<String> getPage(Pageable pageable, @SpringQueryMap TestObject pojo);
}
@JsonAutoDetect(fieldVisibility = ANY, getterVisibility = NONE, setterVisibility = NONE)
public static class TestObject {

81
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/Http2ClientConfigurationTests.java

@ -1,81 +0,0 @@ @@ -1,81 +0,0 @@
/*
* Copyright 2013-2023 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.test;
import java.net.http.HttpClient;
import feign.Client;
import feign.http2client.Http2Client;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
import org.springframework.context.annotation.Bean;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.util.ReflectionTestUtils;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author changjin wei(魏昌进)
*/
@SpringBootTest(properties = { "spring.cloud.openfeign.http2client.enabled= true",
"spring.cloud.openfeign.httpclient.hc5.enabled= false", "spring.cloud.loadbalancer.retry.enabled= false" })
@DirtiesContext
class Http2ClientConfigurationTests {
@Autowired
FeignBlockingLoadBalancerClient feignClient;
private static final HttpClient defaultHttpClient = HttpClient.newHttpClient();
@Test
void shouldInstantiateFeignHttp2Client() {
Client delegate = feignClient.getDelegate();
assertThat(delegate instanceof Http2Client).isTrue();
Http2Client http2Client = (Http2Client) delegate;
HttpClient httpClient = getField(http2Client, "client");
assertThat(httpClient).isEqualTo(defaultHttpClient);
}
@SuppressWarnings("unchecked")
protected <T> T getField(Object target, String name) {
Object value = ReflectionTestUtils.getField(target, target.getClass(), name);
return (T) value;
}
@FeignClient(name = "foo")
interface FooClient {
}
@SpringBootConfiguration
@EnableAutoConfiguration
static class TestConfig {
@Bean
public HttpClient client() {
return defaultHttpClient;
}
}
}

256
spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignHttp2ClientTests.java

@ -1,256 +0,0 @@ @@ -1,256 +0,0 @@
/*
* Copyright 2013-2023 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.valid;
import java.util.Objects;
import feign.Client;
import feign.http2client.Http2Client;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.ServiceInstanceListSuppliers;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.cloud.openfeign.loadbalancer.FeignBlockingLoadBalancerClient;
import org.springframework.cloud.openfeign.test.NoSecurityConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author changjin wei(魏昌进)
*/
@SpringBootTest(classes = FeignHttp2ClientTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT,
value = { "spring.application.name=feignclienttest", "spring.cloud.openfeign.circuitbreaker.enabled=false",
"spring.cloud.openfeign.httpclient.hc5.enabled=false",
"spring.cloud.openfeign.http2client.enabled=true", "spring.cloud.loadbalancer.retry.enabled=false" })
@DirtiesContext
class FeignHttp2ClientTests {
@Autowired
private TestClient testClient;
@Autowired
private Client feignClient;
@Autowired
private UserClient userClient;
@Test
void testSimpleType() {
Hello hello = testClient.getHello();
assertThat(hello).as("hello was null").isNotNull();
assertThat(hello).as("first hello didn't match").isEqualTo(new Hello("hello world 1"));
}
@Test
void testPatch() {
ResponseEntity<Void> response = testClient.patchHello(new Hello("foo"));
assertThat(response).isNotNull();
String header = response.getHeaders().getFirst("x-hello");
assertThat(header).isEqualTo("hello world patch");
}
@Test
void testFeignClientType() {
assertThat(feignClient).isInstanceOf(FeignBlockingLoadBalancerClient.class);
FeignBlockingLoadBalancerClient client = (FeignBlockingLoadBalancerClient) feignClient;
Client delegate = client.getDelegate();
assertThat(delegate).isInstanceOf(Http2Client.class);
}
@Test
void testFeignInheritanceSupport() {
assertThat(userClient).as("UserClient was null").isNotNull();
final User user = userClient.getUser(1);
assertThat(user).as("Returned user was null").isNotNull();
assertThat(new User("John Smith")).as("Users were different").isEqualTo(user);
}
@FeignClient("localapp")
protected interface TestClient extends BaseTestClient {
}
protected interface BaseTestClient {
@GetMapping("/hello")
Hello getHello();
@PatchMapping(value = "/hellop", consumes = "application/json")
ResponseEntity<Void> patchHello(Hello hello);
}
protected interface UserService {
@GetMapping("/users/{id}")
User getUser(@PathVariable("id") long id);
}
@FeignClient("localapp1")
protected interface UserClient extends UserService {
}
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@RestController
@EnableFeignClients(clients = { TestClient.class, UserClient.class })
@LoadBalancerClients({
@LoadBalancerClient(name = "localapp", configuration = FeignHttpClientTests.LocalClientConfiguration.class),
@LoadBalancerClient(name = "localapp1",
configuration = FeignHttpClientTests.LocalClientConfiguration.class) })
@Import(NoSecurityConfiguration.class)
protected static class Application implements UserService {
@GetMapping("/hello")
public Hello getHello() {
return new Hello("hello world 1");
}
@PatchMapping("/hellop")
public ResponseEntity<Void> patchHello(@RequestBody Hello hello,
@RequestHeader("Content-Length") int contentLength) {
if (contentLength <= 0) {
throw new IllegalArgumentException("Invalid Content-Length " + contentLength);
}
if (!hello.getMessage().equals("foo")) {
throw new IllegalArgumentException("Invalid Hello: " + hello.getMessage());
}
return ResponseEntity.ok().header("X-Hello", "hello world patch").build();
}
@Override
public User getUser(@PathVariable("id") long id) {
return new User("John Smith");
}
}
public 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);
}
}
public static class User {
private String name;
User() {
}
User(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
User that = (User) o;
return Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
// Load balancer with fixed server list for "local" pointing to localhost
@Configuration(proxyBeanMethods = false)
static class LocalClientConfiguration {
@LocalServerPort
private int port = 0;
@Bean
public ServiceInstanceListSupplier staticServiceInstanceListSupplier() {
return ServiceInstanceListSuppliers.from("local",
new DefaultServiceInstance("local-1", "local", "localhost", port, false));
}
}
}

1
spring-cloud-openfeign-core/src/test/resources/feign-properties.properties

@ -12,7 +12,6 @@ spring.cloud.openfeign.client.config.default.capabilities=org.springframework.cl @@ -12,7 +12,6 @@ spring.cloud.openfeign.client.config.default.capabilities=org.springframework.cl
spring.cloud.openfeign.client.config.default.queryMapEncoder=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.NoOpQueryMapEncoder
spring.cloud.openfeign.client.config.foo.requestInterceptors[0]=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.FooRequestInterceptor
spring.cloud.openfeign.client.config.foo.requestInterceptors[1]=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.BarRequestInterceptor
spring.cloud.openfeign.client.config.baz.responseInterceptor=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.BazResponseInterceptor
spring.cloud.openfeign.client.config.singleValue.defaultRequestHeaders[singleValueHeaders]=header
spring.cloud.openfeign.client.config.singleValue.defaultQueryParameters[singleValueParameters]=parameter
spring.cloud.openfeign.client.config.multipleValue.defaultRequestHeaders[multipleValueHeaders]=header1,header2

11
spring-cloud-openfeign-core/src/test/resources/logback-test.xml

@ -1,11 +0,0 @@ @@ -1,11 +0,0 @@
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>

17
spring-cloud-openfeign-dependencies/pom.xml

@ -6,16 +6,16 @@ @@ -6,16 +6,16 @@
<parent>
<artifactId>spring-cloud-dependencies-parent</artifactId>
<groupId>org.springframework.cloud</groupId>
<version>4.1.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<relativePath/>
</parent>
<artifactId>spring-cloud-openfeign-dependencies</artifactId>
<version>4.1.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<name>spring-cloud-openfeign-dependencies</name>
<description>Spring Cloud OpenFeign Dependencies</description>
<properties>
<feign.version>12.5</feign.version>
<feign.version>12.1</feign.version>
<feign-form.version>3.8.0</feign-form.version>
</properties>
<dependencyManagement>
@ -41,17 +41,6 @@ @@ -41,17 +41,6 @@
<groupId>io.github.openfeign.form</groupId>
<artifactId>feign-form-spring</artifactId>
<version>${feign-form.version}</version>
<exclusions>
<exclusion>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.5</version>
</dependency>
</dependencies>
</dependencyManagement>

5
spring-cloud-starter-openfeign/pom.xml

@ -5,12 +5,9 @@ @@ -5,12 +5,9 @@
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-openfeign</artifactId>
<version>4.1.0-SNAPSHOT</version>
<version>4.0.0-SNAPSHOT</version>
<relativePath>..</relativePath>
</parent>
<scm>
<url>https://github.com/spring-cloud/spring-cloud-openfeign</url>
</scm>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<name>Spring Cloud Starter OpenFeign</name>
<description>Spring Cloud Starter OpenFeign</description>

Loading…
Cancel
Save